目录

1. Wafers 项目背景

2. 效果展示

3. 实现方案

4. 改造期间遇到的问题

5. 如何接入使用

6. 对比 Instant Run 和 Apply Changes

7. 总结

1. Wafers项目背景

Wafers [ˈweɪfəz] 威化饼。以甜品命名更贴近生活,贴近 Google 的命名风格。同时威化饼象征着一种分层设计,可拆装的动态化设计理念。

随着 58 App 融合的功能越来越多,包体积越来越大,带来如下问题:

  1. 开发期间的编译速度比较慢;
  2. 厂商限制内置包的大小,厂商基础包的改造成本越来越大;
  3. 推广转换率下降。

Wafers 项目正是在这个大背景下产生的,整个项目分为 2 个里程碑:

里程碑 

 进度

基于 Android App Bundle (AAB) 改造所有业务线 lib 库,提升开发期的编译速度

已完成

基于一期改造成果,实现业务模块动态下载运行

进行中

目前一期已顺利完成,产出了一种全新的开发模式。这种开发模式是业内首创,除了大幅提升了开发期的编译速度外,还具有以下优点:

  1. 无侵入性。通过开发期增加工程壳,使用自研插件修改字节码适配 AAB (即使用 Wafers 还有另一个使用方向:无侵入地将旧有工程改造成 AAB 模式);
  2. 改造成本低。只在开发期间生效,不影响线上包。同时保留原有的开发模式,支持两种开发模式的自由切换;
  3. 迎接大势。目前 Google Play 已全面支持 AAB 格式,国内市场未来两年也将逐步迎上。

为此我们将其开源,贡献给更多的业务线,更多的团队!

2. 效果展示

目前 Wafers 已经应用在58同城 APP 中,我们将主业务,房产,黄页,拼车,招聘,二手,金融这几个业务线改造成 AAB 开发模式。同时,我们保留了之前的开发模式,支持一键切换。

  • 首次编译:第一次编译,AAB 模式下会构建 base APK 和 dynamic feature APKs。每次编译前执行 gradle clean task,并且释放 Android Studio 内存。
  • 增量编译:对开发的 lib 改动一行代码,AAB 模式下只会构建当前业务线的 dynamic feature APK,base APK 无需重新构建。

2.1 整体报告

  • 首次编译的速度根据依赖方式的不同,分别有 32% 和 33% 的提升;
  • 增量编译的速度根据依赖方式的不同,分别有 67% 和 70% 的提升;
  • 库越小速度提升越明显,因为在 debug 开发期间,会向 AAB 工程壳注入插件,修改字节码,以达到无侵入的 AAB 开发模式切换。所以库越小,修改的时间越短;
  • 所有业务线都打开 AAB 方式比只打开开发的业务线 AAB 方式更快一些,根据官方文档说明:AAB 采用了并行构建,以前的模式是串行构建一个大的 APK,现在的模式是并行构建多个 APK。

2.2 详细报告

  • OS     内存:Macbook pro 16 GB 1600 MHz DDR3   处理器:2.2 GHz Intel Core i7
  • IDE     Android studio 3.5
  • 取样   每个场景执行 3 次,取平均值

开发主业务

开发方式 

首次编译 

 增量编译

 对比全 library 依赖提速

全 library 依赖

3m 20s

2m 45s

-

主业务 AAB 打开,其他业务线 AAB 关闭

2m 27s

1m 8s

首次编译 26.5%, 增量编译 60%

主业务 AAB 与其他业务线 AAB 都打开

2m 23s

1m 3s

首次编译 28.5%, 增量编译 62%

开发房产业务

开发方式 

首次编译 

 增量编译

 对比全 library 依赖提速

全 library 依赖

3m 20s

2m 45s

-

房产业务 AAB 打开,其他业务线 AAB 关闭

2m 16s

1m 3s

首次编译 32%, 增量编译 62%

房产业务 AAB 与其他业务线 AAB 都打开

2m 14s

1m 6s

首次编译 33%, 增量编译 61%

开发黄页业务

开发方式 

首次编译 

 增量编译

 对比全 library 依赖提速

全 library 依赖

3m 20s

2m 45s

-

黄页业务 AAB 打开,其他业务线 AAB 关闭

2m 9s

43s

首次编译 35%, 增量编译 73%

黄页业务 AAB 与其他业务线 AAB 都打开

1m 48s

42s

首次编译 46%, 增量编译 73.5%

开发拼车业务

开发方式 

首次编译 

 增量编译

 对比全 library 依赖提速

