口红 5年磨一剑|优酷Android包瘦身治理思路全解( 十 )


这里划分了14项整包瘦身技术 , 其中Android官方没有提供的能力 , 都已经沉淀到了优酷自研gradle plugin中 , 目前正在开源筹备中 。
【代码】Proguard/R8 。 利用Proguard工具 , 对java代码进行裁剪、混淆、优化处理 , 从而实现无用代码删除、符号(类、变量、方法)混淆、代码逻辑优化 , 包体积降低效果非常显著 。 值得注意的是 , google官方已经在近几年的Android Gradle Plugin中 , 使用自研的R8替代了java领域传统的Proguard工具 , 裁剪和优化效果更为强大 , 进一步压缩包大小的同时处理耗时更低 , 优酷从proguard切换到R8后 , 包大小降低约4.8%(3.2MB) , 构建耗时降低约25%(2min) , 当然这也和原progaurd的全局配置有关 , 尤其是优化次数-optimizationpasses 。【代码】D8 。 在apk构建过程中 , java代码需要经历由jvm字节码到dalvik字节码的转换处理 , DX/D8就是承担这个责任的工具 。 在优酷的具体实践中 , 由DX升级到D8后 , 包大小降低约9.5%(9.7MB) , 由于额外对dex合并进行了优化 , dex数量降低 , 导致包大小收益出现一次跃升 , 因此比官方给出的Benchmark收益5%要更高 。【代码】R类合并 。 将所有模块package.R类移除 , 并将java代码中对前者的引用统一替换为.R类 , 以此来降低包大小的一种技术手段 。 在优酷当前情况下(模块800多个 , dex24MB) , R类裁剪可以减少80万个java类Field , 带来近5MB包大小收益 。 由于每个dex中Field数量也受到65536限制 , 因此Field数量大幅减少所带来的dex数量减少 , 是瘦身收益的主要来源 。 进一步 , 可以把所有java代码中R..的引用 , 也全部替换为对应id值 , 这样.R类也可以删除 , 但是在已经完成R类合并的情况下 , 这个处理的收益比较有限 , 因此优酷并没有实际投入研发和使用 , 但是如果追求极致瘦身确实可以这么做! 【代码】Dex排布优化 。 Dex排布优化是指通过合理安排dex中包含的类 , 从而尽可能减少常量池冗余度以及dex数量 , 进而降低dex整体大小的一种瘦身技术 。 由于历史原因 , Dalvik字节码中调用method和field指令的操作数是16位 , 因此一个dex中method和field数量上限均为65536 , 而现代app一般都会包含多个dex , dex数量过多会导致各类常量池冗余度变高 , 从而导致包大小增加 。 事实上 , Dex排布优化不仅可以用于降低包大小 , 还可以通过选择不同的优化策略 , 来提升app运行时的性能 , Facebook的Redex即是这一领域的著名开源框架 。 优酷并没有使用复杂的排布优化策略 , 而是自定义了简单的Dex合并能力 , 获得了约2MB左右的包瘦身收益 。【代码】字节码指令精简 。 准确的说这并不是一项瘦身技术 , 而是一类瘦身技术的集合 。 通过更精细的字节码上下文分析 , 以删除、合并、转换等方式精简指令序列 , 从而达到瘦身目的 , 例如删除冗余赋值指令(值与类型默认值一致)、access$xxx方法消除(修改private方法为public , 避免access方法生成)、常量/短方法内联等等 。 不知道大家是不是会有个疑问 , proguard/R8没有进行这些处理吗?这些“民间”自定义的字节码指令精简方案 , 可以看作是对前者的一种“极致性”扩展 , 因为前者在进行字节码优化时对正确性的要求极高 , 如果有些优化策略存在风险 , 或者违背原代码设计意图(比如修改private方法为public) , 那么就不会应用 。 当需要使用自定义的字节码指令精简之前 , 建议先把proguard/R8各种优化配置选项研究透彻并充分应用 , 可能你会发现通过配置就可以实现同样效果 , 并且处理过程更稳定、高效、可信 , 如果不得不走到需要进行自定义处理的境地 , 也一定要谨慎使用 。【资源】无用资源裁剪 。 ShrinkResources[3

经验总结扩展阅读