booster包体积优化之R内联

booster是通过把子module的R.class中定义的变量都删除掉,然后子module中的代码引用的资源变量会直接被R.txt中定义的常量值所代替。这就是R文件内联的过程

上一节通过booster-transform-toast的使用,接着介绍了booster插件的主框架,最后自己简单使用了asm替换class中方法的调用,这一节讲解booster通过R内联来减少包体积。

首先我们了解下多组件打包的时候,资源是怎么管理的,首先每个module都会有自己的R.class,分别存储了anim、animator、attr、bool等属性:
可以看到R.class中分别用静态内部类的形式保存各种资源属性,每一个属性是用一个静态常量的形式保存,其中每一个属性值是由PackageId+TypeId+EntryId组合的一个值: alt text 就拿abc_face_in这个属性值,它是0x7f010000这个值。下面来拆解下这个值:

  • PackageId:是包的id值,android中如何三方应用的话,这个值一直是0x7f,系统应用的话是0x01,它是占用一个字节。
  • TypeId:是资源的类型Id值,一般都是由anim、animator、attr等类型,比如anim是0x01,animator是0x02,attr是0x03,依次类推。它也是占用一个字节
  • EntryId:是具体类型下资源实例的id值,从0开始,依次递增,它是占用2个字节。

在多module下,每一个module打包后,都会有自己的R.class,其中module为library如果引用了资源的时候,编译apk的时候module中引用的还是R.class中的资源id。如果是主module,也就是application类型的,它会直接被替换成常量值了。因为module的class文件,在主工程编译时,不会再次进行编译,module的calss文件原封不动的打包进apk。而资源id为常量是在主工程编译时才形成的,但module生成class时,使用的是上面说到的变量,所以一直被保留了下来。 如果同一个资源分别在module1和module2都定义了,module1中引用了自己module的资源,此时主module先后依赖了module1和module2,那么此时module1中引用的资源是module1中的资源,如果主module先后依赖了module2和module1,那么此时module1中引用的资源是module2中定义的。出现上面的原因是因为主module在编译的时候,看谁先依赖,如果找到了对应的资源,那么就会以先依赖的module中的资源为主。
在编译的过程中,会把合并后的资源生成一个临时的R.txt文件,它的路径在app模块下的build/intermediates/symbols/debug/R.txt中: alt text 在不同版本的agp上symbols文件夹可能不同,我这里用的agp是3.5.3,可以看到R.txt中每一行标明了资源的值类型、资源类型、资源名、资源对应的16进制值。最终通过aapt工具会构建resource.arsc文件。resource.arsc是一个资源映射表,里面通过16进制值找到对应的资源名。其中values文件夹下的资源都会在该文件中定义: alt text 可以看到多语言默认打到apk中,所以如果不需要国际化是可以省去多语言的。而像非values文件夹下的资源直接在res目录中。

总结 app打包过程中,总会用先依赖的module中定义的资源,即使该module用的是该module中的资源,最终由于打包合并资源的时候,优先去用先依赖的module中的资源。然后会生成R.txt文件,该文件是资源合并后生成的文件,里面每一行定义了某个资源的资源值类型、资源类型、资源名、资源16进制值,最终通过该文件生成resources.arsc,该文件是描述每个资源的资源值和资源名的一个对应关系,最终通过该关系找到资源。
在代码中,主module默认是通过常量引用资源,而library的module中默认引用资源的时候,是引用自己module的R中的变量,而资源最终会合并,所以最终使用的资源看合并时候哪个资源被打到apk中。

上面提到谁先依赖的问题, 其实这个可以通过gradle命令查看依赖关系,比如我想看app这个module的依赖,比如我想看运行时的依赖:

1
./gradlew app:dependencies --configuration debugRuntimeClasspath
  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