全 library 依赖

3m 20s

2m 45s

-

拼车业务 AAB 打开,其他业务线 AAB 关闭

2m 15s

48s

首次编译 32.5%, 增量编译 70%

拼车业务 AAB 与其他业务线 AAB 都打开

2m 20s

43s

首次编译 30%, 增量编译 73%

开发招聘业务

开发方式 

首次编译 

 增量编译

 对比全 library 依赖提速

全 library 依赖

3m 20s

2m 45s

-

招聘业务 AAB 打开,其他业务线 AAB 关闭

2m 14s

59s

首次编译 33%, 增量编译 64%

招聘业务 AAB 与其他业务线 AAB 都打开

2m 20s

47s

首次编译 30%, 增量编译 71%

开发二手业务

开发方式 

首次编译 

 增量编译

 对比全 library 依赖提速

全 library 依赖

3m 20s

2m 45s

-

二手业务 AAB 打开,其他业务线 AAB 关闭

2m 15s

46s

首次编译 32.5%, 增量编译 72%

二手业务 AAB 与其他业务线 AAB 都打开

2m 25s

49s

首次编译 27.5%, 增量编译 70%

开发金融业务

开发方式 

首次编译 

 增量编译

 对比全 library 依赖提速

全 library 依赖

3m 20s

2m 45s

-

金融业务 AAB 打开,其他业务线 AAB 关闭

2m 14s

48s

首次编译 33%, 增量编译 70%

金融业务 AAB 与其他业务线 AAB 都打开

2m 10s

42s

首次编译 35%, 增量编译 74.5%

3. 实现方案

3.1 AAB 简介

Android App Bundle 是 Android 新推出的一种官方发布格式 (.aab)。通过使用 Android App Bundle 你可以减少应用的包大小,从而提升安装成功率并减少卸载量。

Google Play 就是基于对 aab 文件处理,将 App Bundle 在资源维度,ABI 维度和 Language 等维度进行了拆分。你只要按需组装你的 APK 然后安装即可。如果你的手机是一个 x86,xhdpi 的手机,你在 Google Play 应用市场下载 APK 时,Google Play 会获取手机的信息,然后根据 App Bundle 会帮你拼装好一个 APK,这个 APK 的资源只有 xhdpi 的,so 库只有 x86,其他无关的都会剔除,从而减少了 APK 的大小。

3.2 我们的奇思妙想

看完 AAB 的简介,丝毫没有看到提升编译速度的点,不过 Google 在 AAB 的特点中提到了一点:

缩短构建时间:构建系统(例如使用 Gradle 的 Android Studio 构建系统)针对组织为模块的项目进行了优化。 例如,如果您在具有多核处理器的工作站上启用 Gradle 的并行项目执行优化,则构建系统能够并行构建多个模块,并显着减少构建时间。项目的模块化程度越高,构建性能的提高就越显着。

这一点从上面的数据报告中也可以得到结论,我们将所有业务线都改成了 AAB 模式后,即使是全量构建(所有的 APK 都需要重新构建一遍),编译速度也提升了 30%,这就是得益于 AAB 支持的并行构建特性。但是这一点,还不足以令我们兴奋。AAB 可以构建出一个 base APK 和多个 dynamic feature APK,最后使用 adb install-multiple 命令安装到手机上。

这一点让我们想到了:

58 APP 业务线众多,每个业务线在开发时,大部分需求都是在自己的业务线代码中进行开发。如果我们将所有业务线都改造成AAB 模式,除了可以利用并行构建的优势,还可以在业务线开发时,只构建它自身的 APK。base APK 和其他业务线的 APK 在全量构建后,如果没有修改,后续本地开发无需重新构建。最后直接使用 adb install-multiple 命令一起安装即可,这将大幅减少开发期的编译速度。

3.3 设计方案

有了这个振奋人心的发现,我们小组几个人立马开始设计改造方案,58 APP 的原有的架构图如下:

可以看到,工程结构非常复杂,为此我们定下了几个目标:

  1. 降低改造影响范围,不能影响全量正式包;
  2. 降低改造成本,实现无侵入地改造;
  3. 提高体验,保留之前旧的开发模式,支持快速切换。

经过各种测试,实验,我们最终落地的工程结构图如下:

我们在本地调试期间打开 AAB 开关,对需要被改造的 lib 库加上一个 dynamic feature 工程壳,这层壳用来打出业务 APK,内部的依赖是这个待改造的业务库 aar/源码。这样业务线在没有某个 dynamic feature 库的代码时,也可以通过它们的工程壳打出对应的 APK 文件。

