关于java:百度APP-Android包体积优化实践一总览

41次阅读

共计 7185 个字符,预计需要花费 18 分钟才能阅读完成。

01 前言

此前百度 APP 曾经具备根本的包体积优化机制、束缚和意识,但作为巨舰型 APP,业务的高速迭代依然不可避免地造成了包体积爆炸式增长。包体积或间接或间接地影响着下载转化率、安装时间、运行内存、磁盘空间等重要指标,所以投入精力排除积弊、挖掘更深层次的体积优化项是十分必要的。

依据谷歌商店的外部数据,APK 体积每缩小 10M,均匀可减少~1.5% 的下载转化率,如下图所示:

<p align=center> 图 1 谷歌商店利用转化率减少幅度 / 10M [1]</p>

Android 包体积优化伎俩有很多,比方业务裁剪、插件化、混合开发、资源后下发等。本系列文章次要针对的是业务无关、集成在 APK 中的内容的体积优化,如 Dex 优化、资源优化、so 优化等,咱们称之为根底机制优化。

包体积根底机制优化实际将会以系列文章的形式出现,次要包含以下局部:心路历程、Dex 行号优化残缺计划、资源优化实际与摸索、Dex 优化实际与摸索、so 优化摸索、其余优化教训与总结。

本文讲述的是百度 APP 包体积根底机制优化心路历程,包含起继续指导作用的根本思维、优化对象剖析、对现有优化工具的学习、以及最终产出的体积优化项。

02 根本思维

2.1 分而治之

咱们的优化对象不只是 APK 这个最终产物,也包含 APK 中的内容,这些内容的体积优化思路与伎俩不尽相同。

2.2 可继续优化

好的优化机制不止失效于当下,也失效于将来。举例来说,从源码仓库删除以后的 Dead Code 属于一次性存量优化操作,而编译器的 DCE 机制 (Dead Code Elimination) 可继续失效于将来产生的 Dead Code。从长线思考,咱们应优先建设后者,而后倒推前者的执行。

2.3 站在前人的肩膀上

包体积优化并不是一个陈腐的话题,Android 官网和开发者们都在继续致力于优化体积。反复造轮子是不被提倡的,但对于不同的利用场景,尤其是巨舰型 APP,体积优化应该有定制化的计划。

2.4 明确代价,有所取舍

依据热力学第一定律,收益不会凭空产生,肯定会随同着代价,例如人力的投入、编译工夫的减少、适配难度的减少等。明确代价后,咱们能力决定某优化项是否要做、何时做、如何做。

2.5 束缚与意识

除了自动化的优化机制,还须要配套有自动化的体积增长束缚,同时从源头晋升开发者的体积优化意识,多管齐下能力达到最优成果。

03 APK 构造剖析

接下来咱们会简略剖析下 APK 内各组成部分,以及 APK 作为 ZIP,其规范构造是什么样的。

3.1 APK 内容分析

<p align=center> 图 2 APK 构造 </p>

  • classes.dex
    APK 中可能蕴含一个或多个 classes.dex 文件,应用程序内的 Java/Kotlin 源码最终会以 dalvik 字节码的形式存在于 classes.dex 文件中。
  • resources.arsc
    该文件是蕴含配置信息的资源查问表,起着链接代码与资源的作用。Dex 文件中的 R.class 仅蕴含资源 id,AssetManager 会利用 id 到 arsc 表中查问与以后设施信息最匹配的资源文件门路(或资源内容)。
  • res/
    蕴含源码工程中 res 目录下除了 values 外的资源文件,这些文件门路同时会体现在 resources.arsc 中。
  • lib/
    native libraries。即源码工程 jni 目录下的 so 文件,二级目录必须为 NDK 反对的 ABI。
  • assets/
    与 res/ 资源目录不同,assets/ 下的资源文件不会在 resources.arsc 中生成查问条目,且 assets/ 下的资源目录可齐全自定义,业务代码获取 assets 资源和 res 资源的形式也齐全不同。
  • META-INF/
    利用签名信息。该目录在利用签名后生成,蕴含以下三个文件:
    MANIFEST.MF:摘要文件,蕴含 APK 内所有文件的门路及其 SHA1/SHA256 值。
    CERT.SF:对摘要的签名文件,蕴含 APK 内所有文件的门路,及其在 MANIFEST.MF 中对应信息的 SHA1/SHA256 值。
    CERT.RSA:保留公钥、加密算法及其私钥加密后的内容。
  • AndroidManifest.xml
    利用清单文件,用于形容利用根本信息,次要包含利用包名、利用 id、利用组件、所需权限、设施兼容性等。

