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預覽功能沒法正常顯示,請下載後預覽本地文件

相關文章
相關標籤/搜索