debugRuntimeClasspath - Resolved configuration for runtime for variant: debug
+--- project :lib
|    +--- project :lib2
|    |    +--- androidx.constraintlayout:constraintlayout:1.1.3
|    |    |    \--- androidx.constraintlayout:constraintlayout-solver:1.1.3
|    |    +--- com.google.android.material:material:1.1.0
|    |    |    +--- androidx.annotation:annotation:1.0.1 -> 1.1.0
|    |    |    +--- androidx.appcompat:appcompat:1.1.0
|    |    |    |    +--- androidx.annotation:annotation:1.1.0
|    |    |    |    +--- androidx.core:core:1.1.0
|    |    |    |    |    +--- androidx.annotation:annotation:1.1.0
|    |    |    |    |    +--- androidx.lifecycle:lifecycle-runtime:2.0.0 -> 2.1.0
|    |    |    |    |    |    +--- androidx.lifecycle:lifecycle-common:2.1.0
|    |    |    |    |    |    |    \--- androidx.annotation:annotation:1.1.0
|    |    |    |    |    |    +--- androidx.arch.core:core-common:2.1.0
|    |    |    |    |    |    |    \--- androidx.annotation:annotation:1.1.0
|    |    |    |    |    |    \--- androidx.annotation:annotation:1.1.0
|    |    |    |    |    +--- androidx.versionedparcelable:versionedparcelable:1.1.0
|    |    |    |    |    |    +--- androidx.annotation:annotation:1.1.0
|    |    |    |    |    |    \--- androidx.collection:collection:1.0.0 -> 1.1.0
|    |    |    |    |    |         \--- androidx.annotation:annotation:1.1.0
|    |    |    |    |    \--- androidx.collection:collection:1.0.0 -> 1.1.0 (*)
|    |    |    |    +--- androidx.cursoradapter:cursoradapter:1.0.0
|    |    |    |    |    \--- androidx.annotation:annotation:1.0.0 -> 1.1.0
|    |    |    |    +--- androidx.fragment:fragment:1.1.0
|    |    |    |    |    +--- androidx.annotation:annotation:1.1.0
|    |    |    |    |    +--- androidx.core:core:1.1.0 (*)
|    |    |    |    |    +--- androidx.collection:collection:1.1.0 (*)
|    |    |    |    |    +--- androidx.viewpager:viewpager:1.0.0
|    |    |    |    |    |    +--- androidx.annotation:annotation:1.0.0 -> 1.1.0
|    |    |    |    |    |    +--- androidx.core:core:1.0.0 -> 1.1.0 (*)
|    |    |    |    |    |    \--- androidx.customview:customview:1.0.0
|    |    |    |    |    |         +--- androidx.annotation:annotation:1.0.0 -> 1.1.0
|    |    |    |    |    |         \--- androidx.core:core:1.0.0 -> 1.1.0 (*)
|    |    |    |    |    +--- androidx.loader:loader:1.0.0
|    |    |    |    |    |    +--- androidx.annotation:annotation:1.0.0 -> 1.1.0
|    |    |    |    |    |    +--- androidx.core:core:1.0.0 -> 1.1.0 (*)
|    |    |    |    |    |    +--- androidx.lifecycle:lifecycle-livedata:2.0.0
|    |    |    |    |    |    |    +--- androidx.arch.core:core-runtime:2.0.0
|    |    |    |    |    |    |    |    +--- androidx.annotation:annotation:1.0.0 -> 1.1.0
|    |    |    |    |    |    |    |    \--- androidx.arch.core:core-common:2.0.0 -> 2.1.0 (*)
|    |    |    |    |    |    |    +--- androidx.lifecycle:lifecycle-livedata-core:2.0.0
|    |    |    |    |    |    |    |    +--- androidx.lifecycle:lifecycle-common:2.0.0 -> 2.1.0 (*)
|    |    |    |    |    |    |    |    +--- androidx.arch.core:core-common:2.0.0 -> 2.1.0 (*)
|    |    |    |    |    |    |    |    \--- androidx.arch.core:core-runtime:2.0.0 (*)
|    |    |    |    |    |    |    \--- androidx.arch.core:core-common:2.0.0 -> 2.1.0 (*)
|    |    |    |    |    |    \--- androidx.lifecycle:lifecycle-viewmodel:2.0.0 -> 2.1.0
|    |    |    |    |    |         \--- androidx.annotation:annotation:1.1.0
|    |    |    |    |    +--- androidx.activity:activity:1.0.0
|    |    |    |    |    |    +--- androidx.annotation:annotation:1.1.0
|    |    |    |    |    |    +--- androidx.core:core:1.1.0 (*)
|    |    |    |    |    |    +--- androidx.lifecycle:lifecycle-runtime:2.1.0 (*)
|    |    |    |    |    |    +--- androidx.lifecycle:lifecycle-viewmodel:2.1.0 (*)
|    |    |    |    |    |    \--- androidx.savedstate:savedstate:1.0.0
|    |    |    |    |    |         +--- androidx.annotation:annotation:1.1.0
|    |    |    |    |    |         +--- androidx.arch.core:core-common:2.0.1 -> 2.1.0 (*)
|    |    |    |    |    |         \--- androidx.lifecycle:lifecycle-common:2.0.0 -> 2.1.0 (*)
|    |    |    |    |    \--- androidx.lifecycle:lifecycle-viewmodel:2.0.0 -> 2.1.0 (*)
|    |    |    |    +--- androidx.appcompat:appcompat-resources:1.1.0
|    |    |    |    |    +--- androidx.annotation:annotation:1.1.0
|    |    |    |    |    +--- androidx.core:core:1.0.1 -> 1.1.0 (*)
|    |    |    |    |    +--- androidx.vectordrawable:vectordrawable:1.1.0
|    |    |    |    |    |    +--- androidx.annotation:annotation:1.1.0
|    |    |    |    |    |    +--- androidx.core:core:1.1.0 (*)
|    |    |    |    |    |    \--- androidx.collection:collection:1.1.0 (*)
|    |    |    |    |    +--- androidx.vectordrawable:vectordrawable-animated:1.1.0
|    |    |    |    |    |    +--- androidx.vectordrawable:vectordrawable:1.1.0 (*)
|    |    |    |    |    |    +--- androidx.interpolator:interpolator:1.0.0
|    |    |    |    |    |    |    \--- androidx.annotation:annotation:1.0.0 -> 1.1.0
|    |    |    |    |    |    \--- androidx.collection:collection:1.1.0 (*)
|    |    |    |    |    \--- androidx.collection:collection:1.0.0 -> 1.1.0 (*)
|    |    |    |    +--- androidx.drawerlayout:drawerlayout:1.0.0
|    |    |    |    |    +--- androidx.annotation:annotation:1.0.0 -> 1.1.0
|    |    |    |    |    +--- androidx.core:core:1.0.0 -> 1.1.0 (*)
|    |    |    |    |    \--- androidx.customview:customview:1.0.0 (*)
|    |    |    |    \--- androidx.collection:collection:1.0.0 -> 1.1.0 (*)
|    |    |    +--- androidx.cardview:cardview:1.0.0
|    |    |    |    \--- androidx.annotation:annotation:1.0.0 -> 1.1.0
|    |    |    +--- androidx.coordinatorlayout:coordinatorlayout:1.1.0
|    |    |    |    +--- androidx.annotation:annotation:1.1.0
|    |    |    |    +--- androidx.core:core:1.1.0 (*)
|    |    |    |    +--- androidx.customview:customview:1.0.0 (*)
|    |    |    |    \--- androidx.collection:collection:1.0.0 -> 1.1.0 (*)
|    |    |    +--- androidx.core:core:1.1.0 (*)
|    |    |    +--- androidx.fragment:fragment:1.0.0 -> 1.1.0 (*)
|    |    |    +--- androidx.lifecycle:lifecycle-runtime:2.0.0 -> 2.1.0 (*)
|    |    |    +--- androidx.recyclerview:recyclerview:1.0.0 -> 1.1.0
|    |    |    |    +--- androidx.annotation:annotation:1.1.0
|    |    |    |    +--- androidx.core:core:1.1.0 (*)
|    |    |    |    +--- androidx.customview:customview:1.0.0 (*)
|    |    |    |    \--- androidx.collection:collection:1.0.0 -> 1.1.0 (*)
|    |    |    +--- androidx.transition:transition:1.2.0
|    |    |    |    +--- androidx.annotation:annotation:1.1.0
|    |    |    |    +--- androidx.core:core:1.0.1 -> 1.1.0 (*)
|    |    |    |    \--- androidx.collection:collection:1.0.0 -> 1.1.0 (*)
|    |    |    +--- androidx.vectordrawable:vectordrawable:1.1.0 (*)
|    |    |    \--- androidx.viewpager2:viewpager2:1.0.0
|    |    |         +--- androidx.annotation:annotation:1.1.0
|    |    |         +--- androidx.fragment:fragment:1.1.0 (*)
|    |    |         +--- androidx.recyclerview:recyclerview:1.1.0 (*)
|    |    |         +--- androidx.core:core:1.1.0 (*)
|    |    |         \--- androidx.collection:collection:1.1.0 (*)
|    |    +--- androidx.cardview:cardview:1.0.0 (*)
|    |    \--- androidx.appcompat:appcompat:1.1.0 (*)
|    +--- androidx.constraintlayout:constraintlayout:1.1.3 (*)
|    +--- com.google.android.material:material:1.1.0 (*)
|    +--- androidx.cardview:cardview:1.0.0 (*)
|    \--- androidx.appcompat:appcompat:1.1.0 (*)
+--- project :lib1
|    +--- project :lib3
|    |    +--- androidx.constraintlayout:constraintlayout:1.1.3 (*)
|    |    +--- com.google.android.material:material:1.1.0 (*)
|    |    +--- androidx.cardview:cardview:1.0.0 (*)
|    |    \--- androidx.appcompat:appcompat:1.1.0 (*)
|    +--- androidx.constraintlayout:constraintlayout:1.1.3 (*)
|    +--- com.google.android.material:material:1.1.0 (*)
|    +--- androidx.cardview:cardview:1.0.0 (*)
|    \--- androidx.appcompat:appcompat:1.1.0 (*)
+--- androidx.constraintlayout:constraintlayout:1.1.3 (*)
+--- com.google.android.material:material:1.1.0 (*)
+--- androidx.cardview:cardview:1.0.0 (*)
+--- androidx.appcompat:appcompat:1.1.0 (*)
\--- com.didiglobal.booster:booster-android-instrument-toast:3.1.0
     \--- com.didiglobal.booster:booster-android-instrument:3.1.0

