深入浅出Android动态载入jar包技术
在实际项目中。因为某些业务频繁变更而导致频繁升级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 ...
随机推荐
- POJ 3321 Apple Tree(树状数组)
点我看题目 题意 : 大概是说一颗树有n个分岔,然后给你n-1对关系,标明分岔u和分岔v是有边连着的,然后给你两个指令,让你在Q出现的时候按照要求输出. 思路 :典型的树状数组.但是因为没有弄好数组 ...
- The working copy at 'xxx' is too old 错误解决
使用svn update更新时候提示: svn: E155036: The working copy at 'xxx' is too old (format 29) to work with clie ...
- POJ - 2711 Leapin' Lizards
题目大意: 在一个网格地图中有一些高度不同的石柱,一些石柱上站着一些蜥蜴,你的任务是让尽量多的蜥蜴逃到边界外. 每行每列中相邻石柱的距离为1,蜥蜴的跳跃距离是d,即蜥蜴可以跳到平面距离不超过d的任何一 ...
- 二层安全之MAC Flooding解析与解决方法
一.了解MAC Flooding原理 1.1 如图所示,网络中有3个PC和一个交换机,在正常情况下,如果PC A向PC B发送信息,PC C是不会知道的,过程都通过中间的交换机进行透明的处理,并且会记 ...
- Android list1去除list2中的元素
public static void main(String[] args) { List<String> firList = new ArrayList<String>(); ...
- Myeclipse开发内存溢出问题
MyEclipse开发内存溢出问题 window --> preferences --> MyEclipse --> servers --> Tomcat --> J ...
- ruby 线程学习
i=1 Thread.start{ while true print "Thread 1 \n" i+=1 if i==5 then Thread.kill Thread.curr ...
- Oracle系列之触发器
涉及到表的处理请参看原表结构与数据 Oracle建表插数据等等 创建一个触发器,使其可以修改tb_Department表的deptno. create or replace trigger upda ...
- sdut2536字母哥站队(dp)
简单DP 说是简单 还是推了好一会 推出来觉得好简单 保留当前i的最小值 dp[i] = min(dp[i],dp[j]+i-j-1) j<i #include <iostream> ...
- php include include_once require require_once 的区别与联系
一.require 与 include 的区别: The require() function is identical to include(), except that it handles er ...