Android插件化技术是一种这几年间非常火爆的技术,也是只有在中国才流行起来的技术,这几年间每每开发者大会上几乎都会提起关于插件化技术和相关方向。在国内各大互联网公司无不都有自己的插件化框架。
插件化技术到底是什么?
其实很好理解,像某些App中整合了很多功能点,而一些不是非常必要的功能一般会在用户点击功能入口时才会通过网络下载一个像补丁的东西,然后将其加载起来,使功能可用。而这种通过补丁式免安装、热加载的技术就是插件化技术。插件化技术需要清楚区分两个概念:宿主和插件。宿主是指我们的主APK包,它是像正常APK一样需要安装的; 而插件理论上它也是一个APK包,但是它是被宿主加载起来整合到宿主中的像补丁一样的东西。它们的关系就像Windows中exe文件和dll文件一样。
为什么需要插件化?
在我们日常开发版本迭代中,产品需求如滚雪球般使得工程代码越来越庞大,也开始难以维护。新功能以及Bug修复想要在最短时间发布版本到各大市场的到达率低,使得App更新周期非常得长。这时使用插件化的好处就非常明显。因为插件化可使功能模块代码解耦,使得能够很好地规范工程代码和并行开发以及提高发版的灵活性。在用户手机环境中只要有网络情况下使用App就能够下载插件补丁快速到达和动态加载插件代码达到免安装即时升级的效果。而且在性能方面考虑,因为工程代码越来越多,也能有效地解决DEX文件方法数达65535的问题,抽离代码后,安装包大小也相应得到控制。在用户环境中,也可按需加载逻辑,从而也更有效地控制内存。
什么情况需要插件化?
对于核心底层模块,一般不建议进行插件化,就算要插件化也得在App起来后第一时间将其加载起来使得生效;
对于核心业务模块,可进行插件化,但也要在最初时加载起来;
对于非核心业务模块,建议进行插件化,用户可以联网后下载插件按需加载。
模块功能开发初期要反复添加或修改四大组件情况下不宜进行插件化,因为大多插件化框架对四大组件支持并不完美,一般情况下都是先在宿主中AndroidManifest中先有声明组件才能够在插件中实现使用,所以在模块功能开发成熟后再考虑插件化。
国内插件化框件可以称得上是五花八门,各有各的流派,下面我们来简单认识一下一些比较出名有代表性的插件化框架:
2012年7月,大众点评的屠毅敏发布了第一个Android插件化开源项目:AndroidDynamicLoader。这个插件化的特点在于整个项目中只有一个Activity,它通过动态加载插件中的Fragement来实现页面的跳转。而插件资源的加载就是通过反射调用AssetManger的addAssetPath方法来处理的。由于它是最古老的插件化框架,而且是基于Eclipse和Ant来实现的,因此在今天AndroidStudio和Gradle盛行的今天显得有些过时。
2013年3月,淘宝客户端的伯奎的一个技术分享,专门介绍了Atlas插件化框架,分享中包括了对Android系统底层几个重要类的Hook介绍、增量更新、降级、兼容等技术。
2014年3月,《Android开发艺术探索》作者任玉刚开源了dynamic-load-apk插件化框架,些框架并没有对Android系统底层代码进行Hook,而是通过代理形式创建一个ProxyActivity类来进行分发启动相应的Activity,最有特式的语法就是that关键字。
2014年11月,houkx在GitHub上发布了插件化项目android-pluginmgr,此框架是最早提出在AndroidManifest文件中注册一个替身StubAcitivty来欺骗ActivtyManagerService,来解决插件Activity的支持。
2015年4月,淘宝的Atlas框架也就是OpenAtlas项目发布在Github上,并改名为ACCD。它是基于OSGI框架的。在资源方面,里面提出了通过修改重新生成aapt命令来支持插件中资源id的自定义,使得很好地支持插件资源和宿主资源合并后冲突的问题。它也对Android底层的Instrumentation的execStartActivity方法做了Hook,来实现Activity的插件化。
2015年8月,360手机助手的张勇公开了他们家手机助手使用的插件化框加DroidPlugin。些框架有一个牛逼的地方就是对四大组件很好地支持,以至它可以将任意的App当成插件来加载到它自己的宿主中去。它的内部Hook了很多Android不同版本的系统底层代码,所以也是一个相当复杂的插件化框架。
2015年11月,林光亮发布了Small插件化框架,此框架特点是把插件的ClassLoader对应的dex文件塞入到宿主的ClassLoader中,从而使宿主可加载插件中任意类,在资源方面也是通过反射调用AssetManger的addAssetPath方法来处理的,但它没有使用修改aapt来解决资源冲突,而是在生成插件R.java和resources.arse这两个文件后,再把它们所有资源前缀进行修改。
2016年8月,掌阅推出Zeue
2017年3月,阿里推出Atlas
2017年6月,360手机卫士的RePlugin
2017年6月,滴滴推出VisualApk
……
虽然说插件化技术框架五花八门,但它们所解决的事情都是一样的,就是怎样支持或者说怎样更好地支持宿主加载使用插件的逻辑。而这个支持问题其实就是要攻克以下最根本的三点技术:
1、宿主和插件中如何相互调用代码
2、宿主加载插件后如何解决资源读取问题
3、在插件中对四大组件怎样更好地支持
解决了这三点后,剩下的事情就是锦上添花了。我们从上面插件化简史中可以大概认识到一些插件框的特点,综合一下解决方案。比如要解决第1点代码相互调用的问题,就可以把插件的ClassLoader对应的dex文件通过反射塞入到宿主的ClassLoader中,然后在宿主或插件中可以通过反射来实现相关类的调用; 而第2点资源问题,就是通过反射调用AssetManger的addAssetPath方法来合并资源,至于资源冲突就修改aapt就可以解决好了;最后一点插件中使用四大组件是最为麻烦的事情,我们可以通过that关键字的代理形式实现,也可以通过Hook系统底层代码来尽理更好地实现,也可以不实现,提前在宿主中声明相当多的空的组件来待用。其实都行,所以我们在决定对工程下刀插件化之前,就要认真考虑结合实际情况来选择哪一种的插件化技术才是适合自己的了。