可以看出来app的module先是依赖了lib和lib1这两个module,并且lib这个module又依赖了lib2,lib1依赖了lib3,所以资源合并的时候,优先使用lib和lib2中的资源。 关于更多依赖的gradle命令:

1
2
//查看编译时的依赖,它只会看直接依赖的子module,间接依赖的子module不会查看
./gradlew app:dependencies --configuration debugCompileClasspath
1
2
//它会输出debug、release、test、compile、runtime组合的依赖
./gradlew app:dependencies

注:下面介绍的R内联插件是在booster3.1.0版本上 介绍完上面资源文件引用问题,我们发现随着module和aar越来越多的时候,R.class的数量会急剧增加,导致包体积急增。那booster是怎么解决这个R.class剧增的呢?
它是通过把子module的R.class中定义的变量都删除掉,然后子module中的代码引用的资源变量会直接被R.txt中定义的常量值所代替。这就是R文件内联的过程。那下面就分析下这个过程怎么实现的,前面介绍过各个插件是通过自己的ClassTransformer来实现字节码修改,其中R文件内联的插件是通过RInlineTransformer实现的,首先看下onPreTransform中的实现:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
override fun onPreTransform(context: TransformContext) {
    this.appPackage = context.originalApplicationId.replace('.', '/')
    //①解析R.txt,生成sybols信息
    this.symbols = SymbolList.from(context.artifacts.get(SYMBOL_LIST).single())
    this.appRStyleable = "$appPackage/$R_STYLEABLE"
    this.ignores = context.getProperty(PROPERTY_IGNORES)?.split(',')?.map { Wildcard(it) }?.toSet() ?: emptySet()

    //②忽略constrainlayout中的id属性
    val retainedSymbols: Set<String>
    val classpath = context.compileClasspath.map { it.absolutePath }
    if (classpath.any { it.contains(PREFIX_SUPPORT_CONSTRAINT_LAYOUT) || it.contains(PREFIX_JETPACK_CONSTRAINT_LAYOUT) }) {
        // Find symbols that should be retained
        retainedSymbols = context.findRetainedSymbols()
        if (retainedSymbols.isNotEmpty()) {
            this.ignores += setOf(Wildcard.valueOf("android/support/constraint/R\$id"))
            this.ignores += setOf(Wildcard.valueOf("androidx/constraintlayout/R\$id"))
        }
    } else {
        retainedSymbols = emptySet()
    }
}

