专访DroidPlugin作者张勇:安卓黑科技是怎样炼成的

5,115 阅读9分钟
原文链接: www.infoq.com

前段时间,奇虎360在Github上发布了一个Android开源项目DroidPlugin,这是一个实现动态加载的Android插件框架,可以免安装、免修改的运行第三方APK。一时间,它被誉为安卓黑科技,引起行业内的关注。

据其官方文档介绍,DroidPlugin的目的是改进大型APP的架构,实现多团队协作开发。它的部分特性如下:

  • 支持Android 2.3 - 6.0(Android M)系统版本。
  • 集成简单,将DroidPlugin引入到项目后仅需很少代码集成。
  • 高度隔离,宿主App和插件App之间的是完全隔离的。
  • 插件管理,插件的空进程等会被及时处理,静态广播会被当作动态处理。

DroidPlugin的原理是利用Android一个进程可以运行多个APK的机制,通 过API欺骗让系统以为只有宿主App存在,同时通过预先占坑来创造插件App的运行环境,最后通过动态代理实现函数hook、Binder代理绕过部分系统服务限制,从而实现应用的组件化。

据InfoQ了解,它的作者是360高级工程师张勇,InfoQ记者对其进行了采访,了解了项目背后的更多信息。同时,张勇也接受InfoQ邀请,将于12月18日在ArchSummit北京2015架构师大会上作“分拆:DroidPlugin的实现原理及其应用”的分享。

受访嘉宾介绍

张勇,从2009年开始从事Android的研发工作,分别在机锋网、金山、360从事过桌面、安全、市场等Android APP的研发管理工作。2011年加入360手机助手团队,目前在奇虎从事360手机助手Android客户端的研发工作,专注于Android APP安全、架构领域。

InfoQ:请介绍一下DroidPlugin项目的背景,你们为什么要开发这样一个框架呢?

张勇:之所以开发这个项目,应该算是现实的驱动,我们手助(360手机助手)的团队并不大,但是业务特别 多,并且还有跨部门的对接,这些业务对应着手助里面不同的产品模块。我们开发和发布的节奏是“搭火车”的模式,一般每周发布个小版本,然后每个月发布一个 大版本,各个产品模块独立研发,然后在每周的固定时间整合到主应用里面发布。但这样会遇到一个问题是,各个产品模块的研发进度、时间并不统一,有的产品周 一已经开发完了,有的产品周四还没开始做,通过这样“搭火车”就有不少时间会被浪费掉。

另外,客户端发版有不少成本,一次应用更新从发布到有一定的覆盖量,通常需要一到两周时间,而新产品从上线到有一定量的反馈,也需要时间,这对于一个快速迭代的产品来说也不够高效。

所以我就想着调整我们应用的架构,将一些业务模块做成可拆分的,并且做到彻底隔离,以实现独立发版、快速放量。另外手助中的一些功能模块,公司 的其它团队也在做,并且做的比我们好,那我就想能不能把他这个模块拿过来放到手助里面,他们只需要交付一个APK就可以,这样既会解决掉我们重复造轮子的 问题,也不会存在开发成本、进度和版本管理等问题。

InfoQ:能介绍一下DroidPlugin的开发过程吗?它是如何应用到手机助手里的?

张勇:我大概从14年6月份就有想法,但当时没有时间开发,不过一直在构思怎么做,然后14年12月份我才能够把更多精力花在改善手助的架构上来。真正开发的时间不长,大概到3月份就做完了,但因有一些兼容性担忧,所以直到15年4月份到5月份,才开始慢慢应用到手机助手里面去。

一开始我还不是很自信,因为这对我们的手助改动比较大,同时Droid Plugin会涉及到很多系统底层的修改。不过我的领导、手助的技术负责人韩三普非常支持我,所以我就开始尝试着去做了。

实际应用的的契机不是重构,而是App瘦身。当时手助的APK越来越大,早期能控制在2M以内,但最后都接近10M了。我们知道App的体积对 安装的成功率影响很大,比如内存不足、dexopt优化导致的安装失败会增加,安装失败对用户造成的挫败感特别强。同时更小的体积也会便于用户下载,所以 我们就使用了Droid Plugin将一些模块拆分出去、动态加载。从而实现了对手助App的瘦身。

