组件化路上之二 Android 资源重名问题解决

0. 具体源码与插件请参考:

https://github.com/zhaoyubetter/AndroidResourceTools

1. Android 分模块开发资源重名问题

因为Android中资源名称不能重复,一般在另一个module开始时,我们一般会在此module对应的

gradle文件中,添加一个资源前缀,但这个并不能帮我们重命名资源自动添加前缀,他实现的只是一个

新建资源时,提示你名称必须添加指定的前缀;

如下:

icon_app_drawable.png
添加前缀后:
moduleName_icon_app_drawable.png

通过模块名,并以此模块名为前缀,为该模块内的所有资源统一添加前缀,并替换所有该资源出现的位置,来实现资源名称唯一;

但我们在module开发中,新增资源时,经常忘记添加资源名的前缀,直到集成到主app时,才会意识到

冲突问题,这个时候,手动去改,麻烦且容易出错;

1.1 如何解决此问题

https://developer.android.com/guide/topics/resources/available-resources

通过分析Android工程,分析得出Android App 工程的资源分为2大类:

  • 文件夹形式资源,即每个资源在改特定的文件夹下,该类型文件夹大约如下:

    • layout
    • drawable
    • anim
    • color
    • menu
    • raw
    • xml
    • mipmap
  • values 类型资源,即在values文件夹下,除了id可重用,其他目前都不允许重用,这些大约有如下:

    • string
    • arrays
    • color
    • dimens
    • bool
    • integer
    • id
    • attr
    • style

1.2 发现资源规律

以上资源内容,会出现在2个地方,一是java源代码中,二是资源间的引用,整理表格如下:

资源类型 源代码中 xml引用中 备注
layout R.layout.XXX @layout/XXX 布局文件
drawable R.drawable.XXX @drawable/XXX
anim R.anim.XXX @anim/XXX
color R.color.XXX @color/XXX
mipmap R.mipmap.XXX @mipmap.XXX
menu R.menu.XXX Xml 暂无引用
raw R.raw.XXX Xml 暂无引用
xml R.xml.XXX Xml 暂无引用
string R.string.XXX @string/XXX
color R.color.XXX @color/XXX
dimens R.dimen.XXX @dimen/XXX
arrays R.array.XXX @array/XXX
style R.style.XXX @style/XXX 需要考虑到parent,比较复杂
attr R.attr.XXX 需要特殊处理,主要在自定义属性上

从上表格看,大部分资源都是有规律的,也就是我们可以读取源代码文件,与xml文件,来进行整体资源的重命名与整体替换;

Tips: attr 与 style 有些特殊,需要特殊处理;

1.3 资源重名替换的风险性

  1. 如何保证原子性,即:要么文件全部替换成功,要么全部不替换;

    目前没有找到合适方案;

  2. 如何保证不替换公共库资源,比如:当前模块中a,引用了公共库的资源 common.png,如何保证

    common.png 不被重命名成 a_common.png

    解决:只替换本模块中声明的资源,也就是只读取本模块中res/下定义的资源;

  3. attrsstyle 资源的特殊性

    这2块资源,可能用的不是很多,暂时可以先手动修改一下;麻烦在以下原因:

    因为style可能会是继承下来的,所以这块替换容易出问题,后期再考虑;

    attrs同样如此,主要用来自定义控件属性这块;后期再考虑;

1.4 方案可行性

如果暂不考虑批量文件替换,重命名事务性问题,通过上面的分析,我们得知,如果要进行资源重命名,我们需要通过程序检索出某资源出现在该模块位置,然后替换之:

因为源代码与xml文件都是文本文件内容,所以我们可以通过正则全工程检索,然后将用到资源按顺序分别重命名;然后替换到现有文件;从而达到目的;

比如 strings 字符串资源:

  1. 资源名(资源定义处, xml 中定义):

    <string name="default_loading">正在加载…</string>
    
  2. 源代码中(引用,源代码包括Java与Kt代码):

    String s = getResources().getString(R.string.default_loading);
    
  3. xml中引用

    <TextView
        android:text="@string/default_loading"
        ...
        />
    

假设前缀为:common_ui_, 那么资源default_loading 出现的所有位置,应该替换成:

  1. 资源名(资源定义处):

    <string name="common_ui_default_loading">正在加载…</string>
    
  2. 源代码中(引用):

    String s = getResources().getString(R.string.common_ui_default_loading);
    
  3. xml中引用:

    <TextView
        android:text="@string/common_ui_default_loading"
        .../>
    

这样就完成了一个string类型资源的替换,类似的,可用这个方式替换其他资源;

2.具体实现