在onPreTransform①处首先解析R.txt文件,文章开头已经介绍过该文件,它主要是用来存储资源文件合并后的信息。在②处会忽略ConstrainLayout中的R.id.**的变量,因为在 ConstraintLayout 中,R.id.xxx 可能会被 ConstraintSet 或者其他代码 动态解析,比如:

1
2
3
val constraintSet = ConstraintSet()
constraintSet.clone(constraintLayout)
constraintSet.connect(R.id.viewA, ConstraintSet.START, R.id.viewB, ConstraintSet.END)

Booster 进行 代码压缩/重排 时,如果对 R.id.xxx 进行了内联或优化,可能会导致 ID 失效,进而导致约束布局无法正常工作。接着看下transform方法,它是操作字节码的关键方法:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
override fun transform(context: TransformContext, klass: ClassNode): ClassNode {
    if (this.symbols.isEmpty()) {
        return klass
    }
    if (this.ignores.any { it.matches(klass.name) }) {
        logger.println("Ignore `${klass.name}`")
    } else if (Pattern.matches(R_REGEX, klass.name) && klass.name != appRStyleable) {
        //通过正则匹配删除R$color这种内部类的所有属性
        klass.fields.clear()
        removedR[klass.name] = klass.bytes()
    } else {
        //如果是代码中调用了R.color.**这种,直接操作字节码
        klass.replaceSymbolReferenceWithConstant()
    }

    return klass
}

在transform方法中,通过正则匹配删除R$color这种内部类的所有属性,如果不是R$color这种内部类,则通过扩展函数replaceSymbolReferenceWithConstant直接操作字节码

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
private fun ClassNode.replaceSymbolReferenceWithConstant() {
    methods.forEach { method ->
        //①:通过方法的指令集来过滤
        val insns = method.instructions.iterator().asIterable().filter {
            //②:如果当前指令是getstatic
            it.opcode == GETSTATIC
        }.map {
            it as FieldInsnNode
        }.filter {
            //③:返回值是int类型并且是以**R$开头获取R文件中的常量形式并且不是以android内部的R开头的话
            ("I" == it.desc || "[I" == it.desc)
                    && it.owner.substring(it.owner.lastIndexOf('/') + 1).startsWith("R$")
                    && !(it.owner.startsWith(COM_ANDROID_INTERNAL_R) || it.owner.startsWith(ANDROID_R))
        }
        //④:过滤出返回值为int类型的指令
        val intFields = insns.filter { "I" == it.desc }
        val intArrayFields = insns.filter { "[I" == it.desc }

        intFields.forEach { field ->
            //⑤:获取R文件的资源类型
            val type = field.owner.substring(field.owner.lastIndexOf("/R$") + 3)
            try {
                //⑥:在获取R中常量指令之前插入常量的指令,LdcInsnNode中的参数是获取这个常量值
                method.instructions.insertBefore(field, LdcInsnNode(symbols.getInt(type, field.name)))
                //⑦:移除当前获取变量的指令
                method.instructions.remove(field)
            } catch (e: NullPointerException) {
                logger.println("Unresolvable symbol `R.$type.${field.name}` : $name.${method.name}${method.desc}")
            }
        }
        //⑧:int数组类型的指令直接把owner替换成app目录的包名
        intArrayFields.forEach { field ->
            field.owner = "$appPackage/${field.owner.substring(field.owner.lastIndexOf('/') + 1)}"
        }
    }
}