3.2 ZIP 构造剖析

<p align=center> 图 3 ZIP 规范构造示意图 </p>

  • 压缩源文件信息
    Local file header:形容源文件信息。
    File data:源文件数据。
    Data descriptor:校验码及压缩前后大小。
  • 核心目录区
    Central directory
    记录 ZIP 目录构造。每一条 file header 对应一个源文件,形容文件相干信息。
  • 核心目录完结标识
    End of central directory record
    标识 ZIP 包完结,蕴含 ZIP 包及核心目录的简要信息。

04 现有优化工具介绍

对于倒退初期的利用,体积优化的优先级较低,间接应用以下体积优化工具是性价比最高的抉择。百度 APP 同样比照借鉴了以下工具,从中衍生出了全新的、定制化的优化需要。

4.1 ProGuard

在 AGP3.3 之前,ProGuard 作为官网体积优化工具,负责在编译实现之后对 class 文件进行缩减混同等操作,其优化后果交给 Dx/D8 转化为 Dex 产物。

<p align=center> 图 4 Proguard 解决对象及作用示意图 [9]</p>

ProGuard 的优化操作次要包含:

缩减:平安移除无用类、办法、字段和属性。

混同:缩短类与成员的名称。

优化:指令级别的优化,合并反复指令、清理无用指令、晋升指令执行效率。

4.2 R8

AGP 3.3 之后官网开始举荐应用 R8,R8 与 ProGuard 不只是简略的代替关系,它还将脱糖、D8 整合到了一起,极大的晋升了构建效率。

图 5 R8 解决对象及作用示意图 [9]

R8 根本兼容此前的 ProGuard 规定,但仍存在些许差别(applymapping、行号解决、Kotlin 元数据处理、无用断定等)。R8 不再高优思考兼容性问题后,两者会派生出越来越多的不同点,倡议定期关注,博采众长。

丨 Jack & Jill

小插曲:官网在 2015 年推广过一段时间的 Jack & Jill 工具,它甚至把 javac 也囊括了进来,算是真正实现了端到端的编译。但 Jack 的性能与生态相比 javac 切实差距太大,官网出于老本思考最初还是弃坑了。

4.3 AndResGuard

AndResGuard 是微信推出资源优化工具。它的根本思维相似于 ProGuard 中的混同,体积优化是它的附加收益,同时还提供了压缩、加密等选项。

4.4 ByteX

ByteX 是字节开源的一套 Java 字节码插桩工具,目前次要包含优化与查看工作,其中一些子项最终会带来体积收益。包含 R 类内联、移除 debug 信息、access 办法内联等。

4.5 Booster

Booster 是滴滴开源的一套品质优化框架,其中包含体积优化专项,例如资源文件压缩、资源产物.ap\_ 压缩、去冗余资源、R 类内联、DataBinding BR 内联等。

4.6 AGP

Android Gradle Plugin(AGP)蕴含了多个体积优化工作,提供了许多优化配置项,大部分工作曾经作为 APK 打包的标配。


一般来讲,咱们的优化工作会依赖于这些工作的执行。如果定制的优化无奈兼容现存工作,则须要敞开或 hook 这些工作。接下来将依照编译程序简略介绍几个优化工作与配置:

  • OptimizeResources
    AGP4.2+ 新增的资源优化工作,目前只实现了资源文件门路的缩短,默认开启,可通过 android.enableResourceOptimizations 敞开。
  • StripSymbols
    NDK 会利用 llvm-strip 移除掉 native libraries 中的 unneeded symbols,这部分优化工作也能够放在 so 编译期间实现。
  • MinifyWithR8/ProGuard
    利用 R8 或 ProGuard 实现代码优化,此处就不再赘述了。
  • ShrinkResources
    由 ShrinkResources 开关管制,启用前提是必须开启 minifyEnable。其作用是将未被援用的资源文件替换为一个体积很小的格式文件(仍存在占位体积,同时保留了该资源条目,所以 resources.arsc 体积并不会缩小),可通过 res/raw/keep.xml 文件配置 shrinkMode 和白名单。
  • PackageOptions
    打包时选项,包含过滤 exclude、雷同文件仅打包 pickFirst、全副打包 merge、so 优化豁免 doNotStrip。
  • Splits
    分包 / 过滤策略,配置项包含 ABI、资源配置(语言、分辨率等)。

