上一篇文章,[Android插件化开发之Atlas初体验](
http://blog.csdn.net/sbsujjbcy/article/details/47446733),简单的介绍了使用Atlas的整个流程,但是如果你没有实践过的话估计还是一头雾水,从这篇文章开始,慢慢的切入细节。这篇文章的主题是生成插件信息列表。
细心的你或许发现了上篇文章中我们使用了一个叫openatlasbundler的项目生成了so以及一个json文件,说白了这个so只不过就是apk重命名的,而这个json文件中包含了插件的信息,比如上篇文章中的两个插件,其信息如下。
[
{
"pkgName": "com.lizhangqu.test",
"version": "1.0",
"activities": [
"com.lizhangqu.test.MainActivity"
],
"services": [],
"receivers": [],
"contentProviders": [],
"dependency": [],
"md5": "882d50a3c2668360b96a3af8f72b3bd2",
"size": 24780,
"hasSO": false
},
{
"pkgName": "com.lizhangqu.zxing",
"version": "1.0",
"activities": [
"com.lizhangqu.zxing.android.CaptureActivity"
],
"services": [],
"receivers": [],
"contentProviders": [],
"dependency": [],
"md5": "e68270557ae9776d218bb034916c124a",
"size": 312352,
"hasSO": false
}
]
可以看到里面存了插件的一些组件信息,以及大写和md5等。按照作者原话说是这样的。
BundleList的生成。这部分用的Java写的,BundleListInfo一个存储json的文本文件,我们所做的这一切无非是提高效率,举个例子,你安装插件的时候如果有动态库的话要解压,不能安装的时候每个文件检测一下apk存在so不,打开文件本来就慢,程序优化一点是一点
我们可以试试,如果不将该so文件打包进去,程序运行会出现什么。
我们发现,打印了一个日志说找不到插件信息列表。至于插件信息列表的读取,这里姑且不去理会,本篇的重点是如何生成该列表。
打开BundleMakeBooter类,里面就一个main函数,也就是说这个只不过是普通的java项目,其实我们可以从命令行将参数传入,但是还记得上篇文章我们将前三行代码注释掉,加入了三行写死的代码。
args=new String[2];
args[0]="C:\\Users\\kltz\\Desktop\\AtlasDemo\\plugin";
args[1]="C:\\Users\\kltz\\Desktop\\AtlasDemo\\plugin\\bundle-info.json";
数组第一个参数代表待处理的apk所在目录,数组第二个参数代表生成的插件信息存的文件。而我们是在项目根目录建立一个文件夹叫plugin,后来将插件的apk都复制到该目录进行处理。
main函数中开始调用了ApkPreProcess.preProcess(path);将第一个参数传入,该函数所做的事其实很简单,就是将apk重命名为so,怎么个重命名法呢。首先遍历该文件夹,得到以apk后缀的文件,通过解析拿到该apk的包名,将该apk命名为lib开头,后面紧跟包名,包名中的.会被替换成_,最后文件后缀是so,如果该目录存在了该so,则将原so文件删除,然后将apk重命名。就是这么简单。看代码
public static void preProcess(String mDir) {
Collection<File> apkFiles = org.apache.commons.io.FileUtils.listFiles(new File(mDir), new String[]{"apk"}, true);
for (File file : apkFiles) {
String pkgName = PackageLite.parse(file.getAbsolutePath()).packageName;
pkgName = "lib" + pkgName.replaceAll("\\.", "_") + ".so";
File targetFile = new File(mDir + File.separator + pkgName);
if (targetFile.exists())
targetFile.delete();
System.out.println("rename: " + file.getName() + " -> " + pkgName);
while(!file.renameTo(targetFile)) {
System.gc();
Thread.yield();
}
System.out.println("ApkPreProcess.preProcess() processed " + pkgName);
}
}
重命名完成后接下来就是遍历该文件夹,查找以libcom_开头的的文件,注意这里是一个约定,就是包名以com开头。然后通过PackageLite.parse()函数解析每一个so,通过packageLit.getBundleInfo()函数获得解析结果并将其扔到一个json数组中去,最后将该json数组写入文件,也就是args数组第二个参数指定的内容。
置于如何解析,这里不做展开了,代码太长,其实这个已经涉及到反编译的范畴了,其实该项目的一大部分源码是AXMLPrinter的源码,一个android xml反编译的东西,这里提供源码下载AXMLPrinter。 倒是可以看看getBundleInfo()函数
public JSONObject getBundleInfo() throws JSONException {
JSONObject jsonObject=new JSONObject();
jsonObject.put("pkgName", packageName);
jsonObject.put("version", versionName);
JSONArray activityArray=new JSONArray();
for (String name:activitys) {
activityArray.put(name);
}
jsonObject.put("activities", activityArray);
JSONArray servicesArray=new JSONArray();
for (String name:services) {
servicesArray.put(name);
}
jsonObject.put("services", servicesArray);
JSONArray receiversArray=new JSONArray();
for (String name:receivers) {
receiversArray.put(name);
}
jsonObject.put("receivers", receiversArray);
JSONArray providersArray=new JSONArray();
for (String name:providers) {
providersArray.put(name);
}
jsonObject.put("contentProviders", providersArray);
JSONArray dependencyArray=new JSONArray();
for (String name:dependency) {
dependencyArray.put(name);
}
jsonObject.put("dependency", dependencyArray);
jsonObject.put("md5", apkMD5);
jsonObject.put("size", size);
jsonObject.put("hasSO", hasSO);
return jsonObject;
}
其实就是将解析结果转化为json,很简单有木有。
注意生成的json文件的文件名要为bundle-info.json,当然你也可以通过修改代码使用别的文件名,然后该文件要放在assets目录下。
解析该json文件的代码在openatlascore项目中的BundleParser类中的parser函数中,有兴趣可以先去看看。
接下来要做的事就是将该json打包进apk,把so放到对应目录,即armeabi目录下,按照作者的原话就是加快启动速度,可以见这个issue插件可以不放在主工程的libs/armeabi目錄下麼
还有,赶紧亲自试一试吧,只要亲自试过了之后,才会觉得,哦,原来是这么一回事啊。
什么,源代码,木有源代码,代码见上一篇文章末尾[Android插件化开发之Atlas初体验](
http://blog.csdn.net/sbsujjbcy/article/details/47446733)