深入浅出Android动态加载jar包技术
在实际项目中,由于某些业务频繁变更而导致频繁升级客户端的弊病会造成较差的用户体验,而这也恰是Web App的优势,于是便衍生了一种思路,将核心的易于变更的业务封装在jar包里然后通过网络下载下来,再由android动态加载执行的方案,以改善频繁升级的毛病
--前言
该技术的具体实现步骤可参考农民伯伯的博客:http://www.cnblogs.com/over140/archive/2011/11/23/2259367.html
本文以此为基础,扩展了一个简单的框架,教大家如何使用该技术实现业务的动态变更升级
上效果图先:

再看看csdn code上工程的项目结构

FrameExample 就是我们的demo
frame是给demo引用的链接库,也就是框架壳
FrameCore 就是核心框架包,用于被android动态加载的jar包
---------------------------------------------------------------------------------------------------------------------------------
test目录展开如下

hfs2_3b287.rar
一个本地http服务器应用,用于存放测试用的apk和jar包
des.jar 经过优化的可用于动态加载的jar包
BtcMonitor.apk 测试用的apk
----------------------------------------------------------------------------------------------------------------------
http服务器界面如下:

demo通过frame库提供的api接口调用到framecore核心包里的具体实现代码
这样当业务具体实现有变更时,只需修改framecore里的内容,然后放到网络上
framecore里可以实现一个自检测jar包版本并自动更新下载jar包的功能,本例去掉了这个功能
有兴趣的童鞋可以自己尝试一下,另外本例只提供了下载的接口,其它接口根据框架模板定义自行添加即可
再贴一个核心类FrameInstance的实现:
public class FrameInstance implements IFrame{
private static final CommonLog log = LogFactory.createLog();
private static FrameInstance mInstance;
private boolean isFrameInit = false;
private Context mContext;
private Handler mJarHandler;
private DownloadJARProxy mJARProxy;
private ISimpleDownloadCallback mJARDownloadCallback;
public static synchronized FrameInstance getInstance(Context context) {
if (mInstance == null){
mInstance = new FrameInstance(context);
}
return mInstance;
}
private FrameInstance(Context context)
{
mContext = context;
mJarHandler = new Handler(){
@Override
public void handleMessage(Message msg) {
switch(msg.what){
case IHandlerMsg.ON_START:
break;
case IHandlerMsg.ON_PROGRESS:
{
int cur = msg.arg1;
int max = msg.arg2;
double rate = cur * 1.0 / max;
int value = (int) (rate * 100);
String conString = String.valueOf(value) + "%";
log.e("download jar percent:" + conString);
}
break;
case IHandlerMsg.ON_SUCCESS:
if (mJARDownloadCallback != null){
mJARDownloadCallback.onDownload(true);
}
break;
case IHandlerMsg.ON_FAIL:
if (mJARDownloadCallback != null){
mJARDownloadCallback.onDownload(false);
}
break;
case IHandlerMsg.ON_CANCEL:
break;
}
}
};
}
@Override
public boolean startFramework() {
if (isFrameInit){
return true;
}
isFrameInit = loadFrameCore();
log.e("startFramework ret = " + isFrameInit);
if (mIFrameCore != null){
mIFrameCore.startEngine(mContext);
}
return isFrameInit;
}
@Override
public boolean stopFramework() {
if (!isFrameInit){
return true;
}
if (mIFrameCore != null){
mIFrameCore.stopEngine(mContext);
}
releaseFrameCore();
isFrameInit = false;
log.e("stopFramework... ");
return true;
}
@Override
public boolean isFrameworkInit() {
return isFrameInit;
}
@Override
public boolean isFrameCoreExist() {
if (!FrameTool.hasSDCard())
return false;
File file = new File(FrameTool.getJARSavePath());
if (!file.exists()){
return false;
}
return true;
}
@Override
public boolean startDownloadFrameCore(ISimpleDownloadCallback callback) {
if (!FrameTool.hasSDCard()){
log.e("SDCard not exist!!!");
return false;
}
if (mJARProxy != null){
if (mJARProxy.isTaskRunning()){
return true;
}
}
mJARProxy = new DownloadJARProxy(mJarHandler);
mJARProxy.startDownloadTask(FrameTool.getJARURL(), FrameTool.getJARSavePath());
mJARDownloadCallback = callback;
log.e("startDownloadFrameCore...JAR_URL:" + FrameTool.getJARURL() + ", SAVE_PATH:" + FrameTool.getJARSavePath());
return true;
}
public boolean updateDownloadAPK(String url, String dstPath, IUpdateDownloadCallback callback){
if (mIUpdateTools == null){
log.e("mIUpdateTools = null!!!");
return false;
}
if (TextUtils.isEmpty(url) || TextUtils.isEmpty(dstPath)){
return false;
}
mIUpdateTools.updateDownloadAPK(url, dstPath, callback);
return true;
}
public boolean cancelDownloadAPK(){
if (mIUpdateTools == null){
log.e("mIUpdateTools = null!!!");
return false;
}
mIUpdateTools.cancelDownloadAPK();
return true;
}
public boolean checkJARVersion(){
return true;
}
public boolean installAPK(String path){
if (TextUtils.isEmpty(path)){
return false;
}
Intent i = new Intent(Intent.ACTION_VIEW);
i.setDataAndType(Uri.parse("file://" + path), "application/vnd.android.package-archive");
i.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
mContext.startActivity(i);
return true;
}
private IFrameCore mIFrameCore;
private IUpdateTools mIUpdateTools;
private boolean loadFrameCore(){
// if (true){
// mIVersionTools = new VersionTools();
// mIUpdateTools = new UpdateTools();
// return true;
// }
if (!isFrameCoreExist()){
return false;
}
DexClassLoader classLoader = DexClassLoadTools.newDexClassLoader(mContext, FrameTool.getJARSavePath());
if (classLoader == null){
return false;
}
mIFrameCore = ClassFactory.newFrameCore(classLoader);
if (mIFrameCore == null){
return false;
}
mIUpdateTools = ClassFactory.newUpdateTools(classLoader);
return true;
}
private void releaseFrameCore(){
mIFrameCore = null;
mIUpdateTools = null;
}
private static class ClassFactory{
public static IFrameCore newFrameCore(DexClassLoader classLoader){
IFrameCore object = null;
Class cls = newFrameCoreClass(classLoader, "com.lance.framecore.externex.FrameCore");
if (cls != null){
try {
object = (IFrameCore) cls.newInstance();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
return object;
}
public static IUpdateTools newUpdateTools(DexClassLoader classLoader){
IUpdateTools object = null;
Class cls = newFrameCoreClass(classLoader, "com.lance.framecore.externex.UpdateTools");
if (cls != null){
try {
object = (IUpdateTools) cls.newInstance();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
return object;
}
public static Class newFrameCoreClass(DexClassLoader classLoader, String className){
Class libProviderClazz = null;
try {
libProviderClazz = classLoader.loadClass(className);
} catch (Exception exception) {
exception.printStackTrace();
}
return libProviderClazz;
}
}
public static class FrameTool{
private final static String JAR_URL = "http://192.168.1.101/jar/des.jar";
public static boolean hasSDCard() {
String status = Environment.getExternalStorageState();
if (!status.equals(Environment.MEDIA_MOUNTED)) {
return false;
}
return true;
}
public static String getRootFilePath() {
if (hasSDCard()) {
return Environment.getExternalStorageDirectory().getAbsolutePath() + "/";
} else {
return Environment.getDataDirectory().getAbsolutePath() + "/data/";
}
}
public static String getJARURL(){
return JAR_URL;
}
public static String getJARSavePath(){
return getRootFilePath() + "FrameCore.jar";
}
}
private static class DexClassLoadTools{
public static DexClassLoader newDexClassLoader(Context context, String jarPath){
final File optimizedDexOutputPath = new File(jarPath);
if (!optimizedDexOutputPath.exists()){
return null;
}
File file = context.getDir("osdk", 0);
log.e("getDir:" + file.getAbsolutePath());
DexClassLoader cl = new DexClassLoader(optimizedDexOutputPath.getAbsolutePath(),
file.getAbsolutePath(),
null,
context.getClassLoader());
return cl;
}
}
@Override
public boolean deleteFrameCore() {
log.e("deleteFrameCore");
if (!isFrameCoreExist()){
log.e("framecore.jar is not exist:" + FrameTool.getJARSavePath());
return false;
}
FileTools.deleteDirectory(FrameTool.getJARSavePath());
return true;
}
@Override
public FrameCoreInfo getFrameCoreInfo() {
try {
if (mIFrameCore == null){
return new FrameCoreInfo();
}
return mIFrameCore.getFrameCoreInfo(mContext);
} catch (Exception e) {
e.printStackTrace();
return new FrameCoreInfo();
}
}
}
值得注意的是
在导出framecore时无需导出com.lance.framecore.extern包下代码,否则加载时会出现重复定义错误
同时要保持framecore工程和frame工程该包代码的一致,在扩展接口时把相应接口写在这个包下即可

--------------------------------------------------------------------------------------------------------------------------------
其它的没啥好说的了,自个儿download代码看吧
下面附上code地址:https://code.csdn.net/geniuseoe2012/dynamicjar
如果童鞋们觉得本文有用,不妨关注我的code主页:https://code.csdn.net/geniuseoe2012
以后一些博文相关的demo会放在上面,这样大家就不用耗下载积分了,同时便于代码更正
更多开源项目可关注我的github主页:https://github.com/geniusgithub
深入浅出Android动态加载jar包技术的更多相关文章
- 【Android】Android动态加载Jar、APK的实现
本文介绍Android中动态加载Jar.APK的实现.而主要用到的就是DexClassLoader这个类.大家都知道Android和普通的Java虚拟机有差别,它只能加载经过处理的dex文件.而加载这 ...
- JAVA动态加载JAR包的实现
如何动态的加载这些驱动!不可能把所有的数据库驱动都集成到JAR包中吧?!于是动态加载驱动的JAR包就产生了!其实这些在做系统基础代码时,经常用到,只是一般我们没有机会去搞而已. 动态加载JAR包,使用 ...
- Android动态加载jar/dex
前言 在目前的软硬件环境下,Native App与Web App在用户体验上有着明显的优势,但在实际项目中有些会因为业务的频繁变更而频繁的升级客户端,造成较差的用户体验,而这也恰恰是Web App的优 ...
- java动态加载jar包,并运行其中的类和方法
动态加载jar包,在实际开发中经常会需要用到,尤其涉及平台和业务的关系的时候,业务逻辑部分可以独立出去交给业务方管理,业务方只需要提供jar包,就能在平台上运行. 下面通过一个实例来直观演示: 第一: ...
- Android动态加载jar、apk的实现
前段时间到阿里巴巴参加支付宝技术分享沙龙,看到支付宝在Android使用插件化的技术,挺好奇的.正好这几天看到了农民伯伯的相关文章,因此简单整理了下,有什么错误希望大神指正. 核心类 1.1 ...
- 动态加载jar包(二)
上次说的加载jar包,有几个问题没有解决: 1.如果项目包含了其他的jar包如何解决? 2.如何规范上传的jar包的类和方法? 下面就解决一下上面两个问题 一.首先编写被调用的类,这次使用maven工 ...
- 动态加载jar包(一)
一.编写被调用的类 package com.qunar.helloworld; public class HelloWorld { public String sayHello(){ return ( ...
- 动态加载jar包中的类(方式一)
嘛, 直接上代码 public static class TestClassLoader extends ClassLoader { @Override protected Class<?> ...
- Java动态加载JAR包
参考代码: package org; import java.io.File; import java.net.URL; import java.net.URLClassLoader; import ...
随机推荐
- CF 888E Maximum Subsequence——折半搜索
题目:http://codeforces.com/contest/888/problem/E 一看就是折半搜索?……然后排序双指针. 两个<m的数加起来如果>=m,一定不会更新答案.因为- ...
- Azure ARM (22) Azure Policy入门
<Windows Azure Platform 系列文章目录> 我们知道,在Azure服务层级中,分为以下几个层次: 1.企业合同 2.订阅 3.资源组 4.资源 我们使用的Azure资源 ...
- Ubuntu 安装mysql和 简单命令操作
ubuntu上安装mysql非常简单只需要几条命令就可以完成. 1. sudo apt-get install mysql-server 2. apt-get isntall mysql-clie ...
- 【旧文章搬运】深入分析Win7的对象引用跟踪机制
原文发表于百度空间及看雪论坛,2010-09-12 看雪论坛地址:https://bbs.pediy.com/thread-120296.htm============================ ...
- 4.js屏蔽浏览器鼠标右键菜单
document.oncontextmenu = function(){return false;} 参考链接:js 屏蔽浏览器事件汇总
- A - Mike and Cellphone
While swimming at the beach, Mike has accidentally dropped his cellphone into the water. There was n ...
- c++函数模板二栈实现
1 没有使用模板的栈实现 #include <iostream> #include <string> using namespace std; class Stack { pu ...
- 20个Flutter实例视频教程-第15节: 贝塞尔曲线切割
博客地址: https://jspang.com/post/flutterDemo.html#toc-61b 视频地址: https://www.bilibili.com/video/av397092 ...
- 前端基础 之css
css 介绍 css(层叠样式表)定义如何显示html 元素 当浏览器读到一个样式表, 他就会按照这个表对文档进行格式化(渲染) css语法 css实例 css 注释 注释是代码之母 /* 这是注释* ...
- [ACM] hdu 1285 确定比赛名次 (拓扑排序)
确定比赛名次 Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 65536/32768 K (Java/Others)Total Subm ...