看动画,轻松学习23种C++设计模式无密

#1

download:看动画,轻松学习23种C++设计模式无密

X-shrink _ r源代码分析
为什么要内联R文件?
首先说一下Android R文件的生成。众所周知对于Android开发者来说,当我们想要使用一些布局文件、drawable等资源时,可以直接使用r . id . r . drawable .等等。这个R.java文件类是在Android编译和打包过程中作为res/ directory中的一个文件创建的。R.java工具会对其中的资源进行编译和压缩,从而生成相应的资源id,并生成文件,用来保存当前的资源信息。同时,将生成resource.arsc文件来建立id及其对应的资源值。
最后,生成以下代码。

这里解释一下这个资源ID的含义:0x7F0600C9,由三部分组成:Package ID+TypeID+EntryID。0x7F0600C9可以拆解成0x7f +06 +00c9。

Package:是包的Id值。在Android中,如果是第三方应用,默认值为0x7f,如果是系统应用,默认值为0x01。对于插件,它是分配给插件的id值,占用一个字节。

TypeId:是资源的类型Id值。一般有这些类型:attr、drawable、layout、anim、raw、dimen、string、bool、style、integer、array、color、id、menu等。应用程序所有模块中资源类型的名称,按字母顺序排序后。值从1支出开始逐渐增加,顺序不可改变(每个模块下R文件的相同资源类型id值相同)。例如,如果anim=0x01占用1个字节,那么所有编译的R文件中anim的值都是0x01。

EntryId:是特定类型下资源实例的Id值,从0开始依次递增。它占据了四个单词部分。
一般情况下,APP的R文件的生成是这样结束的,但是当我们的开发是多模块模式开发时,问题就来了。模块或aar也会生成R文件,然后打包apk后的R文件格式会生成如下结构
可以,每个摇篮都有自己的R文件,上层R文件会和下层R文件资源合并。但这会带来一个问题,那就是
r越来越多的文件是冗余的,导致数据包大小增加。

上层R文件容易出现R字段过多,导致MultiDex 65536的问题。(如果miniSDK>21可以忽略)
r文件内联
实际上,Android Studio已经在编译时为我们做了内联处理。比如我们来看看APP模块的smail文件。
源代码
反编译的小代码
可以看到源代码中的R.layout.activity_main已经被替换为资源id0x7f08007e。
但是让我们来看看MoudleA反编译的smali代码。
源代码
反编译smail生成
可以看出,模块项目中的资源文件不是内联的。为什么会这样呢?这是因为编译主项目时不会再编译模块的类文件,模块的类文件原封不动的打包成apk。但是当资源id不变的时候,只在编译主项目的时候形成,但是模块生成类的时候使用了上面提到的变量,所以一直保留着。
什么是shrink_r?
x是ByteX团队的开源字节码插装工具,shrink_r是用来插入字节码的插件之一。
r文件常量内联,r文件瘦身;
垃圾资源检查;
无用的资产检查。
X.Shrink _ R是解决上述模块项目中R文件不是内联生成的问题的解决方案。它通过ASM操作类文件来替换使用R变量的常量值,然后删除R文件来减小数据包大小。
使用收入
我们来看看使用前后的效果和收益对比。
使用前
数据包大小
r文件数量
使用后
数据包大小
r文件数量
删除Moudle的r文件,然后内联替换模块项目的资源id。
源代码分析
Traverse()方法确定如果是R文件,进入R类文件分析类分析器,主要用于保存需要替换的R资源的id。有三种方法。
VisitField访问变量主要做两件事。
保存要替换的资源id。
保存不需要替换的资源id。
transform()-第二次遍历,字节码转换
就是在这种方法中,R变量的常数值被替换。这个方法做两件事。
删除白名单外的r文件
替换r类变量
为ShrinkRClassVisitor类中的每个方法创建方法,然后在方法中使用R类变量的地方替换常数值。
VisitMethod()访问方法,在方法中使用R型变量的地方进行常量值替换处理。所有替换都在Replacerfield access method visitor中处理。

此时,资源id被替换为一个常数,这就结束了。
摘要
总的过程如下
第一次遍历Traverse类,获取所有要替换的R文件类变量的常数。
第二次遍历所有类,将所有需要替换的R类变量替换为常量,删除R文件。