在实际项目中。因为某些业务频繁变更而导致频繁升级client的弊病会造成较差的用户体验,而这也恰是Web App的优势,于是便衍生了一种思路。将核心的易于变更的业务封装在jar包里然后通过网络下载下来,再由android动态载入运行的方案。以改善频繁升级的毛病

--前言

该技术的详细实现步骤可參考农民伯伯的博客:http://www.cnblogs.com/over140/archive/2011/11/23/2259367.html

本文以此为基础,扩展了一个简单的框架,教大家怎样使用该技术实现业务的动态变更升级

上效果图先:

再看看csdn code上project的项目结构

FrameExample 就是我们的demo

frame是给demo引用的链接库。也就是框架壳

FrameCore 就是核心框架包。用于被android动态载入的jar包

---------------------------------------------------------------------------------------------------------------------------------

test文件夹展开例如以下

hfs2_3b287.rar  
一个本地httpserver应用,用于存放測试用的apk和jar包

des.jar           经过优化的可用于动态载入的jar包

BtcMonitor.apk   測试用的apk



----------------------------------------------------------------------------------------------------------------------



httpserver界面例如以下:

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包下代码,否则载入时会出现反复定义错误

同一时候要保持framecoreproject和frameproject该包代码的一致,在扩展接口时把对应接口写在这个包下就可以

--------------------------------------------------------------------------------------------------------------------------------

其他的没啥好说的了,自个儿download代码看吧

以下附上code地址:https://code.csdn.net/geniuseoe2012/dynamicjar





假设童鞋们认为本文实用,最好还是关注我的code主页:https://code.csdn.net/geniuseoe2012

以后一些博文相关的demo会放在上面,这样大家就不用耗下载积分了。同一时候便于代码更正

很多其它开源项目可关注我的github主页:https://github.com/geniusgithub

深入浅出Android动态载入jar包技术的更多相关文章

  1. Android动态载入JAR包的实现方法

    有时候我们须要使用动态更新技术,简单来说就是:当我们把开发jar包发送给用户后.假设后期升级了部分代码.这时让用户的jar包自己主动更新,而不是用户主动手动地去更新的技术就是动态更新.这个须要使用的技 ...

  2. Android动态载入Dex机制解析

    1.什么是类载入器? 类载入器(class loader)是 Java™中的一个非常重要的概念.类载入器负责载入 Java 类的字节代码到 Java 虚拟机中. Java 虚拟机使用 Java 类的方 ...

  3. Android动态加载代码技术

    Android动态加载代码技术 在开发Android App的过程当中,可能希望实现插件式软件架构,将一部分代码以另外一个APK的形式单独发布,而在主程序中加载并执行这个APK中的代码. 实现这个任务 ...

  4. Android Studio导出Jar包

    这篇博客将介绍一下如何用Android Studio导出jar包,希望能给大家带来帮助. 首先需要修改build.gradle文件,在Android Studio中会显示多个build.gradle文 ...

  5. 如何制作Jar包并在android中调用jar包

    android制作jar包: 新建android工程,然后右击,点击导出,选择导出类型为Java下的JAR file,在java file specification 中不要选择androidmani ...

  6. 使用命令动态更新JAR包中的文件

    动态更新JAR包中的文件,经本人实际测试可正常执行! 一.查询jar包中要替换的文件位置 jar  -tvf  gateway.jar  |  grep  topjui.config.js 二.在当前 ...

  7. 深入浅出Android动态加载jar包技术

    在实际项目中,由于某些业务频繁变更而导致频繁升级客户端的弊病会造成较差的用户体验,而这也恰是Web App的优势,于是便衍生了一种思路,将核心的易于变更的业务封装在jar包里然后通过网络下载下来,再由 ...

  8. 实现Android 动态载入APK(Fragment or Activity实现)

    尊重原创:http://blog.csdn.net/yuanzeyao/article/details/38565345 近期由于项目太大了.导致编译通只是(Android对一个应用中的方法个数貌似有 ...

  9. android studio 使用jar包,arr包和怎么使用githup开源项目中的aar包或module

    我这里的android studio的版本是2.2.3版本 一.现在大家都用android studio了,就有人问怎么使用jar包 其实使用jar包比较简单 直接吧jar放入工程的app目录下的li ...

随机推荐

  1. python image模块

    Image 模块 Image 模块提供了同名的类用来表示PIL的图像.Image模块还提供了许多工厂(factory)函数,包块从文件加载图像的函数,以及创建新图像的函数.    例子  下面的脚本加 ...

  2. 1089-Duplicate Removal

    描述 The company Al's Chocolate Mangos has a web site where visitors can guess how many chocolate cove ...

  3. php截取小时和分钟,在进行和其它时间段的比较

    用php截取时间的小时和分钟,然后判断这个时间是不是在 8:00到11:30之间,用php应该怎么写? date_default_timezone_set("Asia/Shanghai&qu ...

  4. PHP reset() 函数

    定义和用法 reset()函数把数组的内部指针指向第一个元素,并返回这个元素的值.若失败,则返回 FALSE. reset()函数用来将数组指针设置回数组的开始位置.如果需要在一个脚本中多次查看或处理 ...

  5. 处理MySQL数据库出现大量Locked的一个案例 (转)

    本文转自:http://blog.itpub.net/7607759/viewspace-696781/ 做为一款轻量级数据库软件,MySQL在使用过程中遇到访问速度慢,或者无法响应这类的问题,解决方 ...

  6. Android OTG支持USB读卡器

    我们知道,三星Android手机将USB读卡器通过OTG线插入Micro USB插口后,插拔读卡器里的SD卡,文件管理器也能够识别卡的插拔:而很多手机的OTG连上USB读卡器也来插拔SD卡,会发现文件 ...

  7. SQL Server 2008R2 数据库出现“可疑”导致无法访问

    日常对Sql Server 2005关系数据库进行操作时,有时对数据库(如:Sharepoint网站配置数据库名Sharepoint_Config)进行些不正常操作如数据库在读写时而无故停止数据库,从 ...

  8. Bex64

    http://speedupcomputercleaner.com/windows-7-error-bex64.php   How To Fix Windows 7 Error Bex64 ? Wha ...

  9. 【转】objective-c基本数据类型之输出格式符

    原文网址:http://blog.csdn.net/mamong/article/details/8255691 基本数据类型 1. int 输出格式符:%i, %d, %o %x, 2. float ...

  10. openlayer调用geoserver发布的地图实现地图的基本功能

    转自:http://starting.iteye.com/blog/1039809 主要实现的功能有放大,缩小,获取地图大小,平移,线路测量,面积测量,拉宽功能,显示标注,移除标注,画多边形获取经纬度 ...