Android 组件资源覆盖冲突解决方案

项目地址:https://github.com/hust201010701/CheckResourceConflicthtml

Download

1、 引言

在 Android 的平常开发中,咱们会使用到大量的第三方库或者本身编写的组件库,这些依赖库中资源加上主工程自己的资源,可能会发生同名冲突,会发生资源相互覆盖的现象。java

因为资源覆盖不会有任何提示,并且只会在 APP 运行到相关代码时暴露出来,若是测试不细致的话,很容易把问题带到线上,形成严重后果。在个人业务开发过程当中,就发生过两起因为资源覆盖致使的实际问题:android

  1. 颜色资源被覆盖

咱们有两款 APP 分别记做 A 和 B,它们同时依赖了一个对话框组件记做 C,C中有个颜色资源名为 @color/main_color,B 项目在主工程中也同时声明了一个同名的颜色,可是资源的值与组件 C 中不同,最终致使 B 项目中的对话框显示效果异常。git

  1. 布局资源被覆盖

若是你认为颜色资源被覆盖最多只影响显示效果,并不算严重的话,那么再来看这个布局资源被覆盖的问题,它是有可能引发崩溃的。 咱们有个已有的业务,相关程序都在主工程中,因为一些需求,须要把这个业务相关的代码迁移到一个独立的组件库中,咱们在迁移的过程当中,有些自定义View页跟随迁移了。可是因为粗心缘由,主工程的原有资源并无删除。当咱们使用布局资源时,主工程中的 layout 布局文件会覆盖组件中的布局文件,当咱们在 Java 文件中使用 findViewById 去绑定 id 时,布局中的 View 的包名是老包名,而java 文件中的 View 的包名是新包名,在运行时就会发生类型转换失败的崩溃。举个例子,主工程中有个自定义控件 package.a.view.CircleWithBorderView,迁移到 A 组件中后包名发生变化,变成了 package.b.view.CircleWithBorderView,在组件 A 某个布局 activity_main.xml 中使用到了这个控件,并且对应的java文件中,咱们使用的都是 package.b.view.CircleWithBorderView 包下的自定义控件,没有任何问题。可是在接入主工程时,因为主工程的 activity_main.xml 并无删除,而主工程中 activity_main.xml 使用的 package.a.view.CircleWithBorderView 下的自定义控件,这样在运行时就会发生崩溃现象。github

<package.a.view.CircleWithBorderView
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"/>
复制代码

在了解同名资源被覆盖,会产生一些难以及时发现的问题时,咱们须要思考如何避免这种问题的产生。浏览器

2、 解决思路

若是咱们可以在编译期间,把全部冲突的资源找出来,告诉开发者有哪些资源同名可是内容不一样,让开发者在最终发版前将这些同名资源处理掉。处理的方式能够有两种:bash

  1. 若是冲突的资源应该保持一致,能够将其中一份资源删除,或者保持资源内容一致;
  2. 若是冲突的资源自己就不该该一致,那么能够将其中一个资源增长前缀防止资源被覆盖。

资源类型

资源按照类型来区分,能够分为两种,区分的规则是文件类型整个文件占用一个 R.id,而值类型的资源每一项元素占用一个 R.idapp

  • 值类型:好比 @color/black, @dimen/screen_width 等对应xml文件中声明的每一项子元素
  • 文件类型: 好比 drawable-xxhdpi 文件夹下的图片资源,layout 文件夹下的布局文件

如何区分资源类型

那程序须要怎么区分某个文件是不是值类型仍是文件类型呢?maven

咱们能够根据这个这个文件所在目录的目录名是否 values 或者 以values-开头进行判断。布局

如何判断资源冲突

要判断一个资源存在冲突,咱们须要处理两件事:一是给资源肯定惟一的id,二是如何判断资源的值是否发生冲突;

资源肯定惟一的id