3.4 打包流程

本地调试期间,当 dynamic feature 工程壳编译时会注入自研插件,修改字节码文件,实现无侵入地修改。而 release 全量包则会关闭 AAB 开关,工程结构会和改造之前一模一样,从而不影响全量正式包。

Debug 打包:

Release 打包:

4. 改造期间遇到的问题

虽然最终的方案很完美,但是在改造期间还是遇到了很多问题,这边简单地描述一下:

4.1 关于 AAB 的拆分规则

官方并没有给出 dynamic feature 的拆分规则,为此我们做了一个测试,有如下几个场景:

场景1:

lib1 是否会添加到 dynamic feature APK A 中?

答案:lib1 会添加到 dynamic feature APK 中。

场景2:

构建出 dynamic feature APK A 与 dynamic feature APK B 的时候 lib1 的代码会在哪一个库中呢?都存在? 还是在 base APK 中?

答案:Dynamic feature APK 不能打包相同的 library 依赖,需要将改 library 放到 base APK 中。

场景3:

base APK 中包含 app lib1 与 common 库,dynamic feature APK 只包含自己的库的代码和资源。

规则总结:

  • 与 app 库有直接或间接依赖的都会打包到 base APK 中;
  • Dynamic feature moudle 直接或间接依赖的库而不被 app 依赖的库会打到 dynamic feature APK 中;
  • Dynamic feature moudle 间不能同时依赖相同的库,需要将其放到 base APK 中;
  • 只有 dynamic feature moudle 可以依赖 app 库。

以上几点结论,为我们开发设计 Wafers 一期,奠定了重要的基础。

4.2 Dynamic feature R 文件规则

我们开发的插件,解决的主要问题就是 R 文件。之前旧的开发模式,最终所有的库会打包到一个 APK 文件中,所以很多资源访问的问题不会存在,或者说暴露出来,包括开发者引用错误的 ID。

首先我们调研了下 dynamic feature R 文件的引用规则:

  • Dynamic feature moudle 如果引用 base moudle 的 R 文件会自动替换为数值;
  • 不是 dynamic feature 而被打入 dynamic feature APK 中的库,依然是 pkgName.R.id 的形式存在,不会被替换成数值。

改造过程中遇到了如下几个 R 相关的问题:

  • (1) Dynamic feature APK 中引用 base APK 的资源

解决方案:增加一个 dependences 中间库用作 R 资源收敛,使用插件排查注入包名前缀。使用 Wafers 开发模式,都需要新增一个用作 R 资源收拢的中间库。

  • (2) Dynamic feature APK 中反射获取资源

解决方案:插件中拦截反射资源函数,使用代理 Resources 方式替换:优先从自身库查找 (pkg 需要加上 .featureName),找不到再从 base APK 中查找。

  • (3) Dynamic feature APK 中和 base APK 中有同名 id,但编码时错误引用了 base APK 中的 id 值 

解决方案:插件拦截 findViewById 函数,替换成自定义实现的 findViewById:针对 Activity、View、Dialog、Window 分别从当前库,base APK 查找,最后使用反射方式处理。

  • (4) Base APK 中使用了 id 查找,但其引用的 layout 被 dynamic feature 替换了

解决方案:通过插件修改 base APK,加白名单列表,修改指定的类。

4.3 国产 VIVO, OPPO 不支持 adb install-multiple

adb install-multiple 在国产的 VIVO, OPPO 手机上会安装失败,接入方可以参考 demo 关闭所有 AAB 开关,在 Wafers 二期我们将把 dynamic feature APK 内置到 assets 解决此问题。

5.如何接入使用

Wafers 目前仅在 58 内部进行了开源,后期会逐步向外界开放。58 的同学如果有兴趣对自身的项目做改造,可以在公司 gitlab 仓库中搜索 Wafers, 里面有详细的接入文档,使用文档和 demo。在改造期间遇到问题,可以在 Wafers gitlab 的 issues 反馈问题,我们会及时进行跟进处理, 同时也欢迎提交 MergeRequest,为Wafers 贡献一份力量。

6. 对比 Instant Run 和 Apply Changes

6.1 Instant Run

Instant Run, 是 Android Studio2.0 以后新增的一个运行机制,能够显著减少第二次及以后的构建和部署时间。简单通俗的解释就是,当你在 Android Studio 中改了你的代码,Instant Run 可以很快的让你看到你修改的效果。而在没有 Instant Run 之前,你的一个小小的修改,都肯能需要几十秒甚至更长的等待才能看到修改后的效果。

