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

阅读数:7219 2015 年 9 月 29 日

前段时间,奇虎 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:本次采访就到这里,感谢您接受采访。