U8SDK——declare-styleable自定义资源的合并

作者: 分类: U8SDK 发布时间: 2021-06-18 19:44 6U8SDK——declare-styleable自定义资源的合并已关闭评论

之前我们分享了U8聚合SDK打包工具中对于Android母包、渠道SDK资源和各个插件SDK资源的合并原理:[回顾]

打包工具在打包的时候,经过上面的资源合并,对于普通的资源类型,基本没有问题。但是极少数情况下,我们遇到一种资源合并后,找不到某些资源的情形。 比如:

母包引用了androidx或者android support v7的库,该库中有不少declare-styleable类型的自定义资源。如果某个渠道SDK中也刚好有这个同名的自定义资源, 可能导致打包之后, 渠道SDK找不到该自定义资源里面的某个item

那为什么会出现这种情况呢?

经过分析我们发现:母包中定义的declare-styleable自定义资源,经过apktool.jar反编译之后, 自定义资源的局部item会变成全局定义的item,存放在res/attrs.xml中。如果渠道SDK某个自定义资源中某个局部item,在全局定义attrs.xml中已经存在,那么按照母包资源优先级>渠道资源优先级的规则,会将渠道SDK中的这个自定义资源的同名item删除,保留母包的全局的资源item定义。

我们看一段母包中原本的引用support库中的某个自定义资源的案例:

经过apktool.jar反编译后, 上面的资源,变成了res/atts.xml中的这样的全局定义了(丢失了外层的style定义):

那加入渠道SDK的资源文件中,正好也有一个一模一样的自定义资源(AppCompatTheme,内部item也完全相同), 那么经过打包工具的资源合并之后(根据优先级规则,舍弃渠道SDK中的局部资源item), 渠道SDK就变成这样了:

可以看到,丢失了全局定义(res/attrs.xml中)已经存在的条目。 这样,如果渠道SDK中某段代码,正好读取了这个自定义资源中的属性的话, 就读取不到了。 那为什么会这样呢?

我们知道,在编译apk后,资源会生成一个R.java资源索引文件。 对于上述自定义资源, 在R.java中正常会生成这样的资源索引条目:

按照上面资源合并之后, 导致AppCompatTheme节点中内容是空的,在使用aapt/aapt2编译资源生成R.java之后,生成的R.java里面,就没有上面AppCompatTheme_windowActionBar这样的资源索引的定义了。只有全局的索引定义:

那渠道SDK中读取资源的话, 默认是按AppCompatTheme_windowActionBar这样的方式去查询资源索引,就找不到该资源中的对应item条目了。导致资源找不到。

明白了其中的机制和原因, 我们怎么来处理这种资源合并的缺陷呢?

通过上面我们的分析可以看到, 问题的根源还是因为母包自定义资源中局部的item,经过apktool.jar反编译后,变成了全局的定义。那么,我们是否可以在母包反编译后, 将这些“被全局的资源item”还原成原先的局部定义的样式呢?这样都按局部的同名资源item合并, 就不会导致局部item的丢失了。

答案是肯定的。 而且实现起来很简单。通过上面的分析,我们发现:

经过apktool反编译后, 原本的局部定义的自定义资源虽然变成全局的定义了,但是资源索引文件R.java中的资源索引定义还是正确的。这样我们可以结合母包反编译后的R.java资源文件(反编译后在母包包名路径下R$styleable.smali)和res/attrs.xml中的全局资源定义,反向推到出原本的所有declare-styleable局部资源定义。

这个很简单了,就是解析R$styleable.smali文件中的属性定义, 按生成规则进行分割,然后将反推后的资源,重新生成到一个temp_values.xml文件中,同时删除attrs.xml中全局定义中的item条目。

该机制已经加入到了U8SDK最新打包脚本中, 如果对源码感兴趣同学, 可以看打包工具client/scripts/res_recovery.py 脚本类。

本文出自 U8SDK技术博客,转载时请注明出处及相应链接。

本文永久链接: http://www.uustory.com/?p=2428

评论功能已经关闭,请加入U8SDK技术群进行讨论和咨询:207609068
Ɣ回顶部
U8SDK技术群 x
技术同学请加入
点击加入