Android P 来了,插件化将何去何从?Android作为操作系统涉及的面比较广,不可能在所有的方面都能走在大家的需求之前,尤其是这几年,App越来越大,而相应的Android官方给出的方案一直不太符合中国的国情,先是65536问题,再是多团队协同开发,再到App如何瘦身,这些问题一直困扰着中国的开发者。国内的开发者们在经过了无数高手的实践之后,在2016、2017年插件化技术越来越成熟,发展到了顶峰,井喷了许多高质量的插件化框架,以应对大型App的开发。2018年,Google官方也终于祭出了大杀器 -- 禁止调用私有API,并推出官方的动态化框架Android App Bundles。 随着Android P的发布,使用插件化的你们做好应对方案了吗?
京东商城资深Android系统架构师,Android组件化技术负责人,致力于Android系统构架和优化,带领团队从无到有实现了Aura组件化体系建设。
导读
全球移动技术大会(GMTC):是由InfoQ主办的全球顶级技术盛会,大会的目的是促进全球移动技术交流,推动国内技术升级。
手机京东开放平台:基于手机京东数年移动研发技术实践积累,为企业移动应用研发提供一站式技术解决方案,帮助企业快速构建高可用移动应用。
Aura:是手机京东开放平台 Android 端解决方案,提供了适合大型企业开发的模块化,插件化框架和平台,可有效提高企业的开发效率。
2018年6月22日,我有幸在北京国际会议中心的GMTC大会上,分享了一个主题《当插件化遇到AndroidP》。分享这个议题的初衷是由于今年Google放出了Android P的开发者预览版的同时加入了限制App调用非SDK接口的计划,它这是释放了一个将会治理目前各种篡改Android系统乱象的信号,随着这项计划的推进,将会涉及到国内的很多App,尤其是使用加固和插件化技术的App。作为App的开发者,早一步重视这个事情,则将会早一步受益,基于这个前提,京东已经开始着手进行此方面的研究和推进工作了。
演讲的主题主要分为以下五个部分:
● 解读一下Android P的禁令
● 我们一起来学习一下Android官方的动态化框架Android app bundles
● 插件化的路在何方
● 与大家一起分享京东的架构升级和重构是怎么做的
● 解决京东架构升级中的一些关键技术点
Android P 是 google 将在2018年推出的 Android 新版本,将于第三季度发出正式版本,在这之前会有五个预览版本。在3月份放出第一个预览版本的时候,宣布了禁止应用调用系统非SDK接口的消息;在5月初,Google IO 开发者大会上,推出了 Android 的官方动态化框架 Android app bundles。下面篇幅中我将会对这两个方面进行一个全面解读,并对可以采取的一些应对方案进行分析和阐述。
访问非SDK接口的后果
在 Android 官网上面列出了几种通过反射调用访问非SDK接口的方式和引发的后果,从表格中我们看到后果主要有两个分别是抛出异常和返回的结果列表中没有私有API接口。
实施计划
但是,Google 并没有一刀切的直接执行禁令,而是分步骤的进行,给了开发者一个缓冲期去修改自己的App。采用的办法是维护三个名单,分别是浅灰名单,深灰名单,黑名单:
浅灰名单中的接口还可以继续在 AndroidP 中使用,但是在未来的版本中就有可能会移到黑名单中,也可能会开发新的public API来供开发者光明正大的使用。
深灰名单中的接口,在Android P 的后续预览版中将不可访问,所以如果有使用深灰名单中的接口,必须马上进行整改。
黑名单中的接口就不允许调用了,如果调用直接就是抛异常等行为。
注意
这个禁令是在 Android P 上起作用,而且针对应用的 target SDK 是否是 28 会有不同的表现;Google Play 要求从 2018 年 8 月份开始应用的最低版本为 Target SDK 26,明年可能将会要求最低的 Target SDK 为 28,所以开发者一定要重视。
Android 官方动态化框架 Android App Bundles,简称 AAB,是今年5月初在Google IO 开发者大会上首次亮相,这是官方支持的动态化框架,值得开发者尽快学习和使用。
通过一张动图来感性的认识一下AAB,左图是legacy APK,右图是动态分发 apk, 可以看到左图是把所有的代码和资源统统塞到了用户的App中,所以导致App体积变大,而右图只把适合用户手机分辨率和语言等按维度划分的相关部分动态下发到用户手机上,这样能够极大的减小APK的大小。
Android App bundles的内部结构
AAB拆分后主要分为三种类型的APK,它们分别是Base APK, configuration APKs, Dynamic feature APKs;
Base APK是首次安装的APK,它提供了应用的基本功能,并且包含其它Splite Apks 可以访问的代码和资源;
Configuration Apks包含了特定的屏幕密度,cpu架构和语言资源等,有两种类型的configuration apk, 一种是 Base Configuration Apks, 一种是 Feature Configuration Apks,当用户下载Base APK或者Dynamic feature Apk的时候,只需要下载其对应的特定资源;
Dynamic feature Apks是首次安装的时候不需要的代码和资源,是由开发者自行定义和开发出来的,在代码中使用 Play Core Library 动态下载和安装
AAB的整体流程
首先,开发者研发自己的APP,并把它编译打包成AAB格式,然后上传到Google Play服务器上;
服务器会把客户的AAB文件拆分成三种类型的APK,并存储在Google Play的服务器中;
当用户使用时,会从市场上首次安装Base apk, 然后按需下载 Dynmaic Apk, 对于以上两种APK, 均有配套对应的 feature configuration APK进行下载。
Android App Bundles 总结
优点
● 官方推出的,背景强大
● 通过了两种方式减小了 APK 的体积,一是去除了与用户机型不匹配的代码和资源,二是动态下发首次不需要安装的部分
限制
● 目前只能通过 Google Play发布
● 处于测试阶段,用户需要申请加入 Google 的 Dynamic feature beta program 计划才能使用
● 最低支持的版本是 Android 5.0,
● 只是单纯的减小了包体积,不能通过动态下发来修复Bug和增加之前未预算的新功能,也不能增加Base Apk的manifest中没有的四大组件
使用成本
● 需要开发者的 IDE 升级到 Android Studio 3.2
● 需要集成 Play Core Library 来支持 dynamic feature 的下载和安装
● 可以加入 SplitCompat 库以支持6.0以下的手机来热更新D ynamic feature apk
● 所有的模块必须位于同一个工程
● 如果之前的Library想改改造成Dynamic Feature Module 必须手动改造
实现插件化的黑科技
插件化的流派比较多,但不管是哪种流派,它们一般都用到了一些黑科技,主要有三种:
● Hook App运行的关键点,以达到动态检测是否调用了插件中的代码和资源,比如Hook Instrumentation等
● 动态的加载插件中的类,这里分为单Class Loader和多Class Loader技术,这两种技术都用到了反射技术,以达到替换系统的ClassLoader或者是增加自己的Class Path的目的
● 动态的加载插件中的资源,一般会反射调用 AssetManager.addAssetPaths 这个方法.下面这张图是通过官方的扫描脚本扫描出来的手机京东的插件化框架Aura使用非sdk接口的结果,从中可以看出使用了浅灰名单中的两个方法
路在何方
当初为什么要研发和使用插件化技术,现在我们的真实需要是什么?
研发和使用插件化技术的原因
● 绕过65536问题,快速启动
● 模块解耦
● 多个团队并行开发
● 加快编译速度
● 动态部署
现在的真实需求
● 去黑科技
● 平稳过渡
● 边界隔离
● 并行开发
● 独立调试
● 快速编译
● 动态部署
探索道路
● Follow官方的方案
● 组件化方案
● 大前端:RN, Weex, 快应用等
方案对比
功能 | 插件化 | AAB | 组件化 | 前端 |
---|---|---|---|---|
边界隔离 | ✔️ | ✔️ | ✔️ | ✔️ |
并行开发 | ✔️ | ✘ | ✔️ | ✔️ |
独立调试 | ✔️ | ✘ | ✔️ | ✔️ |
兼容性 | 差 | 好 | 好 | 好 |
成熟度 | 黑科技 | 差 | 需研发 | 好 |
动态部署 | ✔️ | 打折 | 无 | ✔️ |
迁移成本 | / | 大 | 中 | 大 |
这几个方案各有优缺点,其中AAB方案还不太成熟,前端和AAB的迁移成本都比较高,组件化的方案也需要研发一个适合自己公司的框架。
适配步骤
● 增加组件化功能
● 组件化和插件化相结合,具有两者的优点
● 从长期来看,可以向AAB和前端方向靠拢
● 持续改进和优化,以保持自身的活力
手机京东的架构升级和重构
制定重构目标
第一要务就是去除插件化去除黑科技
第二要务是从现有的方案平稳过渡到新方案
第三是我们在重构的过程中顺便把京东之前的一个毒瘤去除掉,也就是体积庞大并且中心化的基础类库JdLib去中心化
最后是插件和组件可随时互相转换
升级手机京东的架构
目前京东的插件化框架使用的是阿凡达(Avatar)项目中的 Aura 框架,我们将现在的的插件化框架 Aura 升级到插件化和组件化相结合的 AuraPlus 框架,它具有灵活互转,无缝调用,优势互补等优点。
从图中,我们看到,一切皆组件,最下层是基础组件,再往上层是业务组件和业务插件,业务组件是直接依赖基础组件的,它们中间不再存在JDLIB这个中心化的基础类库了。再上层是通过业务模块任意组合成的京东的应用。
一个核心,四大技术
一个核心
● 我们是围绕"高内聚,低耦合"这个核心思想来进行的重构和升级的。
四大技术
● 以 Project 和 Module 为边界进行组件间物理隔离
● 使用了一个巧妙和灵活的组件间通讯的方式,在自由和隔绝之间寻找到了一个最佳平衡点
● 处理组件间的依赖关系
● 其它的关键技术点
架构的项目结构
我们采用的是一主工程多子工程的开发模型,一切皆组件,组件是采用的小团队独立开发的模式。
我们这个模型与网上流行的一般的 Library和 Application 承着编译类型的不同而转换的方式是不同的,我们这种方式需要在每个组件内实现一个独立的 Application 壳工程
这样带来的好处是避免了很多 Library和 Application 互相转换带来的坑,比如 ButterKnife 中的 R 和 R2 问题。
组件间的通讯
组件最终的编译结果生成了两个产物,一个是Api.jar,一个是Full.aar
当组件需要依赖其它组件时,只需要provided其它组件的Api.jar,而在宿主工程编译整体APK时,再 Compile Full.aar
这种巧妙的方式,能够在自由和隔离之间找到一个最佳平衡点。
组件依赖
我们看一下这个图,组件A依赖组件B,C,D,组件B依赖组件E。
那么能不能通过依赖传递让组个A再依赖组件E呢,答案是不能,我们制定了一个规则,如果组件A想依赖组件B,则必须明确写出
这样做的好处是简化了组件之间的依赖关系,使它们基于接口的依赖更加明确和清晰化。
组件提效武林秘籍
前边我们重构的的结构图中介绍到,我们去除了 JDLIB 这个庞大而臃肿的基础类库,业务组件和业务插件是直接依赖基础组件的
但是常用的基础组件有好多个,如果每个业务组件都写一遍的话,则会比较麻烦和重复,这对于追求完(tou)美(lan)的程序员来说是不可接受的
所以我们使用 Gradle 插件做了一个薄而灵活的SDK层,这样,业务组件和业务插件只需要一行代码即可引入多个常用的基础组件,起到了原先JDLIB的功能,但又不致于耦合和臃肿。
其它秘籍
预防资源ID冲突,可以加上resourcePrefix前缀;
为了方便管理第三方库,做了一个统一管理第三方库的组件 ThirdBundle;
因为组件对外暴露的API是jar,不能有资源,所以还专门实现了一个BaseResBundle来存放公共资源。