05 百度 APP 优化项项概览

5.1 Dex 优化

百度 APP 实现 Dex 的体积优化项能够分为两类:源码编译期间的优化;APK 打包期间对 Dex 文件的优化。两者的区别次要是优化对象不同,所以基于不同的优化工具实现,前者基于 Java 字节码工具实现(如 ASM),后者基于 Dex 字节码工具实现(如 Titan-Dex [10])。

丨 Titan-Dex

Titan-Dex 是百度开源的面向 Android Dalvik(ART)字节码 (bytecode) 格局的操纵框架,能够在二进制格局下实现批改已有的类,或者动静生成新的类。百度 Titan-hotfix 工具即基于此框架实现。

  • R 类优化

工程组件越多,R 类所占体积越大,未敞开资源依赖传递的状况下则更重大。咱们在编译期将代码中调用 R.type.name 的中央全副替换成了对应的 id 常量,最终 R.class 会作为无用类被 R8/ProGuard 清理掉。

  • 行号优化

Dex 中的 debug 区域占 5~10% 的大小,但其最大的作用是剖析解体堆栈时定位。该区域能够通过去除 ProGuard 规定 -keepattributes SourceFile,LineNumberTable 齐全移除。咱们抉择在指令级别实现 debug infos 的映射与复用,同时联动百度性能平台 (目前仅供公司外部应用,性能可类比腾讯 bugly) 实现解体堆栈的还原,既优化了体积,又不会影响堆栈的剖析。

  • 注解优化

Dex 中注解分为三种类型:Build、Runtime、System。Build 和 Runtime 对应 ProGuard 规定 -keepattributes *Annotation*_,可优化的 System 注解依据具体类型别离对应 -keepattributes InnerClasses, Signature, EnclosingMethod_。跟行号一样,能够通过去除这些规定实现一刀切的优化。但因为咱们接入的三方组件自带这些 ProGuard 规定,且局部类的 System 注解有保留的须要,咱们抉择后置地解决 Dex 文件,基于 Dex 字节码工具实现指标注解的移除。

5.2 资源优化

资源优化的对象分为两类,一是资源查问表 resources.arsc,局部优化操作会波及到 res/ 及 R 文件的批改,但实质都是从 resources.arsc 登程的;二是原始资源文件,包含 res/ 和 assets/。

介绍优化项前,咱们先看一张网上最经典的 resources.arsc 结构图(起源 CSDN 社区):

<p align=center> 图 6 resources.arsc 结构图 </p>

  • 资源同名化

在理论利用中,咱们默认通过资源 id 查找资源内容,对资源名的应用频率非常低,仅限于通过资源名反查资源 id 以及 通过资源 id 获取资源名两种状况。所以资源项名称字符串池所占据的空间即是咱们的优化对象。极限优化后果是,这个池子里仅寄存一个字符串,所有 ResTable\_entry 的资源项名称 index 均指向这个池子里仅有的字符串,即所有资源的名字都变得一样了。理论场景中,咱们会有豁免和降级为混同的需要,例如通过资源名反向查问资源 id 等状况。

  • 资源文件门路优化

与 AndResGuard 中的资源门路混同成果类似,都是尽可能缩短资源文件的门路长度,从而缩小 ResTable\_entry 的 value 大小。咱们将门路 Hash 值转换到碰撞起码的位数,作为最终的混同后果,其长处在于混同后果基本上是固定的,无需 applymapping。除此之外,咱们还较为激进地去掉了大部分文件的后缀名。

  • 资源配置优化

从 arsc 中的资源 id 蕴含了偏移量信息,零碎通过偏移量在 arsc 中定位资源。所以图 7 中的空白区域必须保留一个 4 字节的占位,以满足偏移量查问形式。咱们正在对此局部做优化,主旨是通过优化不必要的 configuraion,达到缩小对齐占位的目标。

<p align=center> 图 7 resources.arsc 空白占位示意图 </p>

  • 图片压缩

因为 webp 格局受限于 minsdkversion18,咱们目前还是针对 png 图片做压缩优化,应用的工具包含 TinyPng 和 ImageOptim。除了出图阶段压缩外,也会有后置流水线做压缩查看。

  • 无用资源清理

如 4.5 中提到的,ShrinkResources 并不会真正移除未被援用的资源文件。不过咱们能够拿到被 shrink 的 resources 列表,而后再利用资源优化工具做真正的删除。