在完成后,我们用灰度发布的方式来逐渐尝试。开始就试着放了几千的量,发现没什么问题,然后就是几万,十万,然后终于有一些反馈过来。主要问题 还是在适配方面,虽然我们在发布前在不同型号的手机上都做了适配性测试,但放出去还是有问题。后来我们就让用户把他们的手机寄过来,调试、修复完bug之 后再给他寄回去,然后继续放量、解决用户反馈,通过这样的循环,到现在已经基本没什么问题了。

InfoQ:别人都叫它安卓黑科技,您自己对它是怎么看的?

张勇:我觉得它并算是多么的“黑”,因为它还是有一些限制的,并不是百分之百的完美。在项目介绍里我也提 到,它不能发自定义的通知栏,以及Launch Mode的bug,这些都表明它其实并不够完善。如果真正完善了我觉得可以称得上是黑科技,但目前来说还算不上。不过,我把它开源出来是希望对更多的 Android开发者有帮助,如果黑科技指的是一些恶意用途的话,那我宁愿它永远不是。

InfoQ:那DroidPlugin的主要应用场景是什么?

张勇:其实它主要目的刚才已经提到,就是拆分模块。如果是小团队,几个人甚至独立开发应用的,那么并没有使 用它的必要,相反还会增加产品复杂性、降低代码复用率。但是对于那些平台类的大型产品,是由很多子产品组成的, 而各个子产品又是不同的团队在做,甚至在一些公司会是不同的事业部在做。像这种情况就需要这样的一款框架,因为每个子产品都有自己的开发团队,要将它们整 合起来、实现同步开发比较困难,所以最好有这样一个架子,或者说平台,让各产品团队平时可以各开发各的,他们只需要遵守一些约定即可,在集成时不需要耗费 额外的工作量。

InfoQ:Android的动态加载有不少的实现方式,您为何选择采用动态代理技术来实现?

张勇:其实在DroidPlugin之前,我还做过一个Android的Hook项目,叫ZHook,我也把它的二进制文件放在了GitHub上,它可以实现对任意Java方法的hook,原理和Xposed一样。但ZHook只支持Dalvik虚拟机,不支持ART,并且在Dalvik模式下面也还有一些缺陷,所以我就转到使用动态代理。

动态代理相比较起来更可控,稳定性更好,并且Dalvik和ART两个运行时都支持、适配范围更广,比如Android 6.0刚出来不久,我们测试一下也都是支持的。

InfoQ:从项目介绍里我们看到宿主App和插件App是彻底隔离的,为何这样设计?

张勇:所谓彻底隔离就是说,插件APP直接无法直接访问、调用彼此的代码、资源,这不只是编译层面的隔离, 在运行时通过过Java反射等手段也无法做到,反过来框架也不关心插件里运行的代码。DroidPlugin实现的目标之一就是彻底的拆分模块,做到插件 App不仅能在DroidPlugin中运行,也能安装到系统独立运行。这样我们就可以让插件之间、框架和插件之间尽可能的减少依赖、耦合性。真正实现插 件的独立的研发、测试和发布,而不必过多的考虑插件之间兼容。

当然,实际项目里面,DroidPlugin和插件App之间有通信需求的,有通行需求就有兼容问题。所以我们推荐使用Android的一些标准API来实现这些需求,比如Intent、AIDL、ContentProvider等机制。这样也许能将耦合性降到最低。

InfoQ:您提到这个项目是您个人开发的,那为何会以公司的名义开源出来?

张勇:这点当时没想那么多,因为这是在公司研发、也用在公司项目里面,那么以公司名义开源是很自然的事情。 另外360一向很重视开源,我们在GitHub上有一个统一的账户,所以我也很乐意将它加入进去。我把它以公司的名义开源出去,能够表明它是经过实践检验 的,能吸引更多的人来用,找出问题,回馈项目,这样可以形成一个良性的循环。这样大家可以一起来把这个框架做得更好。

InfoQ:DroidPlugin后续有哪些开发计划?

张勇:第一个是继续完善框架,在项目介绍里也提到还有一些缺陷,后面我们会逐步解决掉。第二个是可能会去加 一些新的功能,比如说插件之间的依赖控制、一套低耦合的通讯机制等等。第三是目前用户使用的这些接口还不是太友好,我想稍微改一改,让它更易用一些。最后 等API稳定以后,可能会补上一些文档。

InfoQ:本次采访就到这里,感谢您接受采访。