前阵子,在面试过程中,了解到android的插件化开发。今天将它大概的思路整了一下。阿里巴巴开源了一个插件化开发框架Atlas,淘宝就是用这个搭建起来,地址:http://atlas.taobao.org/docs/principle-intro/Apk_architecture.html
插件化开发基本思路:
1、第一步:写好插件(就像写一个完整的apk差不多,爱写什么都可以,只要是java写的就行),并生成dex文件。编写好的java文件,打出来的jar文件,在android上是运行不了的,因为打成的jar文件里的字节码,只能在java JVM中运行,而android手机的应用是运行在Dalvik VM上的,它只能运行dex格式的字节码文件,所以再用dx命令,将jar文件转换成dex文件,压缩格式可以是jar、apk、dex。
例如:把ShowStringClass.java文件打包生成showStringClass.jar文件,然后把文件放到sdk目录下的build-tools下的23.0.1目录下,然后用下面命令把showStringClass.jar生成showStringClass_imle.jar的dex文件:
dx --dex --output=showStringClass_impl.jar showStringClass.jar
2、第二步:将dex文件放上手机sdcard中:
adb push showStringClass_impl.jar /sdcard/
3、第三步:使用DexClassLoader加载类
DexClassLoader是一个类加载器,可以用来从.jar和.apk文件中加载class。可以用来加载执行没有和应用程序一起安装的那部分代码。它构造函数是DexClassLoader(String dexPath, String optimizedDirectory, String libraryPath, ClassLoader parent)
参数说明:
dexPath: dex文件的路径,就是第二步上传到sdcard中的jar文件路径,不能为空。
optimizedDirectory:解压后的.dex文件的存储路径(从jar文件、apk文件解压出来后,得到的.dex文件存放路径),不能为空。强烈建议使用应用程序的私有路径,不要放到sdcard上,否则代码容易被注入攻击。
libraryPath:os库的存放路径,可以为空,若有os库,必须填写。
parent:父亲加载器,一般为context.getClassLoader(),使用当前上下文的类加载器。
代码如下:
File dexOpt = this.getDir("dexOpt", MODE_PRIVATE); //在app的包名下创建一个dexOpt目录,返回路径。这是放.dex文件的
final DexClassLoader classloader = new DexClassLoader( apkPath, //加载dex文件
dexOpt.getAbsolutePath(),
null,
this.getClassLoader());
Class<?> classToLoad = (Class<?>) classloader.loadClass("com.wong.mytestapp.ShowStringClass"); //加载特定类
Object instance = classToLoad.newInstance(); //类的实例化
Method method = classToLoad.getMethod("method"); //获得类的方法
method.invoke(instance);//调用类的方法
注意,第二步:
如果dex文件放在android的资源目录asset中,那么准确的做法时。要把asset里的dex、jar、apk文件读出到一个目录下,如应用程序的私有路径。然后再进行第三步及后续操作。
将asset文件读出到应用程序的私有路径:
private final String DexPath = "dex";
private final String ASSETJAR = "plugins/ShowStringClass_impl.jar";
private final String EXPORTJARNAME = "ShowStringClass.jar";
private void copyDex(String DexPath) throws IOException {
File dexPath = getDir(DexPath,0);
File jarFile = new File(dexPath,EXPORTJARNAME);
if(jarFile.exists()) {
boolean b = jarFile.delete();
}
//打开asset中的输入流
InputStream ins = getAssets().open(ASSETJAR);
//将输入流写入文件
FileOutputStream fos = new FileOutputStream(jarFile);
byte[] arrayOfByte = new byte[1024];
for(;;){
int i = ins.read(arrayOfByte);
if(i==-1) break;
fos.write(arrayOfByte,0,i);
fos.flush();
}
}