Instant Run 主要解决以下两个问题:

  • 减少构建和部署 app 到手机的时间;
  • 热更新代码改动,无需重启 app 或者 activity。

为了实现这两个目标,Instant Run 通过重写 APK 的构建流程往每个类里面去注入钩子来达到类的热替换。

Instant Run 这个技术是基于 Transfrom-API 技术,但是 Android Studio 3.5 Instant Run 就被废弃了,主要是 Instant Run 诟病太多,对于小型的应用,Instant Run 确实很好用,能够节省构建和部署的时间,并且不会出错。但是对于大型复杂应用,它会导致更长的构建时间,同时由于 Instant Run 构建过程和正常的 app 构建存在冲突,常常出现另开发者抓狂的错误,如:

  • 偶尔变更代码没生效;
  • 容易卡开发机器;
  • 各种莫名其妙的 crash 和 bug.

6.2 Apply Changes

Android Studio 3.5 引入了 Apply Changes,它取代了旧的 Instant Run。Instant Run 是为了更容易地对应用程序进行小的更改并测试它们,但它会产生一些问题。为了解决这一问题,Google 已经彻底删除了 Instant Run,并从根本上构建了 Apply Changes ,不再在构建过程中修改 APK,而是使用运行时工具动态地重新定义类,它应该比立刻运行更可靠和更快。

和 InstantRun 不同的是,它不会在构建过程中去修改 APK。它依赖的是 Android 8.0 开始虚拟机支持的特殊指令 (JVMTI) 来进行类的替换。ART 在 8.0 上才初步实现此接口,9.0上 基本成型。事实上,Android Studio 的 profiler 也依赖这个接口,用来分析应用程序的堆内存/CPU/GC 等关键参数。

当连接上 Android 8.0 及以上的设备时,Android Studio 将会多出三个按钮,Apply Changes 部署资源和代码改动到手机,重启 activity,但是无需重启应用。仅部署代码改动到手机,不需要重启 activity 和应用。

想使用 Apply Changes 有两个条件:

  • APK 必须是 debug 包;
  • 必须在 Android 8.0 以上的手机上运行。

6.3 对比

抛开 Instant Run, Wafers 在快速构建上和 Apply Changes 的对比:

优点:

  • 没有 Android 8.0 的限制;
  • 继承了 AAB 自身的优势;
  • 复杂的工程结构下,在 58 App 中的体验结果是:Wafers 速度比 Apply Changes 快 20%;
  • Apply Changes 在实际体验中,操作按钮经常莫名地会变灰 (不可操作状态),且经常代码不生效,而 Wafers 不存在这种问题。

缺点:

  • 不支持 OPPO/VIVO 手机;
  • 会重启 app.

快速构建是 Wafers 的一个优点,但不是唯一的优点,Wafers 支持在不侵入原始代码的情况下,将旧有项目改造成 AAB 模式,同时为后面动态安装加载提供基础。

7.总结

Wafers 是种全新的开发模式,在大型,复杂,多模块的项目中,可以有很大的发挥空间。

同时改造成本较低,只要遵循 Google 的 AAB 文档进行改造,再应用 Wafers 的插件,即可实现少量代码快速无侵入的模式切换。但是 AAB 本身有一些限制,如 dynamic feature 库之间不能互相依赖,dynamic feature 的 manifest 中使用的资源需要提前放在 base APK 中 (因为会提前进行 manifest 合并) 等,所以接入改造的项目可以根据自身业务的复杂度选择性地修改某些模块。每个开发模式都有它需要遵循的规则,AAB 是 Google 在模块化,动态化领域更深入,更彻底的一个产物。也是大势所趋,所以朝前走肯定需要顺应一些规则,正如当年的我们从 Eclipse 切换到 Android Studio,从 Ant 切换到 Gradle,从 Java 切换到 Kotlin。

Wafers 2 期,我们将实现动态包的产出,如各种厂商包,业务推广包,这将大大减少包大小。同时可以脱离Google Play 的束缚,在国内的应用市场实现 AAB 模式的动态包下载和装载,也为迎接 AAB 模式在国内应用市场的普及做好准备。

参考文献

https://developer.android.com/platform/technology/app-bundle

https://zhuanlan.zhihu.com/p/86995941

