Android 编译resource.arsc资源Resource ID解析

Android 编译resource.arsc资源Resource ID解析

 这篇文章中,介绍了如何解析Android中编译之后的resource.arsc文件,这里就介绍了Android中资源文件编译之后的类型和格式,其实Android中资源编译之后,会产生一个R文件,所有的资源ID都是存储在这个文件中的的,默认我们看到所有的ID都有一个共同的特点,就是他们都是0x7F开头的,其实这个0x7F是包的ID值,我们在在解析resource.arsc文章中提到一点,Android中的id值其实是一个int类型,他的值由三部分组成:PackageId+TypeId+EntryId

PackageId:是包的Id值,Android中如果是第三方应用的话,这个值默认就是0x7F,系统应用的话就是0x01,具体我们可以后面看aapt源码得知,他占用两个字节。
TypeId:是资源的类型Id值,一般Android中有这几个类型:attr,drawable,layout,dimen,string,style等,而且这些类型的值是从1开始逐渐递增的,而且顺序不能改变,attr=0x01,drawable=0x02….他占用两个字节。
EntryId:是在具体的类型下资源实体的id值,从0开始,依次递增,他占用四个字节。

既然我们了解了Android中的资源Id的结构,下面我们来说说我们遇到的问题:
1、在Android项目中偶尔会出现依赖第三方库包,出现资源ID(packageId+typeId+ItemValue)发生冲突的问题(网上有很多解决方案,不一一列举,如public 限定等)。那么对于我们自己提供的库包,如果能指定其包的命令空间(默认是从127=0x7F开始),特别考虑mutiDex的情况,自定义修改package ID显得意义重大。
2、我们在开发Android中插件技术的时候,为了防止插件工程中的资源Id和宿主工程中的资源Id不冲突,也是需要去修改一下插件中编译之后的资源Id值,来减少冲突。
那么上面就是我们遇到的问题,其实我们的解决方案很简单,就是在编译的时候修改资源Id值,给一个限定值。

我们之前讲解了资源Id的组成结构,发现高两个字节是代表PackageId的值,而且第三方app的默认值是0x7F,那么我们能不能修改这个值呢?比如,插件1中的资源Id中的PackageId为0x30,插件2中的资源Id中的PackageId为0x31…这样每个插件的资源就被划分了一定的区域值,同时保证不要和主工程中的0x7F冲突即可,那么这些值就可以从0x02~0x7E了,这个区间值我们都是可以使用的,为什么0x01不能用呢?因为他是系统应用的呀,所以我们就有0x7E-0x02=124个区间,哈哈,听着好兴奋,那么我们是否可以操作了呢?答案是可以的,我们知道Android中编译资源用的是aapt命令,那么我们就可以查看他的源码来看看是否可以。

aapt命令是Android中提供的编译apk的一个工具,所以源码可以从 Android源码目录/tools/… 下面查看:

这个工具的源码还是不复杂的,没多少文件,当然入口肯定找main啥的关键字了,果然看到一个Main.cpp文件,打开查看,找到入口函数main,这里我们可以看到,他对输入参数做了判断:

这里main函数有点长,我们直接看最后的处理函数:

这里有一个handleCommand函数,这里就是主要处理命令的功能:

这里有好多个函数,但是我们这里需要关注的是doPackage函数,他是打出包的关键,但是这时候我们发现全局搜这个函数,找不到,那么这个函数肯定是被引用的,源码中查找具体函数,
脑补一下:
这里因为是Window系统,不想是Linux系统,可以直接使用find+grep就可以快速的查找到包含指定内容的文件了,但是Windows中提供了可视化的文件搜索,但是他默认在搜索的时候,只是搜索文件名,不搜索包含的内容,所以需要设置一下,可以到文件夹选项中设置:

这时候我们可以在tools目录下搜索了:

这时候看到了,我们搜到了三个文件,Main.cpp可以不用看了,因为已经看过了,那么就在Command.cpp里面了:

这里有一个方法,而且我们看注释,这里就是编译的核心函数:buildResources,我们在全局搜这个函数,没找到,那么我们还是到整个目录下去搜:

搜到了,在Resource.cpp中:

这里看到,一个packgeType字段,这个就是包类型,这里有三个类型:共享的,系统的,第三方
突然发现这个似乎和PackageId的值有关系,

在这里,用到了packageType,而且有一个重要的类型ResourceTable,这个就是资源索引表,和ResId有映射关系的数据结构,所以我们查看他的定义:在ResourceTable.cpp中

这里看到了有三个值,0x00,0x01,0x7F。说明我们找到核心的地方了。

这里构建了一个Package,这里传入了packageId值的,好了,我们分析源码就到这里了,那么下面我们来看一下源码流程:
首先找到入口类:Main.cpp:main函数,解析参数,然后调用handleCommand函数处理参数对应的逻辑,我们看到了有一个函数doPackage,这里就是处理编译工作的。
然后就搜索到了Command.cpp:在他内部的doPackage函数中进行编译工具的一个函数:buildResources函数,在全局搜索,发现了Resource.cpp:具体查看buildResources函数,发现这里就是处理编译工作,同时在这里我们也看到了核心,构建ResourceTable的逻辑,在ResourceTable.cpp中,也是获取PackageId的地方,到此我们就知道了大体的逻辑,那么知道了逻辑,下面我们就来看看如何修改呢?
其实最好的方法是,能够修改aapt源码,添加一个参数,把我们想要编译的PackageId作为输入值,传进来最好了,其实我们在看源码的时候发现,有一个类型始终传递这,那就是Bundle类型,他是从Main.cpp中的main函数传递到了最后的buildResources函数中,那么我们就可以把这个参数用Bundle进行携带。

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值