有了上面的分析,我们就可以开始动手设计了,主要用到的是正则相关的知识,不得不说,正则是一把锋利的瑞士军刀,关键时候,能够大显身手;

2.1 类图设计

因为资源分类2大类(独立文件夹与values形式),但是这2大类资源都可能会出现3个地方:

  1. 定义处;
  2. 源代码中;
  3. 资源xml引用中;

设计的整体类图如下:

[图片上传失败...(image-21c97e-1539049496327)]

工作过程简要说明:

  1. 文件夹形式为了更好的维护与扩展,将每一种的资源独立形成一个类,继承自BaseFolderResReplace, 比如layout ,分为3步走:

    a. 先实现自己的正则表达式;

    b. 利用基类中的方法,进行全局替换;

    c. 替换后完,需要将资源的文件名重名也就是调用 renameFile

  2. values形式资源也就是values文件夹,继承自BaseReplace,操作与上类似,这里面不需要修改文件名;

2.2 详细设计

考虑到需要经常操作xml文件,而groovy内置了强大的xml文件处理能力,因为大部分实现采用groovy来编码,

同时 groovy也是实现gradle的灵魂语言,这就为此模块做成gralde插件提供了保证.

以 layout 为例

源代码如下:

// layout resource
public class LayoutReplace extends BaseFolderResReplace {
    // filter
    private def final DIR_FILTER = new Tools.DirNamePrefixFilter("layout")
    private def final RES_TYPE_NAME = "layout"

    LayoutReplace(Object srcFolderPath, Object resFolderPath) {
        super(srcFolderPath, resFolderPath)
    }

    @Override
    String getResTypeName() {
        return RES_TYPE_NAME
    }

    @Override
    String getJavaRegex() {
        // group 6 为名字
        return "(R(\\s*?)\\.(\\s*?)layout(\\s*?)\\.(\\s*?))(\\w+)"
    }

    @Override
    String getXmlRegex() {
        // group 2为名字
        return "(@layout/)(\\w+)"
    }

    @Override
    Set<String> getResNameSet() {
        Set<String> layoutNameSet = new HashSet<>()
        // 1.获取所有layout开头的文件夹
        File[] layoutDirs = resDir.listFiles(DIR_FILTER)
        // 2.获取layout名字并存储
        layoutDirs?.each { layoutDir ->
            layoutDir.eachFile { it ->
                layoutNameSet.add(it.name.substring(0, it.name.lastIndexOf(".")))
            }
        }
        return layoutNameSet
    }

    @Override
    void replaceSrc(Set<String> resNameSet, java_regx) throws IOException {
        println("---------- layout ----- 替换源代码目录开始")
        replaceSrcDir(srcDir, resNameSet, java_regx)
        println("---------- layout ----- 替换源代码目录结束")
    }

    @Override
    void replaceRes(Set<String> resNameSet, xml_regx) throws IOException {
        println("---------- layout ----- 替换资源目录开始")

        // 1.替换文件内容
        replaceResDir(resDir, resNameSet, xml_regx, DIR_FILTER)
        // 2.修改文件名
        renameFile(resDir, DIR_FILTER, RES_TYPE_NAME)

        println("---------- layout ----- 替换资源目录结束")
    }
}

3. TODO

  1. 文件的事务操作有待支持;
  2. Attrs 有待支持;
  3. style有待支持;
  4. 其他资源类型有待支持;
  5. 如果资源名称与Android自带资源名重复时,会发生替换问题,此问题,有待修正;
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 199,393评论 5 467
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 83,790评论 2 376
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 146,391评论 0 330
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 53,703评论 1 270
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 62,613评论 5 359
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,003评论 1 275
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,507评论 3 390
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,158评论 0 254
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,300评论 1 294
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,256评论 2 317
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,274评论 1 328
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,984评论 3 316
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,569评论 3 303
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,662评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,899评论 1 255
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,268评论 2 345
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 41,840评论 2 339

推荐阅读更多精彩内容

  • Android 自定义View的各种姿势1 Activity的显示之ViewRootImpl详解 Activity...
    passiontim阅读 171,259评论 25 707
  • 用两张图告诉你,为什么你的 App 会卡顿? - Android - 掘金 Cover 有什么料? 从这篇文章中你...
    hw1212阅读 12,672评论 2 59
  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,559评论 18 139
  • 【阳光男孩 张文哲 8月28日 晴 坚持原创分享第301天】 更好玩的是,还有一条斑马条纹的巧克力呢,它的...
    张文哲阅读 490评论 4 12
  • 最近我对一位资深程序员就工作生活问题进行了深度采访,采访内容如下: Q:请问你工作多久了? A:5年了 Q:你觉得...
    陈序元阅读 2,135评论 0 3