深入浅出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 ...
随机推荐
- Makefile的常用技术总结
一.MAKE中的自动变量: $@: 表示target的名字 $%: 仅当目标是函数库文件中,表示规则中的目标成员名.例如,如果一个目标是"foo.a(bar.o)",那 ...
- rsync(六)命令中文手册
rsync(1) rsync(1) 名称 rsync - 一个快速.多功能的远程(和本地)文件拷贝工具 摘要 Local: rsync [OPTION...] SRC... [DEST] Access ...
- 开发板启动时,内核打印出"can't access tty,job control turned off"
启动后的最后一行提示can't access tty,job control turned off, 这说明没有进入到控制台,原因就在于文件系统的/etc/inittab 这个文件里有问题 vi /e ...
- sql语句之正则表达式
select * from employee where name regexp '^jin' select * from employee where name regexp '^jin.*(g|n ...
- 5-1 变量与常量 & 6-1课程总结
变量与常量 常量就是变量定义的的前面加上final final关键字定义常量 新建类FinalDemo 更新常量n的值会报错.常量不可以被修改 常量有个命名规则 一般以大写字母去表示 final in ...
- FLINK源代码调试方式
此文已由作者岳猛授权网易云社区发布. 欢迎访问网易云社区,了解更多网易技术产品运营经验. 第一种,直接通过IDEA WINDOWS调试 前提是Flink所有依赖已经导入,直接在Test中打断点,然后直 ...
- node代理网络请求
server.js var express = require('express') var axios = require('axios') var port = 9000; var app = e ...
- webpack内置模块ProvidePlugin
webpack配置ProvidePlugin后,在使用时将不再需要import和require进行引入,直接使用即可. 使用方法: 在webpack.dev.conf.js和webpack.prod. ...
- lightoj 1422【区间DP·分类区间首元素的情况】
题意: 给你n天分别要穿的衣服种类,可以套着穿, 一旦脱下来就不能再穿,求n天至少要几件. 思路: 区间DP dp[i][j]代表i到j需要至少几件衣服 第i天的衣服在第i天穿上了,dp[i][j]= ...
- Mybatis:resultMap的使用总结(转自https://www.cnblogs.com/kenhome/p/7764398.html)
resultMap是Mybatis最强大的元素,它可以将查询到的复杂数据(比如查询到几个表中数据)映射到一个结果集当中. resultMap包含的元素: <!--column不做限制,可以为任意 ...