在实际项目中,由于某些业务频繁变更而导致频繁升级客户端的弊病会造成较差的用户体验,而这也恰是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包技术的更多相关文章

  1. 【Android】Android动态加载Jar、APK的实现

    本文介绍Android中动态加载Jar.APK的实现.而主要用到的就是DexClassLoader这个类.大家都知道Android和普通的Java虚拟机有差别,它只能加载经过处理的dex文件.而加载这 ...

  2. JAVA动态加载JAR包的实现

    如何动态的加载这些驱动!不可能把所有的数据库驱动都集成到JAR包中吧?!于是动态加载驱动的JAR包就产生了!其实这些在做系统基础代码时,经常用到,只是一般我们没有机会去搞而已. 动态加载JAR包,使用 ...

  3. Android动态加载jar/dex

    前言 在目前的软硬件环境下,Native App与Web App在用户体验上有着明显的优势,但在实际项目中有些会因为业务的频繁变更而频繁的升级客户端,造成较差的用户体验,而这也恰恰是Web App的优 ...

  4. java动态加载jar包,并运行其中的类和方法

    动态加载jar包,在实际开发中经常会需要用到,尤其涉及平台和业务的关系的时候,业务逻辑部分可以独立出去交给业务方管理,业务方只需要提供jar包,就能在平台上运行. 下面通过一个实例来直观演示: 第一: ...

  5. Android动态加载jar、apk的实现

    前段时间到阿里巴巴参加支付宝技术分享沙龙,看到支付宝在Android使用插件化的技术,挺好奇的.正好这几天看到了农民伯伯的相关文章,因此简单整理了下,有什么错误希望大神指正. 核心类 1.1     ...

  6. 动态加载jar包(二)

    上次说的加载jar包,有几个问题没有解决: 1.如果项目包含了其他的jar包如何解决? 2.如何规范上传的jar包的类和方法? 下面就解决一下上面两个问题 一.首先编写被调用的类,这次使用maven工 ...

  7. 动态加载jar包(一)

    一.编写被调用的类 package com.qunar.helloworld; public class HelloWorld { public String sayHello(){ return ( ...

  8. 动态加载jar包中的类(方式一)

    嘛, 直接上代码 public static class TestClassLoader extends ClassLoader { @Override protected Class<?> ...

  9. Java动态加载JAR包

    参考代码: package org; import java.io.File; import java.net.URL; import java.net.URLClassLoader; import ...

随机推荐

  1. 纯css 图片自适应居中

    html 结构 <div class="container"> <div class="content"></div> &l ...

  2. bzoj2159

    树形dp+第二类斯特林数 又是这种形式,只不过这次不用伯努利数了 直接搞肯定不行,我们化简一下式子,考虑x^n的组合意义,是把n个物品放到x个箱子里的方案数.那么就等于这个i=1->n,sigm ...

  3. js正则匹配字符串

    这里我第一时间想到的就是用 js 的search 和 match ,其中最常见的是match: 1. str.search(regexp):search()方法不支持全局搜索,因为会忽略正则表达式参数 ...

  4. Excel的poi缓存问题

    Excel的poi缓存问题 背景: 最近工作需要,需要完成生成新的Excel,然后从Excel中读取包含公式的文本内容. 问题: 当程序中修改公式对应的单元格数据变化时,公式获取的值仍然还是原来的值, ...

  5. 【网络爬虫】【java】微博爬虫(三):庖丁解牛——HTML结构分析与正则切分

    在上一篇文章中已经通过请求的url地址把html页面爬取下来了,这里分别以网易微博的html和新浪微博的html为例来分析如何提取微博数据. 一.网易微博解析 相比新浪微博的html结构,网易微博的比 ...

  6. Identity Server 4 原理和实战(完结)_建立Angular 客户端

    https://material.angular.io/ 第一部是安装angular cli --prefix=ac:前缀 --routing:默认使用路由 style=scss:样式使用scss - ...

  7. jquery的on()绑定无效的方式

    on前面的元素也必须在页面加载的时候就存在于dom里面.动态的元素或者样式等,可以放在on的第二个参数里面 jQuery on()方法是官方推荐的绑定事件的一个方法.使用 on() 方法可以给将来动态 ...

  8. sfc命令

    开始—>运行—>再输入“sfc /scannow”(不含引号),“sfc”是“系统文件检测”程序,它是一个在命令提示符下使用的实用程序,只有是管理员才能使用该程序,这个程序的作用就是检测系 ...

  9. LeetCode: 575 Distribute Candies(easy)

    题目: Given an integer array with even length, where different numbers in this array represent differe ...

  10. STL——pair

    功能:pair将一对值组合成一个值,这一对值可以具有不同的数据类型(T1和T2),两个值可以分别用pair的两个公有函数first和second访问. #include <bits/stdc++ ...