分享篇 - 58同城基于Android APP Bundle开发的全新编译模式(编译速度提升70%)相关推荐

  1. Android App Bundle:动态功能模块

    目录 Android App Bundle 创建动态功能模块 动态功能模块 与主模块建立关联 部署应用 按需分发On-Demand 免安装分发 自 2021 年 8 月起,Google Play 将开 ...

  2. 探索 Android App Bundle

    原文链接 Exploring the Android App Bundle,作者 Joe Birch. 目录 App bundle 格式 拆分 APKs 构建和分发 Android App Bundl ...

  3. Android App Bundle 和Unity AAB BundleTools

    1.为什么要用Android App Bundle 从 2021年8月起,新应用需要使用 Android App Bundle 才能在 Google Play 中发布.现在,Play Feature ...

  4. 将AAB(Android App Bundle)转换为APK

    Google Play 在2021年8月之后,对于新上架的APP,只接受AAB格式,不再支持上传APK.但是打包后,我们肯定还需要对正式发布的包进行测试,去Google Play上发测试版本非常麻烦. ...

  5. 简单聊一聊 Android App Bundle 的话题

    这一篇主要是有感而发,抽空和大家聊聊关于最近 Google Play 上传应用需要更改为 AAB 的话题,顺便说下更改为 AAB 的一些简易流程. 关于 AAB 的话题 如下图所示,最近在收到了不a少 ...

  6. Android App Bundle 自动打包原理

    Google推出Android App Bundle 已经有一段时间了. 根据Google的政策说明 ,预计2021年8月之后,新发布的应用都必须使用Android App Bundle(aab)来上 ...

  7. Android App Bundle混淆加密加壳加固保护的解决方案(过Google App上架审核)

    Android AAB简介和AAB包格式 AAB即Android App Bundle,是Google官方发布的一种新的App包格式,可以有效缩减App大小,提升用户安装和更新App的体验.在Goog ...

  8. Android App Bundle基础入门

    一.前言 重要提示:从 2021 年 8 月起,新应用需要使用 Android App Bundle 才能在 Google Play 中发布.现在,Play Feature Delivery 或 Pl ...

  9. 怎么从Android App Bundle (.aab)提取和转换apks文件(从AAB到APKs的转换和提取)

    在Google的I/O 2018上引入了一个新的APP的发布格式,就是 Android App Bundle. 在 2019年之后,Google开始推荐开发者上传APP或者更新APP使用 .aab 格 ...

最新文章

  1. 我国智能家居行业运行现状分析 标准割裂市场
  2. 【罗马数字转整数】算法优化笔记
  3. 大数据WEB阶段(十四)JavaEE开发模式
  4. 电脑老是提示虚拟内存不足的原因及解决办法
  5. java程序设计图形题_面向对象与Java程序设计基础题目:设计一个程序可以一计算平面图形的面积和立体图形的体积。1.使用interface关键...
  6. 使用tcgetattr函数与tcsetattr函数控制终端-转
  7. 关于e的等式及相关证明
  8. centos 7 8安装quaartus 遇到的问题及解决方法
  9. USB加密狗复制工具
  10. 【谷歌翻译】【CVPR15】Saliency-Aware Geodesic Video Object Segmentation
  11. 去年我国出生率跌破1%,有什么影响?
  12. java 独木桥_Java实现洛谷 P1007独木桥
  13. 11月全球浏览器份额之争:Chrome与Firefox均被蚕食
  14. vue 移动端实现上拉加载更多
  15. 孕妇电子计算机房辐射,孕妇在机房辐射大吗
  16. 和菜头-和腾讯新闻哥谈谈腾讯价值观
  17. 浅谈企业转型过程中的问题与对策
  18. cocos creator粒子不变色_Cocos Creator 3D 粒子系统初战: 不要钱的酷炫火焰拿走不谢!...
  19. leetcode 大礼包
  20. 基于wifi面向android的远程视频监控客户端开发,基于Wifi面向Android的远程视频监控客户端开发...

热门文章

  1. 转换矩阵、平移矩阵、旋转矩阵关系以及python实现旋转矩阵、四元数、欧拉角之间转换
  2. 74HC595驱动(并转串,fpga与时钟匹配,fpga与外部芯片的连接注意事项)
  3. 网络抓包wireshark---转载
  4. 微型项目实践(6):Business层代码分析——实体类的生成策略
  5. CSS简单动画---自用展开区域动画(箭头旋转,区域拉伸)
  6. 各式各样的极品程序员
  7. Linux ping不通
  8. Android中的设计模式-桥梁模式
  9. 【企业信息化】第7集 免费开源ERP: Odoo 16 Maintenance设备与维护保养管理
  10. 2021-02-12 大数据课程笔记 day23