Skip to content

使用Public.xml的坑和填坑

limpoxe edited this page Dec 16, 2018 · 3 revisions

android gradle plugin 3.2.1 + aapt2以后,下文描述的部分坑已经不存在

1、gradle插件1.3.+版本不支持public的问题

android gradle插件1.3.+版本在执行mergeResource任务时忽略了public,所以merge完成后的build目录下的
res目录下没有public相关的内容。

因此,在1.3.0以下,可以直接将public.xml放在源码res目录参与编译, 而1.3.+需要在编译时通过脚本将
public插入到merge完成后的build目录下的res目录下。

之所以这样做可行,是因为aapt本身是支持public的,只是gradle插件在对资源做预处理(merge)时对public做
了过滤。

2、<public-padding />

public 文件原本是用来固定id,用于rom更新后维持系统资源id不变。
用法有两种,
    1、<public />节点,指定单个id的值,
    2、<public-padding />节点,指定批量id的值

id分组使用的是<public-padding />。

3、PPTTNNNN之TT段分组

aapt编译资源时,自动生成id的格式是PPTTNNNN,而且对同一种类型的资源的id而言,生成id的PPTT段要相同,这
是前提。

基于这个前提,由于public事先指定了资源的id,也即指定了某类型资源的id的PPTT段。因此aapt在生成id时为了
保持同种类型资源id的格式一致性,会以public中预先指定的资源id的PPTT为标杆生成资源id。

也即可以通过public来指定某类型的资源Id以什么PPTT开头,达到资源id分组效果。

4、public指定PP段

public的PP段只能指定为7f,指定为其他值编译时会被aapt修正为7f。

5、public指定Attr的TT段

通过public指定PPTT时,attr的TT一定需要是所有类型中最小的,其他几种资源类型顺序可以随意。

6、插件主题和宿主主题继承关系。

插件中只能使用系统内置主题,或者继承自系统内置主题的插件自定义主题。

插件中不能使用宿主自定义主题,不能使用继承自宿主自定义主题的插件自定义主题,在插件中使用这些主题时,来
自于宿主的自定义部分会直接被忽略。

解决办法:
    思路基于:主题不是资源,或者说不是元资源,除主题以外的资源是元资源,而主题是元资源集。

    所以问题转换为:插件不能使用宿主定义的元资源集,只能使用插件自己定义的元资源集。

那么解决办法就是,在插件中把宿主中定义的元资源集(主题)重新定义一遍,只要重新定义的元资源集的和宿主中
定义的元资源集是完全一致的,那么在使用插件自定义主题时,效果上等同于使用了宿主主题,没有任何区别,甚至
如果重定义的主题名字保留和宿主中的主题名字一样,那么从插件代码成面上看都没有区别。

而主题只是元资源集,重定义主题并不会产生新资源, 因此对插件包的没有任何不良影响。

在插件中手动重新定义宿主主题显然不现实,特别是宿主比较复杂时,例如使用的AppCompat之后,来自于appcompat
中的style数量定义粗略估计应该破百了

好在可以通过脚本导出。在编译宿主时,通过脚本将build目录下的merge完成后的res中的values文件设计到style定
义的部分导出一份xml文件。

当然这里的导出脚本还涉及到需要对style中对宿主资源的引用修改为带*packageName的方式,这样才能附加到插件中
使用。

在编译插件时再通过脚本自动将宿主中导出的style相关的xml附加到编译目录下,完成对宿主主题的重定义,说是重定
义,其实就是copy。而且这个过程对插件而言是完全无感知的。然后插件的就可以对“宿主主题”随意使用和扩展了。

7、public实现分组导致declare-styleable不能识别

不确定通过PP段分组资源id是否也有这个问题,未尝试。

插件中使用宿主资源,需要在编译插件时通过-I参数, 追加宿主编译时生成的._ap文件到编译路径,这会导致在插件的
res中定义的declare-styleable不能识别。

解决办法:declare-styleable其实是用来定义属性集的。基于这个前提,直接通过脚本在编译时将插件中的
declare-styleable节点去掉,将节点下的attr移到declare-styleable节点外部即可。

这个做法是否会对其原始语义产生影响呢?结论是并不会,因为declare-styleable本身并不产生新资源。熟悉的味道。
它更像是语法糖一般的存在,增强代码可读性、书写便捷。

那么通过脚本压平了declare-styleable带来的潜在问题是有3个:
    1、代码可读性降低;这个问题其实不存在。脚本是编译时处理,插件中仍然保留有declare-styleable节点

    2、书写便捷;这个问题的原因是去掉了declare-styleable以后,aapt不会在R文件中生成相应的R.styleable.XXXX,
       以及 R.styleable.XXXX_YYYY对象(XXXX为原declare-styleable的name,YYYY为原declare-styleable下自
       定义attr的名字)
      
       解决办法:XXXX和XXXX_YYYY本质其实就是attr数组、以及attr在数组中的索引。只需通过脚本编辑aapt生成好的R
       文件,写入这些数组和索引即可。
    3、去掉declare-styleable会带来attr重复定义的问题。解决方法很简单,脚本去重。这里面还需要处理enum和flag。

好在这些问题都可以通过脚本解决,而且对插件而言是无感知的。    

8、基于attr的主题换肤功能

要实现基于attr的主题换肤功能,attr不能在public中通过TT段来分组, 而是需要通过NNNN段来分段实现分组,保证插件
和宿主的attr的PPTT段相同.

如果不考虑基于attr的主题换肤功能,则无此要求。

所谓基于attr的主题换肤功能是指:
    宿主中的xml布局文件中通过?attr/xxx的方式定义控件的属性,比如背景色。
    插件中定义一个主题保护attr/xxx,
    宿主的主题设置为插件中定义的主题,以达到attr/xxx随主题切换而变化,达到按主题换肤的效果。