深入浅出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 ...
随机推荐
- DLL的远程注入技术
DLL的远程注入技术是目前Win32病毒广泛使用的一种技术.使用这种技术的病毒体通常位于一个DLL中,在系统启动的时候,一个EXE程序会将这个DLL加载至某些系统进程(如Explorer.exe)中运 ...
- hdu 3932 Groundhog Build Home —— 模拟退火
题目:http://acm.hdu.edu.cn/showproblem.php?pid=3932 找一个位置使距离最远的点的距离最小: 上模拟退火: 每次向距离最远的点移动,注意判断一下距离最远的点 ...
- ubuntu16.04 + CUDA 9.0 + opencv3.3 安装
安装前的准备 CUDA 9.0 安装,可以参看Ubuntu16.04 + cuda9.0 + cudnn7.1.4 + tensorflow安装 opencv 3.3.0 下载 ippicv_2017 ...
- read,write,accept,connect 超时封装
//read操作加上超时时间. 1 int read_timeout(int fd, void *buf, uint32_t count, int time) { ) { fd_set rSet; F ...
- 创建calico网络报错client response is invalid json
使用docker创建calico网络失败. # docker network create --driver calico --ipam-driver calico-ipam testcalico E ...
- 3.javascript转换日期字符串为Date对象
js中文网 阮一峰 1.求format“xxxx年xx月xx日 xx:xx”类型的两个日期天数差 var start = "2017年09月17日 13:51"; var end ...
- 基于IOS下的支付宝SDK的学习与使用——实现产品支付(二)
首先本篇为作者原创,仅供学习使用,以后会不断完善,精炼.阅读之前请参考 上一篇 上一篇 中详细说明了结合官方支付宝SDK,对工程环境进行的一些配置,实现了支付,本篇重点说明一下,注意事项和原理,主要 ...
- UVaLive 10859 Placing Lampposts (树形DP)
题意:给定一个无向无环图,要在一些顶点上放灯使得每条边都能被照亮,问灯的最少数,并且被两盏灯照亮边数尽量多. 析:其实就是一个森林,由于是独立的,所以我们可以单独来看每棵树,dp[i][0] 表示不在 ...
- 《剑指offer》面试题5—从尾到头打印链表
重要思路: 这个问题肯定要遍历链表,遍历链表的顺序是从头到尾,而要输出的顺序却是从尾到头,典型的“后进先出”,可以用栈实现. 注意stl栈的使用,遍历stack的方法. #include <io ...
- HDU5880【AC自动机】
题意: 给出n个字符串,再给出一个字符串,把之前出现过的字符串全部变成* 思路: AC自动机,Trie树上存的值是一个字符串的长度,也就是往前的长度,然后倒着处理一遍. 感想: 第三题AC自动机,本来 ...