在实际项目中。因为某些业务频繁变更而导致频繁升级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包技术的更多相关文章
-
Android动态载入JAR包的实现方法
有时候我们须要使用动态更新技术,简单来说就是:当我们把开发jar包发送给用户后.假设后期升级了部分代码.这时让用户的jar包自己主动更新,而不是用户主动手动地去更新的技术就是动态更新.这个须要使用的技 ...
-
Android动态载入Dex机制解析
1.什么是类载入器? 类载入器(class loader)是 Java™中的一个非常重要的概念.类载入器负责载入 Java 类的字节代码到 Java 虚拟机中. Java 虚拟机使用 Java 类的方 ...
-
Android动态加载代码技术
Android动态加载代码技术 在开发Android App的过程当中,可能希望实现插件式软件架构,将一部分代码以另外一个APK的形式单独发布,而在主程序中加载并执行这个APK中的代码. 实现这个任务 ...
-
Android Studio导出Jar包
这篇博客将介绍一下如何用Android Studio导出jar包,希望能给大家带来帮助. 首先需要修改build.gradle文件,在Android Studio中会显示多个build.gradle文 ...
-
如何制作Jar包并在android中调用jar包
android制作jar包: 新建android工程,然后右击,点击导出,选择导出类型为Java下的JAR file,在java file specification 中不要选择androidmani ...
-
使用命令动态更新JAR包中的文件
动态更新JAR包中的文件,经本人实际测试可正常执行! 一.查询jar包中要替换的文件位置 jar -tvf gateway.jar | grep topjui.config.js 二.在当前 ...
-
深入浅出Android动态加载jar包技术
在实际项目中,由于某些业务频繁变更而导致频繁升级客户端的弊病会造成较差的用户体验,而这也恰是Web App的优势,于是便衍生了一种思路,将核心的易于变更的业务封装在jar包里然后通过网络下载下来,再由 ...
-
实现Android 动态载入APK(Fragment or Activity实现)
尊重原创:http://blog.csdn.net/yuanzeyao/article/details/38565345 近期由于项目太大了.导致编译通只是(Android对一个应用中的方法个数貌似有 ...
-
android studio 使用jar包,arr包和怎么使用githup开源项目中的aar包或module
我这里的android studio的版本是2.2.3版本 一.现在大家都用android studio了,就有人问怎么使用jar包 其实使用jar包比较简单 直接吧jar放入工程的app目录下的li ...
随机推荐
-
RabbitMQ介绍6 - 其它
深入话题 Exchange实现路由的方法 http://www.rabbitmq.com/blog/2010/09/14/very-fast-and-scalable-topic-routing-pa ...
-
lintcode-【中等】数飞机
题目: 给出飞机的起飞和降落时间的列表,用 interval 序列表示. 请计算出天上同时最多有多少架飞机? 样例: 对于每架飞机的起降时间列表:[[1,10],[2,3],[5,8],[4,7]], ...
-
解决 label 多行间距
使用. 一个是间距,一个是行高,但是鉴于多行,都写的话,就相当于双倍行高了.我就用行高代替间距. self.textLabel.attributedText = [self getAttributed ...
-
Linux下打包压缩war和解压war包
Linux下打包压缩war和解压war包 unzip是一种方法,如果不行则采用下面的方法 把当前目录下的所有文件打包成game.war jar -cvfM0 game.war ./ -c 创建wa ...
-
js数组的声明与应用
js数组的声明与应用 数组:一种容器,存储批量数据.JavaScript是一种弱类型语言.什么是弱类型,就是变量可以存储多种类型变量的引用不会报错.所以js数组可以存储不同的数据. 一.数组的作用:只 ...
-
ios开发——实用技术篇&;网络音频播放
网络音频播放 在日常的iOS开发中,我们通常会遇到媒体播放的问题,XCode中已经为我们提供了功能非常强大的AVFoundation框架和 MediaPlayer框架.其中AVFoundation框架 ...
-
问题:Maven: missing net.sf.json-lib
问题:Maven: missing net.sf.json-lib 解决: pom.xml里加入json-lib依赖,注意要添加classifier一行,如下: 这里maven地址:http://re ...
-
关于EasyUI中DataGrid控件的一些使用方法总结
一,DataGrid 控件的工作流程 1,通过JavaScript将一个空白的div美化成一个空白的Datagrid模板 2,Datagrid模板通过制定的Url发送请求,获取数据 ...
-
【原创】源码角度分析Android的消息机制系列(五)——Looper的工作原理
ι 版权声明:本文为博主原创文章,未经博主允许不得转载. Looper在Android的消息机制中就是用来进行消息循环的.它会不停地循环,去MessageQueue中查看是否有新消息,如果有消息就立刻 ...
-
Docker(二):Dockerfile 使用介绍
上一篇文章Docker(一):Docker入门教程介绍了 Docker 基本概念,其中镜像.容器和 Dockerfile .我们使用 Dockerfile 定义镜像,依赖镜像来运行容器,因此 Dock ...