丨 New ShrinkResource

2022.1 公布的 AGPv7.1.0 更新了资源缩减性能,增加了实验性选项 android.experimental.enableNewResourceShrinker.preciseShrinking,该选项设置为 true 后 ShrinkResources 会齐全移除未应用的文件资源及 value 资源,但 arsc 中仍会存在这些资源的填充占位。

  • arsc 压缩

resources.arsc 的压缩体积收益很高,但对其进行压缩会影响启动速度和内存指标。具体起因是:零碎在加载 arsc 文件时,若 arsc 文件未压缩,可应用 mmap 进行内存映射;若 arsc 文件被压缩了,则须要将其解压缩后读取到 RAM 缓冲区,会减少内存应用,也会拖慢启动速度。在业界大都压缩 arsc 的状况下,百度 APP 出于综合考量始终未对 arsc 文件进行压缩。独一无二,官网出于同样的思考,从 Android11 开始强制要求 resources.arsc 不可压缩且放弃 4 位对齐,否则会间接装置失败。

<p align=center> 图 8 Android11 强调 resources.arsc 压缩对齐问题 </p>

5.3 ZIP 优化

  • 压缩

目前咱们应用的压缩算法有 7z 和 zopfli[11],后者压缩率和压缩耗时都有明显增加,稳定性还在验证中。采纳新的压缩算法时需特地留神两点,一是不要压缩 resources.arsc;二是留神压缩、对齐、签名操作的程序。

  • 文件门路优化

如章节 3.2 ZIP 构造剖析所示,APK 中有三处体积与文件门路长度相干:META-INF/、压缩源文件数据区的 local file header、核心目录区的 file header,资源文件门路优化成果同样会体现在这里。同理管制 assets/ 下的文件门路也可带来体积收益。

5.4 其余

  • 夜间模式优化

目前百度 APP 的夜间资源是一个 APK 包,此前实现形式是与主包中的资源名保持一致,通过反射的办法查问对应 id。现改为 id 统一,这样既防止了反射查问的耗时,章节 5.2 中的资源优化也能够利用到资源 APK,进一步减小体积。

  • 混同规定

ProGuard/R8 提供了多种多样的规定用以豁免代码优化操作,如果使用不当可能会造成体积的白白浪费。将来咱们打算制订一套具体的 ProGuard 规定应用标准,并对每个组件的 ProGuard 规定都进行校验,例如不容许呈现本组件包名范畴外的 keep 规定、不容许呈现包级别 keep 等。

  • 体积流水线

次要包含仓库体积束缚流水线,二进制组件体积查看流水线,以及 APK 组成体积剖析流水线,分阶段进行束缚与剖析。目前百度 APP 建设了 APK 体积监控流水线,每当主线有代码合入、触发编译打包后,会即时对编译产物 APK 做体积剖析,并与上一次编译产物进行比对,能够马上发现异常的体积增长。

06 总结

第五章介绍的优化项自 21 年 8 月至 22 年 2 月分批次上线,期间业务仍旧在高速迭代,尽管有体积监控流水线作用,包体积仍会不可避免地减少。因为优化机制非常底层,需进行充沛的线下测试与线上小流量灰度,验证稳定性后能力正式上线。上线 / 灰度前后百度 APP 包体积大小比照如下:

——————END——————

参考资料:

[1] 包大小与装置转化率

https://medium.com/googleplay…

[2] ZIP 格局

https://pkware.cachefly.net/w…

[3] ProGuard

https://www.guardsquare.com/p…

[4] R8

https://r8.googlesource.com/r8

[5] ProGuard 与 R8 比照

https://www.guardsquare.com/b…

[6] AndResGuard

https://github.com/shwenzhang…

[7] ByteX

https://github.com/bytedance/…

[8] Booster

https://github.com/didi/booster

[9] AGP

https://developer.android.com…

[10] Titan-Dex

https://github.com/baidu/tita…

[11] zopfli

https://en.wikipedia.org/wiki…

举荐浏览:

百度 APP iOS 端内存优化实际 - 大块内存监控计划

百家号基于 AE 的视频渲染技术摸索

百度工程师教你玩转设计模式(观察者模式)

Linux 通明大页机制在云上大规模集群实际介绍

超高效!Swagger-Yapi 的机密

百度直播 iOS SDK 平台化输入革新

正文完
 0