首先介绍一下要实现的效果:静默更新所有客户端应用程序中的jar包。
产生这个需求的原因:当做好的jar包发布出去,开发者已经在使用的情况下,有一天我们发现jar包中有个bug,这是我们就要去更新jar包,但是如果开发者需要更新jar包的话,那代价无疑是巨大的。出于减少开发者更新jar包的代价的目的。我们想到了使用这种方式来解决。整个项目的结构如下图:
插件管理框架工作流程:
客户端app启动时,插件管理框架读取本地所有插件信息,例如插件版本,提供者等等,并将该信息发送到插件管理服务端,在服务端监测是否有新的插件需要更新。如果有新的插件插件管理框架负责下载等操作。
具体的实现的话,为了统一,我们在插件管理框架中定义好接口,所有插件都要实现该接口,实现该接口的插件,我们就认为它是一个合法的插件。这样在application中我们就可以通过插件管理框架中的方法来调用插件中的方法。
插件管理框架主要模块:
1.插件管理:读取插件信息和服务端交互,根据返回判断是否下载新的插件。
2.调用插件中的方法。
关于第一点就不多少了,就是一些网络访问和下载的东东。我们主要说一下调用插件中的方法。在使用jar包的使用我们知道如果是放在lib目录下,通过build path 加载到项目中,我们可以直接通过import导入使用jar包中的类和方法。但是插件的类和方法该怎么使用呢?这是我们就要使用到android的类加载器。android中有两种类加载器 DexClassLoader和PathClassLoader,两者的区别就是PathClassLoader只能加载已经安装到android系统中的apk文件,也就是/data/app/下的apk文件,加载其他位置的文件会报ClassNotFoundException ,DexClassLoader可以加载apk、jar和dex文件,而且可以加载sdcard下的文件。
先介绍一下我们的插件项目,该项目就是简单的做个加法运算。
package com.apkplug;
public class plugClass {
public plugClass() {
}
public int add(int a, int b){
return a+b;
}
}
在下面的介绍中我们使用DexClassLoader.首先我们来打包jar包并转换成dex文件,首先将写好的jar包导出,Export->java jarfile ->Next 然后选择jar的存放目录。然后我们将jar包转成dex格式,为了方便我们将jar包放到SDK的安装目录plarform-tools下,DOS进入这个目录,执行命令: dx --dex -ouput=classes.jar Myplug.jar 这样我们就将Myplugin.jar转成了dex格式,我们将classes.jar 放到SDcard目录。下面在程序中我们使用DexClassLoader来加载classes.jar中的类
package com.apkplugtest;
import android.os.Bundle;
import android.app.Activity;
import android.view.Menu;
import android.widget.TextView;
import java.lang.reflect.Method;
import dalvik.system.DexClassLoader;
import dalvik.system.PathClassLoader;
public class MainActivity extends Activity {
TextView tv = null;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
tv = (TextView) findViewById(R.id.tv);
useDexClassLoader();
}
private void useDexClassLoader() {
String dexPath = "/mnt/sdcard/classes.jar";
String dexOutputDir = "/data/data/com.apkplugtest";
DexClassLoader pathClassLoader = new DexClassLoader(dexPath, dexOutputDir, null, this
.getClass().getClassLoader());
// dexPath 目标jar或apk的路径 ; dexOutputDirdex 文件路径 ;null 目标类中使用的c/c++库存放的路径
// ; 第四个参数 父装载类
try {
Class<?> class1 = pathClassLoader.loadClass("com.apkplug.plugClass");
Object object = class1.newInstance();
Class[] params = new Class[2];
params[0] = Integer.TYPE;
params[1] = Integer.TYPE;
Method action = class1.getMethod("add", params);
Integer ret = (Integer) action.invoke(object, 12, 13);
tv.setText("method : " + action.getName() + ", return :" + ret);
} catch (Exception e) {
// TODO: handle exception
}
}
}
程序运行结果如下图:
同时我们看到在data/data/com.apkplugtest下生成了.dex 文件如下图:
整个思路就是这样了。