Android R文件生成

Android R文件生成

Android在打包时,通过AAPT工具,对主工程和引入的依赖里的所有资源文件进行编译压缩,并会对res/里的资源文件如drawable、layout、values等生成唯一的id,同时生成R.java文件,保存所有的id值,以及生成resource.arsc文件,建立id对应资源的值(如string)或文件路径(如png)的关系表。
在这里插入图片描述
如上图是我们apk中的最终R文件的样子,可以发现里面会按类型分为不同的类,如anim、attr、string等,每个类里有相应类型的所有资源id,id是16进制的int值,变量名就是我们在资源文件中定义的资源名字。

在这里插入图片描述
上图是一个id的组成:

  1. 第一个字节是指资源所属包,7F代表普通应用程序
  2. 第二个字节是指资源的类型,如02指drawable,03指layout
  3. 第三四个字节是指资源的编号,往往从00开始递增

除此之外我们还可以看到R文件里的id,都是public static final的静态常量,这会有什么好处呢?
Activity.java

Activity.java

R.layout.activity_second

R.layout.activity_second

Activity.class

Activity.class

可以看到,由于java编译器的优化,在编译时,所有使用静态常量的地方,会被直接替换为常量值。
这样一来,R文件里的id在编译完java文件后,就没有被引用的地方了,此时如果开启proGuard混淆,就会删除整个R文件,从而会减少field数和包大小。

AAR

以上是R文件的基本原理,但是这里有一个特殊,就是AAR的R文件规则。

AAR是被主工程引入的SDK,一个工程可能会引入多个AAR,这也就导致了一个问题:每个AAR的module如果在自己编译生成AAR时,按照正常的流程生成R文件,那么由于资源id的值都是从00递增,会导致集成到主工程时的冲突(大量id重复)。

所以其实AAR在编译时不会进行真正的R.java文件的生成,而是等到在主工程集成编译时统一进行所有资源的id分配。

这里需要说明的是,如果不同的AAR中有同类型的同名资源,则可能会在运行中有很多莫名其妙的问题,所以我们需要保证资源名的唯一性。

这里AAR其实做了两步工作:

  1. 为了支持我们的调用语法,生成了一个R.java文件,所以我们才可以在AAR的代码中调用AAR包名下的R.xxx
    aar里的Activity调用aar.R.layout.xxx

    aar里的Activity调用aar.R.layout.xxx

    这里生成的R文件里的id,并不是public static final的常量,而是public static的变量,这是为什么呢?因为如果是常量,则会在编译打包时,调用的地方被替换为常量值,而这个值是AAR内部生成的临时id,是不对的,这样的话主工程编译时将无法修改这个值,就有问题了。

  2. 因为AAR生成的R.java并不是最终正确的,所以这个R.java文件不会被带入AAR中,但是会生成一个R.txt文本文件,这个文件以文本的形式记录了AAR中所有资源的类型、名字等,以便于主工程打包时,可以依据这些资源信息统一生成最终的R.java文件。

主工程

主工程在编译时,会将主工程下的所有资源,连同所有AAR依赖里的R.txt文件一起,为所有的资源统一分配id,并生成R.java文件和resource.arsc文件,这时就可以保证每个资源都是唯一的id值。
这里需要注意的是:

  • 主工程编译时,最终除了会在主工程包名下,生成一个包含主工程和AAR所有资源的R.java文件之外,还会在每个AAR相应的包名下,生成一个包含AAR资源的R.java文件,当然,相同资源的id是一样的。这就是为什么我们可以在主工程中调用主包名的R文件,和AAR包名的R文件,都可以获取到一些资源id的原因。
    主工程包名下的R文件
主工程包名下的R文件

AAR包名下的R文件

AAR包名下的R文件
  • 上述最终生成的所有R.java文件里的id值,都是public static final的静态常量,因为此刻的id值都已经确定了。然后在编译java文件时,常量值会被替换(包括资源文件中的引用也会被替换),从而使R文件的field无引用,可以通过proGuard删除。

上述第2点,在主工程编译完成后,我们会发现一个问题,就是AAR里面的文件,使用到资源id的地方,并没有被替换为相应的常量值,但是R文件里面的资源id确实是常量。

这是因为AAR的class文件,在主工程编译时,不会再次进行编译,也就是说AAR的class文件原封不动的打包进apk。而资源id为常量是在主工程编译时才行程的,但AAR生成class时,使用的是上面说到的变量,所以一直被保留了下来。

这个可能是因为Google怕影响编译打包速度,而将AAR的class文件直接带入apk中,但是却忽略了资源id的引用被保留下来的问题。

这个问题可以被gradle插件解决,大致原理就是:gradle插件将所有AAR中引用到R文件资源id的地方,全部都替换为相应的id常量值,然后在proGuard混淆时,所有的R文件就会因为没有被引用到而删除了。

  • 6
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值