对于文件资源,举个例子, res/drawable-xxhdpi/a.pngres/drawable-xxxhdpi/a.png 即便文件不一样也不会发生冲突,可是 A 组件中的 res/drawable-xxhdpi/a.png 和 B 组件中的 res/drawable-xxhdpi/a.png 会发生冲突,因此对于文件资源,惟一id能够肯定为 file@文件所在文件夹名称/文件名, 如file@layout/activity_main.xml

对于值类型的资源,举个例子, res/values/colors.xmlres/values/values.xml 中声明的 main_color 会发生冲突,而 res/values/colors.xmlres/values-v19/colors.xml 中的 main_color 不会发生冲突,因此对于值类型资源,与所在文件名无关,惟一id能够肯定为 alue@资源所在文件的上层文件夹名称/资源名, 如value@values/main_color

资源值是否冲突

对于文件类型资源,咱们能够计算文件对于的md5是否相等来判断是否冲突; 对于值类型资源,咱们能够直接比较值的内容是否同样。

3、解决方案

咱们的方案已经很清晰了。万事具有只欠东风,只须要在编译期间可以获取全部的资源文件就能够了。

Android Gradle Plugin 3.3 版本及其以上,提供了一个 API 能够获取编译全部的资源文件。

variants.forEach { variant ->
    variant as BaseVariantImpl
    // files 即对应全部的编译资源
    def files = variant.allRawAndroidResources.files
复制代码

我使用这套 API 开发了一套编译期间自动查找全部冲突资源的插件,项目地址: CheckResourceConflict github.com/hust2010107…

4、CheckResourceConflict 插件

4.1 特性介绍

  • 支持全部res文件夹下资源:文件类型+值类型
  • 资源冲突输出html格式树形图
  • 支持指定html文件输出目录
  • 支持资源白名单,即便冲突也不记录
  • 检测完成后支持邮件通知

4.2 使用方法

1. 添加依赖

buildscript {
    repositories {
        jcenter()
    }
    dependencies {
        classpath 'com.orzangleli:checkresourceconflict:0.0.1'
    }
}
复制代码

2. 使用插件

在你的项目的 build.gradle 文件中增长:

apply plugin: 'CheckResourcePrefixPlugin'

3. 运行任务

有两种方式能够运行资源冲突检测任务

  • 添加插件后,会在 check 目录下生产若干个任务,能够根据须要执行相关任务,

  • 在编译apk的过程当中也会自动执行相应的任务

4. 可配置项

checkResourceConfig {
    // 是否开启插件
    enabled false
    // 运行完后自动使用默认浏览器打开html结果进行预览
    autoPreviewResult true
    // 输出文档的目录
    outputDir "./out"
    // 资源冲突白名单
    whiteListFile "../checkResource/whitelist.lxc"
    // 邮件相关配置
    emailConfig {
        // 是否开启邮件发送功能
        needSendEmail true
        // 邮箱指定的 email host
        host ""
        // 发件人邮箱
        fromEmail ""
        // 收件人邮箱 支持多人
        toEmailList ("", "")
        // 邮箱帐号
        account ""
        // 邮箱受权第三方客户端的受权码
        authorizationCode ""
    }
}
复制代码

5. 白名单规则

# 「#」开头表示该行为注释
# 这个文件能够声明检测资源冲突的白名单资源
# 文件资源的格式为 file@文件所在文件夹名称/文件名, 如 file@layout/activity_main.xml
# 值类型资源的格式为 value@资源所在文件的上层文件夹名称/资源名, 如 value@values/colorPrimary

value@values/error_color_material_dark
file@layout/notification_action_tombstone.xml
复制代码

其中 value@values/error_color_material_darkfile@layout/notification_action_tombstone.xml 表示的是资源id,能够从输出的html文档中找到资源对应的id。

6. 注意事项

  • 不支持 assets 中资源冲突检测
  • 不支持 android gradle plugin 3.3 如下版本

7. 使用效果

Tips:

  • 已经处理过的冲突可使用勾选框标记。
  • 邮箱中html预览功能没法正常显示,请下载后预览本地文件

相关文章
相关标签/搜索