Android插件化开发指南
上QQ阅读APP看书,第一时间看更新

序一

当接到包老师邀请写序时,我真是受宠若惊。大家都知道,作为一个程序员,写代码拿手那是自然的事情,文字工作实在不是我的强项,但是能给包老师的书写序,实在是荣幸之至,更何况盛情难却呢。

我与包老师相识是在2015年的一个关于DroidPlugin的分享会上,那会儿我还在360公司做手机助手。2014年年底到2015年年初的时候,在公司比较空闲,所以写了一个插件化框架叫DroidPlugin,并在2015年7月份以公司的名义在GitHub上开源了出来,承蒙大家抬爱,在极短的时间内便收获了数千颗星。因为在项目的介绍中我写上了“免修改、免重打包、免安装运行”的宣传语,所以也被一些人冠以“360黑科技”,在知乎上甚至有人认为这是360的什么阴谋。不管是不是什么阴谋,但这个项目让我认识了很多行业中的大牛(包老师就是其中一位),确实是我意料之外的事情。后来跟包老师、任玉刚老师几位经常结成“饭醉团伙”,自然也少不了讨论插件化技术。既然与包老师是因为DroidPlugin开源项目相识,而包老师这本书的内容涉及的很多技术也跟其相关,所以我还是主要介绍一下DroidPlugin项目中的相关技术。

如你所见,那会儿正是插件化技术大热的时候,各大公司也相继开源了自己的插件化框架,但是总结来看,所涉及的技术原理也大同小异。但是DroidPlugin在其中的确算是比较“奇葩”的一个,因为它实际不止是一个插件化框架,更多的算是一个用户态虚拟机,后来大多数的双开软件也都参考了它的源码或者原理。

要在Android上实现免安装、免修改运行一个App,并不是一件容易的事情。因为Android系统设计和权限的限制,我们需要做很多的工作……这跟Docker不一样,虽然都是Linux系统,但在Android上我们不能要求root权限,而Docker则没有这些限制,因此Linux内核提供的namespace、cgroup、chroot等能力也自然可以应用在Docker中。同时因为国内各大厂商的定制系统,我们也做了大量的适配工作。虽然现在看起来这个框架还有不少缺陷,但在当时确实算是比较先进的一个框架。

DroidPlugin使用了一些比较hack的技巧,但是总结起来也就是一句话“利用hook技术实现欺上瞒下,从而达到免安装运行的目的”。因为Android系统出于安全考虑,系统服务与App进程采用分进程设计,它们之间通讯使用binder技术,系统服务实际上是不知道App进程中运行的具体代码,这为我们实现“欺上瞒下”提供了可能。所谓“欺上瞒下”中的“欺上”是指我们可以通过某种技术手段,拦截所有插件发向系统服务的通信,让系统不知道插件在我们的宿主App中运行;“瞒下”是指通过拦截并模拟系统服务发向插件的通信,让插件“以为”自己已经被安装。这样我们就可以模拟一个环境,让插件运行在宿主的模拟进程中。

要做到欺上瞒下,hook技术不可缺。hook这个词是我从Window安全技术中借用而来的,这实际上是一种函数拦截技术。在某个函数调用流程中插入我们自己的函数,实现对目标函数的参数、返回值的修改。比如某个函数调用流程为:a调用b、b调用c,那么我们可以动态拦截对b函数的调用,插入我们自己的函数h,修改后的调用流程为:a调用h, h调用b, b调用c,因为h是我们新插入的函数,由我们自己编写逻辑,那么在h函数中调用b的时候,我们就可以修改其参数、返回值,甚至可以中断调用流程,不调用b函数而伪造一个返回结果给a。在此过程中,a是不知道它调用的b函数已经被我们修改了。这就达到了我们“欺上瞒下”的目的。

hook技术的思想非常类似于设计模式中的代理模式和Java Spring框架中的AOP(Aspect Oriented Programming,面向切面编程)。尽管它们的实现原理完全不同,但目的却差不多。DroidPlugin中的Hook技术实际上是使用了Java中的动态代理技术,它只是在一些关键点通过动态代理生成的对象替换掉系统原来的对象,从而完成了对系统通信的hook。

这其中最关键的是对AMS(Activity Manage Service)和PMS(Package Manage Service)以及Handler的hook。AMS负责管理Android中Activity、Service、Content Provider、Broadcast四大组件的生命周期以及各种调度工作,我们hook它可以实现对插件四大组件的管理及调度;PMS负责管理系统中安装的所有App,我们hook它是为了让插件以为自己已经被安装;Handler是系统向插件传递消息的一个桥梁,我们hook它则是为了把系统发向宿主的消息转发给插件。至于在DroidPlugin中看到的对其他系统服务的hook,在大多数情况下都是为了“欺上”——让系统感知不到插件的运行。DroidPlugin还实现了一个简单的AMS服务、一个PMS服务,将hook后的AMS和PMS系统调用转发到我们自己的AMS和PMS服务中去,由DroidPlugin自己管理。

除此之外,DroidPlugin的另外一个特色是“占位”操作。众所周知,在Android四大组件中除了BroadcastReceiver之外,其他如Activity三大组件都需要在AndroidManifest. xml中注册,这是静态的,是Android框架要求我们预先写死的,我们没有办法动态地向系统注册一个Activity或者是Service。所以插件中的Activity、Service、Content Provider则是不可能向系统真正注册。所以我们使用了“占位”的技术,也就是说先在宿主中注册很多的“坑位”,比如对于Activity来说,就是Activity1、Activity2、Activity3等。在我们需要启动某插件Activity的时候,可以通过hook技术,将其替换为某个坑位,如Activity1,让系统服务去启动坑位Activity;而在真正的系统服务AMS回调插件进程要求插件进程去启动坑位Activity的时候,我们再换回插件的Activity,这样我们就实现了插件Activity的免注册运行。当然,因为Activity的Launch Mode等各种参数问题,我们还需要做很多的细节工作,才能完美。

为了某种完美,DroidPlugin中做了大量的适配工作,这让其初看起来复杂又臃肿,但是当了解到其中的原理和关键代码后,你会发现如果仅仅是满足“插件化”需求的话,那么其中很多适配并不必需。从现在来看,DroidPlugin实际上算是一个用户态虚拟机的雏形,从插件化的角度来说则有些“重”了。但是它对于大家深入研究Android技术本身,则或多或少会有些帮助。

现在市面上也有各种各样的开源插件化框架,其中很多都已经在各大公司自己的产品中长期稳定使用,满足了各种现实的需求,它们的稳定性、可用性都还是不错的。包老师在本书中也对其中很多插件进行了介绍剖析。

如果我们不满足于业务研发,希望可以了解一些Android底层知识,研读这些开源框架的源码则大有裨益。当然,包老师的这本书是国内第一本介绍插件化技术的书籍,作为我们学习插件化技术的入门书籍,则相当合适。

张勇,2018年6月于北京