在上面字节码操作如果不太熟悉的话,我们可以在library模块中定义类去获取R中的资源,比如我下面定义一个TestView,获取自己的R中的color属性:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
public class TestView extends View {
    public TestView(Context context) {
        this(context, null);
    }

    public TestView(Context context, @Nullable AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public TestView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        setBackgroundColor(getResources().getColor(R.color.black_bg));
    }
}

上面获取R.color.black_bg,那此处会用booster的R内联给替换成常量,怎么替换,先肯定是找到这个指令对不?看下此处的指令,我们通过jclasslib bytecode viewer来查看编译后的class:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
 0 aload_0
 1 aload_1
 2 aload_2
 3 iload_3
 4 invokespecial #3 <android/view/View.<init> : (Landroid/content/Context;Landroid/util/AttributeSet;I)V>
 7 aload_0
 8 aload_0
 9 invokevirtual #4 <com/xc/lib/TestView.getResources : ()Landroid/content/res/Resources;>
12 getstatic #5 <com/xc/lib/R$color.black_bg : I>
15 invokevirtual #6 <android/content/res/Resources.getColor : (I)I>
18 invokevirtual #7 <com/xc/lib/TestView.setBackgroundColor : (I)V>
21 return

在12行,通过getstatic来获取com/xc/lib/R$color.black_bg,返回类型是I,表示整形。所以上面①-④的操作就是在获取该指令,在⑤处获取该资源类型,上面TestView中是获取color类型的资源,在⑥处表示在该获取变量的指令前插入一条LdcInsnNode的常量指令,在⑦处移除当前获取变量的指令。在⑧处如果是int数组类型的指令直接把owner替换成app目录的包名。 结合修改后的字节码,查看TestView修改后的字节码,通过BytecodeViewer来查看编译后的class: alt text
可以看到最终通过ldc指令来获取一个常量来替换之前获取R.color.black_bg变量。同时再来看下module下的R$color.class: alt text
可以看到该内部类所有的属性被置为空了,包括主module下的R$color.class下的属性也是清空了。所以booster的方案是不区分主module还是lib的module。

为什么R$styleable中的静态变量不会被移除?并且主module是原封不动,lib的module是只保留了数组类型的变量,并且是在static代码块中定义数组,并且数组的定于缺少public static final关键字:

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
package com.xc.lib;

