如果你对本文感兴趣,也许你对我的公众号也会有兴趣,可扫下方二维码或搜索公众微信号:mxszgg
本文由玉刚说写作平台提供写作赞助,版权归玉刚说微信公众号所有原作者:joker
版权声明:未经玉刚说许可,不得以任何形式转载
本文插件基于 Android Gradle Plugin 3.0.1 版本
前言
日常开发中,为了避免运行时 R 文件反射失败,一般在混淆的时候都会将 R 文件 keep 住,但是因此也会导致包体积有一定的上升,那么有没有减少 R 文件未混淆带来的体积增长呢?
众所周知,Android 中的 R 文件用来存储资源的映射值,而往往一个 apk 中的 R 文件中的字段值的数量是十分庞大的,笔者将一个未进行任何操作的 debug apk 进行解压后发现该文件行数就已超过 1800 行——
为了减少包体积,可以将混淆压缩的话,可以降到500行+左右——
但是混淆后就会存在两个问题——R 文件被混淆了之后,那么资源反射就不能使用了;混淆过程中删除了除 R$styleable.class
以外的其他的 R$*.class
,但是 R$styleable.class
仍然是可以优化的。那么该如何解决这两个问题呢?一个方案是不开启混淆,这显然不太现实;另一个方案是开启混淆,同时在 proguard-rules.pro
中 keep 住 R 文件,再手动删除 R 文件中的字段信息。实际上美丽说团队早期已经开源过一个 thinrPlugin 就是使用方案二,但其针对的是 Gradle plugin 1.0/2.0 的版本,并未对 3.0 做支持,本文将重写该插件,并命名为 thinr3。
写插件前笔者谈及一点前提知识以及插件思路——为什么 R 文件中的字段可以删除?R 文件中的字段分为两种类型,一种是 static final int
,另一种是 static final int[]
。其中,static final int
作为常量在运行时是不会被改变的,那么将这些常量打进 apk 中很明显是多余的,所以实际上打包进 apk 的 R 文件有很大一部分是冗余的。
例如上图中的 R.layout.activity 是完全可以被其所对应的常量替换的,但是由于 keep 住了 R 文件,所以它不会进行替换。
thinr3 的思想就是找到使用 R 字段的地方,如果该字段是常量则将其替换成这个常量所对应的值,并删除 R 文件中该字段,去除冗余——
插件的运行需要对 .class 文件分两次遍历,第一次遍历先是获取到 R.class
文件,遍历其中所有的常量值,封装成键值对用以后面将字段替换成相应的常量;第二次遍历分两种情况,如果是 R 文件那么则将其常量进行删除,如果是其他 .class 文件,则将当中的 R 字段引用根据前面的键值对进行替换。为了对 .class 文件进行操作,需要引入 ASM,不了解 ASM 没有关系,本文阐述的插件中运用到 ASM 的部分不多。
那么由此可知,何时何地获取 .class 文件实际上就是整个 task 的核心所在——理论上越靠后 .class 文件将会被修改的风险就越小,笔者选择了在混淆 task (transformClassesAndResourcesWithProguardFor${variant.name.capitalize()})执行之后启用