TL;DR
插件名:shrinker
项目地址: https://github.com/yrom/shrinker(其实很早之前就已经发布到github上了,不过无人问津→_→)
插件效果:与removeUnusedCode
同用可以起到最佳效果
这里有一个简单的测试项目,大部分类来自于依赖的support库,结果如下:
选项 | methods | fields | classes |
---|
原始项目 | 22164 | 14367 | 2563 |
应用shrinker 插件 | 21979 | 7805 | 2392 |
应用shrinker 并开启 removeUnusedCode | 11335 | 3302 | 1274 |
如果应用于依赖众多的大型项目则效果惊人。
ps. 其实已经在 b站的APP 上使用很久了,插件稳定、可靠且无副作用。
原理
不论组件化或者说模块化,都有个核心思想:拆分,拆成一个又一个独立的Library。
拆分 Library 引入的问题
举个例子
现一个 APP,它为了实践组件/模块化,拆分出了 common-ui ,business-a, business-b… 依赖关系如下图所示:

R 文件生成的大致流程如下图:

其中processReleaseResources
实际是调用的 aapt
工具来给每个依赖的Library都生成一个最终确定的R.java
。
可想而知,第一个问题:** 拆分的Android Library越多,R 文件越多! **
然而,Library 的 R 文件只会在最终编译成 APK 时确定字段常量值,输出 aar 时只有一个R.txt用于记录声明的资源。
假设 common-ui 声明了15个公共drawable资源,则生成的 R 文件中将有 15个相关的用于记录的字段,而且每个依赖于它的上层的library 生成的R都会有这15个同名的字段,如下图:

由此可得,第二个问题:** 越底层的依赖所声明资源越多,最终生成的 R 文件越庞大 ! ** 因为这些字段没有得到有效内联,最终生成的DEX字段数就会严重超标。
为了解决组件/模块化进程中出现的上述两个问题,shrinker
应运而生。