public final class R$styleable {
   static {
      ActionBar = new int[]{2130903092, 2130903099, 2130903100, 2130903224, 2130903225, 2130903226, 2130903227, 2130903228, 2130903229, 2130903255, 2130903264, 2130903265, 2130903284, 2130903343, 2130903349, 2130903355, 2130903356, 2130903358, 2130903368, 2130903381, 2130903481, 2130903513, 2130903532, 2130903536, 2130903537, 2130903598, 2130903601, 2130903673, 2130903683};
      ActionBarLayout = new int[]{16842931};
      ActionMenuItemView = new int[]{16843071};
      ActionMenuView = new int[0];
      ActionMode = new int[]{2130903092, 2130903099, 2130903192, 2130903343, 2130903601, 2130903683};
      ActivityChooserView = new int[]{2130903303, 2130903369};
      AlertDialog = new int[]{16842994, 2130903142, 2130903143, 2130903470, 2130903471, 2130903510, 2130903566, 2130903568};
      AnimatedStateListDrawableCompat = new int[]{16843036, 16843156, 16843157, 16843158, 16843532, 16843533};
      AnimatedStateListDrawableItem = new int[]{16842960, 16843161};
      AnimatedStateListDrawableTransition = new int[]{16843161, 16843849, 16843850, 16843851};
      AppBarLayout = new int[]{16842964, 16843919, 16844096, 2130903284, 2130903304, 2130903462, 2130903463, 2130903592};
      AppBarLayoutStates = new int[]{2130903586, 2130903587, 2130903589, 2130903590};
      AppBarLayout_Layout = new int[]{2130903460, 2130903461};
      AppCompatImageView = new int[]{16843033, 2130903578, 2130903671, 2130903672};
      AppCompatSeekBar = new int[]{16843074, 2130903668, 2130903669, 2130903670};
      AppCompatTextHelper = new int[]{16842804, 16843117, 16843118, 16843119, 16843120, 16843666, 16843667};
      AppCompatTextView = new int[]{16842804, 2130903087, 2130903088, 2130903089, 2130903090, 2130903091, 2130903269, 2130903270, 2130903271, 2130903272, 2130903274, 2130903275, 2130903276, 2130903277, 2130903326, 2130903329, 2130903337, 2130903399, 2130903464, 2130903633, 2130903660};
      AppCompatTheme = new int[]{16842839, 16842926, 2130903040, 2130903041, 2130903042, 2130903043, 2130903044, 2130903045, 2130903046, 2130903047, 2130903048, 2130903049, 2130903050, 2130903051, 2130903052, 2130903054, 2130903055, 2130903056, 2130903057, 2130903058, 2130903059, 2130903060, 2130903061, 2130903062, 2130903063, 2130903064, 2130903065, 2130903066, 2130903067, 2130903068, 2130903069, 2130903070, 2130903074, 2130903075, 2130903076, 2130903077, 2130903078, 2130903086, 2130903120, 2130903135, 2130903136, 2130903137, 2130903138, 2130903139, 2130903144, 2130903145, 2130903157, 2130903164, 2130903198, 2130903199, 2130903200, 2130903201, 2130903202, 2130903203, 2130903204, 2130903211, 2130903212, 2130903218, 2130903236, 2130903261, 2130903262, 2130903263, 2130903266, 2130903268, 2130903279, 2130903280, 2130903281, 2130903282, 2130903283, 2130903355, 2130903367, 2130903466, 2130903467, 2130903468, 2130903469, 2130903472, 2130903473, 2130903474, 2130903475, 2130903476, 2130903477, 2130903478, 2130903479, 2130903480, 2130903522, 2130903523, 2130903524, 2130903531, 2130903533, 2130903540, 2130903542, 2130903543, 2130903544, 2130903553, 2130903554, 2130903555, 2130903556, 2130903575, 2130903576, 2130903605, 2130903644, 2130903646, 2130903647, 2130903648, 2130903650, 2130903651, 2130903652, 2130903653, 2130903656, 2130903657, 2130903685, 2130903686, 2130903687, 2130903688, 2130903696, 2130903698, 2130903699, 2130903700, 2130903701, 2130903702, 2130903703, 2130903704, 2130903705, 2130903706, 2130903707};
      Badge = new int[]{2130903093, 2130903103, 2130903105, 2130903505, 2130903515};
      BottomAppBar = new int[]{2130903101, 2130903284, 2130903314, 2130903315, 2130903316, 2130903317, 2130903318, 2130903350};
      BottomNavigationView = new int[]{2130903101, 2130903284, 2130903373, 2130903376, 2130903378, 2130903379, 2130903382, 2130903394, 2130903395, 2130903396, 2130903398, 2130903508};
      BottomSheetBehavior_Layout = new int[]{16843840, 2130903101, 2130903111, 2130903112, 2130903113, 2130903114, 2130903116, 2130903117, 2130903118, 2130903557, 2130903560};
      ButtonBarLayout = new int[]{2130903079};
      CardView = new int[]{16843071, 16843072, 2130903148, 2130903149, 2130903150, 2130903152, 2130903153, 2130903154, 2130903230, 2130903231, 2130903232, 2130903233, 2130903234};
      Chip = new int[]{16842804, 16842904, 16842923, 16843039, 16843087, 16843237, 2130903160, 2130903161, 2130903163, 2130903165, 2130903166, 2130903167, 2130903169, 2130903170, 2130903171, 2130903172, 2130903173, 2130903174, 2130903175, 2130903180, 2130903181, 2130903182, 2130903184, 2130903185, 2130903186, 2130903187, 2130903188, 2130903189, 2130903190, 2130903191, 2130903296, 2130903348, 2130903359, 2130903363, 2130903547, 2130903557, 2130903560, 2130903564, 2130903658, 2130903661};
      ChipGroup = new int[]{2130903159, 2130903176, 2130903177, 2130903178, 2130903569, 2130903570};
      CollapsingToolbarLayout = new int[]{2130903195, 2130903196, 2130903235, 2130903305, 2130903306, 2130903307, 2130903308, 2130903309, 2130903310, 2130903311, 2130903548, 2130903550, 2130903593, 2130903673, 2130903674, 2130903684};
      CollapsingToolbarLayout_Layout = new int[]{2130903405, 2130903406};
      ColorStateListItem = new int[]{16843173, 16843551, 2130903080};
      CompoundButton = new int[]{16843015, 2130903140, 2130903146, 2130903147};
      ConstraintLayout_Layout = new int[]{16842948, 16843039, 16843040, 16843071, 16843072, 2130903107, 2130903108, 2130903156, 2130903220, 2130903221, 2130903407, 2130903408, 2130903409, 2130903410, 2130903411, 2130903412, 2130903413, 2130903414, 2130903415, 2130903416, 2130903417, 2130903418, 2130903419, 2130903420, 2130903421, 2130903422, 2130903423, 2130903424, 2130903425, 2130903426, 2130903427, 2130903428, 2130903429, 2130903430, 2130903431, 2130903432, 2130903433, 2130903434, 2130903435, 2130903436, 2130903437, 2130903438, 2130903439, 2130903440, 2130903441, 2130903442, 2130903443, 2130903444, 2130903445, 2130903446, 2130903447, 2130903449, 2130903450, 2130903451, 2130903452, 2130903453, 2130903454, 2130903455, 2130903456, 2130903459};
      ConstraintLayout_placeholder = new int[]{2130903222, 2130903287};
      ConstraintSet = new int[]{16842948, 16842960, 16842972, 16842996, 16842997, 16842999, 16843000, 16843001, 16843002, 16843039, 16843040, 16843071, 16843072, 16843551, 16843552, 16843553, 16843554, 16843555, 16843556, 16843557, 16843558, 16843559, 16843560, 16843701, 16843702, 16843770, 16843840, 2130903107, 2130903108, 2130903156, 2130903221, 2130903407, 2130903408, 2130903409, 2130903410, 2130903411, 2130903412, 2130903413, 2130903414, 2130903415, 2130903416, 2130903417, 2130903418, 2130903419, 2130903420, 2130903421, 2130903422, 2130903423, 2130903424, 2130903425, 2130903426, 2130903427, 2130903428, 2130903429, 2130903430, 2130903431, 2130903432, 2130903433, 2130903434, 2130903435, 2130903436, 2130903437, 2130903438, 2130903439, 2130903440, 2130903441, 2130903442, 2130903443, 2130903444, 2130903445, 2130903446, 2130903447, 2130903449, 2130903450, 2130903451, 2130903452, 2130903453, 2130903454, 2130903455, 2130903456};
      CoordinatorLayout = new int[]{2130903397, 2130903591};
      CoordinatorLayout_Layout = new int[]{16842931, 2130903402, 2130903403, 2130903404, 2130903448, 2130903457, 2130903458};
      DrawerArrowToggle = new int[]{2130903084, 2130903085, 2130903106, 2130903197, 2130903273, 2130903340, 2130903574, 2130903664};
      ExtendedFloatingActionButton = new int[]{2130903284, 2130903312, 2130903348, 2130903564, 2130903567};
      ExtendedFloatingActionButton_Behavior_Layout = new int[]{2130903109, 2130903110};
      FloatingActionButton = new int[]{2130903101, 2130903102, 2130903119, 2130903284, 2130903296, 2130903319, 2130903320, 2130903348, 2130903357, 2130903506, 2130903535, 2130903547, 2130903557, 2130903560, 2130903564, 2130903694};
      FloatingActionButton_Behavior_Layout = new int[]{2130903109};
      FlowLayout = new int[]{2130903390, 2130903465};
      FontFamily = new int[]{2130903330, 2130903331, 2130903332, 2130903333, 2130903334, 2130903335};
      FontFamilyFont = new int[]{16844082, 16844083, 16844095, 16844143, 16844144, 2130903328, 2130903336, 2130903337, 2130903338, 2130903693};
      ForegroundLinearLayout = new int[]{16843017, 16843264, 2130903339};
      GradientColor = new int[]{16843165, 16843166, 16843169, 16843170, 16843171, 16843172, 16843265, 16843275, 16844048, 16844049, 16844050, 16844051};
      GradientColorItem = new int[]{16843173, 16844052};
      LinearConstraintLayout = new int[]{16842948};
      LinearLayoutCompat = new int[]{16842927, 16842948, 16843046, 16843047, 16843048, 2130903265, 2130903267, 2130903507, 2130903563};
      LinearLayoutCompat_Layout = new int[]{16842931, 16842996, 16842997, 16843137};
      ListPopupWindow = new int[]{16843436, 16843437};
      MaterialAlertDialog = new int[]{2130903094, 2130903095, 2130903096, 2130903097};
      MaterialAlertDialogTheme = new int[]{2130903483, 2130903484, 2130903485, 2130903486, 2130903487};
      MaterialButton = new int[]{16843191, 16843192, 16843193, 16843194, 16843237, 2130903101, 2130903102, 2130903243, 2130903284, 2130903358, 2130903360, 2130903361, 2130903362, 2130903364, 2130903365, 2130903547, 2130903557, 2130903560, 2130903594, 2130903595};
      MaterialButtonToggleGroup = new int[]{2130903158, 2130903570};
      MaterialCalendar = new int[]{16843277, 2130903256, 2130903257, 2130903258, 2130903259, 2130903541, 2130903708, 2130903709, 2130903710};
      MaterialCalendarItem = new int[]{16843191, 16843192, 16843193, 16843194, 2130903374, 2130903383, 2130903384, 2130903391, 2130903392, 2130903396};
      MaterialCardView = new int[]{16843237, 2130903151, 2130903160, 2130903162, 2130903547, 2130903557, 2130903560, 2130903588, 2130903594, 2130903595};
      MaterialCheckBox = new int[]{2130903146, 2130903695};
      MaterialRadioButton = new int[]{2130903695};
      MaterialShape = new int[]{2130903557, 2130903560};
      MaterialTextAppearance = new int[]{16844159, 2130903464};
      MaterialTextView = new int[]{16842804, 16844159, 2130903464};
      MenuGroup = new int[]{16842766, 16842960, 16843156, 16843230, 16843231, 16843232};
      MenuItem = new int[]{16842754, 16842766, 16842960, 16843014, 16843156, 16843230, 16843231, 16843233, 16843234, 16843235, 16843236, 16843237, 16843375, 2130903053, 2130903071, 2130903073, 2130903081, 2130903223, 2130903364, 2130903365, 2130903516, 2130903562, 2130903689};
      MenuView = new int[]{16842926, 16843052, 16843053, 16843054, 16843055, 16843056, 16843057, 2130903534, 2130903596};
      NavigationView = new int[]{16842964, 16842973, 16843039, 2130903284, 2130903342, 2130903373, 2130903375, 2130903377, 2130903378, 2130903379, 2130903380, 2130903383, 2130903384, 2130903385, 2130903386, 2130903387, 2130903388, 2130903389, 2130903393, 2130903396, 2130903508};
      PopupWindow = new int[]{16843126, 16843465, 2130903517};
      PopupWindowBackgroundState = new int[]{2130903585};
      RecycleListView = new int[]{2130903518, 2130903521};
      RecyclerView = new int[]{16842948, 16842987, 16842993, 2130903321, 2130903322, 2130903323, 2130903324, 2130903325, 2130903401, 2130903546, 2130903573, 2130903579};
      ScrimInsetsFrameLayout = new int[]{2130903370};
      ScrollingViewBehavior_Layout = new int[]{2130903115};
      SearchView = new int[]{16842970, 16843039, 16843296, 16843364, 2130903185, 2130903219, 2130903260, 2130903341, 2130903366, 2130903400, 2130903538, 2130903539, 2130903551, 2130903552, 2130903597, 2130903602, 2130903697};
      ShapeAppearance = new int[]{2130903238, 2130903239, 2130903240, 2130903241, 2130903242, 2130903244, 2130903245, 2130903246, 2130903247, 2130903248};
      Snackbar = new int[]{2130903571, 2130903572};
      SnackbarLayout = new int[]{16843039, 2130903072, 2130903082, 2130903098, 2130903284, 2130903503};
      Spinner = new int[]{16842930, 16843126, 16843131, 16843362, 2130903532};
      StateListDrawable = new int[]{16843036, 16843156, 16843157, 16843158, 16843532, 16843533};
      StateListDrawableItem = new int[]{16843161};
      SwitchCompat = new int[]{16843044, 16843045, 16843074, 2130903565, 2130903577, 2130903603, 2130903604, 2130903606, 2130903665, 2130903666, 2130903667, 2130903690, 2130903691, 2130903692};
      SwitchMaterial = new int[]{2130903695};
      TabItem = new int[]{16842754, 16842994, 16843087};
      TabLayout = new int[]{2130903607, 2130903608, 2130903609, 2130903610, 2130903611, 2130903612, 2130903613, 2130903614, 2130903615, 2130903616, 2130903617, 2130903618, 2130903619, 2130903620, 2130903621, 2130903622, 2130903623, 2130903624, 2130903625, 2130903626, 2130903627, 2130903628, 2130903630, 2130903631, 2130903632};
      TextAppearance = new int[]{16842901, 16842902, 16842903, 16842904, 16842906, 16842907, 16843105, 16843106, 16843107, 16843108, 16843692, 16844165, 2130903329, 2130903337, 2130903633, 2130903660};
      TextInputLayout = new int[]{16842906, 16843088, 2130903125, 2130903126, 2130903127, 2130903128, 2130903129, 2130903130, 2130903131, 2130903132, 2130903133, 2130903134, 2130903249, 2130903250, 2130903251, 2130903252, 2130903253, 2130903254, 2130903288, 2130903289, 2130903290, 2130903291, 2130903292, 2130903293, 2130903297, 2130903298, 2130903299, 2130903300, 2130903301, 2130903302, 2130903344, 2130903345, 2130903346, 2130903347, 2130903351, 2130903352, 2130903353, 2130903354, 2130903525, 2130903526, 2130903527, 2130903528, 2130903529, 2130903557, 2130903560, 2130903580, 2130903581, 2130903582, 2130903583, 2130903584};
      ThemeEnforcement = new int[]{16842804, 2130903294, 2130903295};
      Toolbar = new int[]{16842927, 16843072, 2130903141, 2130903193, 2130903194, 2130903224, 2130903225, 2130903226, 2130903227, 2130903228, 2130903229, 2130903481, 2130903482, 2130903504, 2130903508, 2130903511, 2130903512, 2130903532, 2130903598, 2130903599, 2130903600, 2130903673, 2130903675, 2130903676, 2130903677, 2130903678, 2130903679, 2130903680, 2130903681, 2130903682};
      View = new int[]{16842752, 16842970, 2130903519, 2130903520, 2130903662};
      ViewBackgroundHelper = new int[]{16842964, 2130903101, 2130903102};
      ViewPager2 = new int[]{16842948};
      ViewStubCompat = new int[]{16842960, 16842994, 16842995};
   }

   private R$styleable() {
   }
}

下面测试下R.styleable结果在class中的体现: alt text 可以看到在lib中引用到的styleable数组属性被替换成主module中的属性了。lib的R.class中只有R$styleable的数组属性,并且在静态代码块中。

Licensed under CC BY-NC-SA 4.0
Built with Hugo
Theme Stack designed by Jimmy