最近在研究Android应用的插件化开发, 插件化都是在解决以下几个问题:
本章我们来研究最后一个问题:资源共享与冲突。在《Android应用程序插件化研究之AssertManager》中,我们实现了加载插件apk中资源问题,实际上我们是单独创建了用于访问插件资源的AssertManager和Resource对象,即,插件独立使用一个资源管理器,这样插件宿主之间无法共享资源。
资源共享
如果需要宿主、插件之间使用同一套资源管理器,那么我们需要将插件的资源路径添加到宿主的AssetManager中。
我们知道,apk包括代码和资源,在apk编译过程中,dex工具将代码打包成.dex文件,资源文件会由aapt工具生成对应的ID,aapt在打包的时候组织成resources.arsc文件(详细参考: Android应用程序资源的编译和打包过程分析),resources.arsc文件是用来描述资源ID和资源位置配置信息,从18个维度描述了一个资源ID的配置信息(语言、分辨率等),就是资源ID和资源的索引表。资源的ID生成是有规则的,规则:0xPPTTNNNN,由8位16进制组成,其中:
PP段:表示资源的包空间:0x01表示系统资源空间,0x7f表示应用资源空间。
TT段:表示资源类型。
NNNN段:4个16进制表示资源id,一个apk中同一类型资源从0000开始递增。
例如:
nt anim pop_dialog_in 0x7f040000
int anim pop_dialog_out 0x7f040001
int anim slide_left_in 0x7f040002
int anim slide_left_out 0x7f040003
int anim slide_right_in 0x7f040004
int anim slide_right_out 0x7f040005
int anim update_loading_progressbar_anim 0x7f040006
int array indicator_tab_icon 0x7f050001
int array indicator_tab_titlt 0x7f050000
现在问题来了,宿主apk和插件apk是独立编译出来的两个独立的apk,那么其中就有资源ID相同的情况出现,从而产生资源ID冲突。如何解决这个问题?看了一些开源框架,解决的办法就是修改资源ID的PP段,大体有两种做法:
修改aapt源码,定制aapt工具编译期间修改PP段。
DynamicAPK的做法就是如此,定制aapt,替换google的原始aapt,在编译的时候可以传入参数修改PP段:例如传入0x05编译得到的资源的PP段就是0x05。个人觉得这个做法不是太灵活,入侵了原有的开发编译流程,不好维护。
修改aapt的产物,即,编译后期重新整理插件Apk的资源,编排ID。
前面说过apk编译之后会生成ID以及对应的索引表resorce.arsc,那么我们能不能后期修改相关ID及索引表呢?答案是肯定的,个人比较赞同这种思路,不用入侵原有编译流程。
结尾
插件系列文章到此结束了,如果你从第一章开始看到此处,你应该明白插件化要解决的问题、实现的思路、要掌握的技术,如果你理解完这5篇文章,你应该有能力写出一个简单的插件方案了。文章重在点明思路,实践才是最重要的,关于实践我们要做的还有很多很多。