安卓開發使用 Gradle 外掛管理依賴包確實非常方便,尤其是在解決一些依賴衝突的問題上。比如,重複依賴的問題,具體內容請我之前寫的一篇文章:
開發中,你可能還會遇到一種情況,就是專案所引用的 AAR 、Library 等第三方庫所包含的 Manifest 清單檔案與主 Module (預設名為 app )中定義的 Manifest 內容合併時發生衝突。
舉個例子。比如在專案中引用的某個 Library 的 AndroidManifest 檔案中,application 標籤中內容如下:
<application
android:theme="@android:style/Theme.Black"/>複製程式碼
其中的 android:theme
屬性在我們的 app module 主工程的 AndroidManifest 檔案中也被定義:
<application
android:allowBackup="false"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/AppTheme">複製程式碼
並且二者所使用的值不同。這樣,在編譯的時候就會發生合併衝突,錯誤資訊如下:
Error:Execution failed for task ':app:processDebugManifest'.
> Manifest merger failed with multiple errors, see logs複製程式碼
通過點選 Messages 選單左側【Show Console Output】選項可以開啟 Gradle Console 控制檯檢視錯誤日誌和對應的解決方案:
如上圖所示,Library 與 主 Module 在合併 Manifest 時發生錯誤。其中還包含看到具體錯誤資訊,註明了是 android:theme
屬性發生衝突。並且給出了建議的解決方案,使用 tools:replace
方式解決衝突。
我們就按照錯誤提示在主 Module 的 Manifest 檔案中新增這行設定試試看:(注意需要宣告 tools 名稱空間)
<application
android:allowBackup="false"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/AppTheme"
tools:replace="android:theme">複製程式碼
如此這樣,再次 Build 時便能編譯通過。其實原理就是,藉助 tools 域名空間設定 Manifest 的合併優先順序問題。這裡 tools:place
表明合併時移除低優先順序 Library 中的相關屬性,使用高優先順序 app module 中定義的對應屬性內容。
有關 Manifest 合併相關的知識,在開發者官網上介紹得非常清楚,大家可以訪問如下連結:
你以為這樣就結束了嗎,非也,還有一種極端情況。像上面這種情況,如果我們腦洞再開大一點,假設這個 Library 也使用了 tools:replace
屬性會發生什麼情況呢。我們不妨試驗一下。修改 Library 的 Manifest 內容:
<application
android:theme="@android:style/Theme.Black"
tools:replace="android:theme"/>複製程式碼
而此時 app module 中的 Manifest 內容的 tools:replace
屬性也做了一些修改:
<application
android:allowBackup="false"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/AppTheme"
tools:replace="android:allowBackup, android:theme">複製程式碼
這裡,我故意設定二者的 tools:replace 屬性為不同值。Build 一下,看看結果:
Error:Execution failed for task ':app:processDebugManifest'.
> Multiple entries with same key: android:theme=REPLACE and android:theme=REPLACE複製程式碼
如我們所想,合併時發生衝突。然而,痛苦的是這種情況下,編譯器也無解,無法給出相應解決方案!
當然,這種情況很極端,但也不是沒有出現的可能。第三方庫和我們的 App Module 都想使用自己的屬性,也在情理之中。那麼怎麼辦呢,如果是本地 Library 依賴方式的話,還可以手動修改 Library 的 Manifest 內容。但是如果是遠端依賴的 AAR的話,我們是改不了的啊。
這個時候,我們就得想辦法在合併的時候自動刪除 Library 的 Manifest 內容。偉大的 GitHub網站有一個外掛,能夠幫助我們實現這個功能。先上地址:
這個外掛可以幫助我們做到這些:
- 刪除 Application 節點中的指定屬性;
- 刪除 Application 節點中
tools:replace
屬性的指定值。
這裡我們還用上面的例子,介紹一下 Seal 外掛的使用方式。
首先在專案根目錄下的 build.gradle 檔案中設定 Seal 外掛的地址:
buildscript {
repositories {
jcenter()
}
dependencies {
classpath 'com.android.tools.build:gradle:2.3.0'
classpath 'me.xx2bab.gradle:seal-manifest-precheck-plugin:1.0.0'
}
}複製程式碼
然後在 app module 的 build.gradle 檔案中引用這個外掛:
apply plugin: 'com.android.application'
apply plugin: 'seal'複製程式碼
接著還是修改這個 build.gradle 檔案,配置合併時的刪除規則:
def projectRoot = project.getRootProject().rootDir.absolutePath
// Folders may include AndroidManifest.xml files
// 1. For gradle plugin 2.3.0 or higher, build-cache is default choice,
// 2. But we should make sure snapshot-libs will be checked too.
// 3. Free to add your folders for more customization
def manifestPath = [
// for AAR of Release
// see note below
projectRoot + '/build-cache',
projectRoot + '/samplelibrary',
// for AAR of SNAPSHOT
projectRoot + '/app/build/intermediates/exploded-aar'
]
def removeAttrs = [
'android:theme'
]
def replaceValues = [
'android:theme'
]
seal {
enabled = true
manifests = manifestPath
appAttrs {
enabled = true
attrsShouldRemove = removeAttrs
}
appReplaceValues {
enabled = true
valuesShouldRemove = replaceValues
}
}複製程式碼
注意,在 manifestPath 配置下新增自己專案引入併發生衝突的 Library 名字,例子中使用的是 samplelibrary。同時在
removeAttrs 和 replaceValues 配置下新增對應衝突的屬性名字,例子中衝突的是 android:theme
屬性。
還有一點需要注意的是,如果 Gradle 外掛開啟了 build-cache 功能(Gradle 外掛 2.3 版本開始預設開啟),還需要在專案根目錄下的 gradle.properties 檔案中新增如下內容:
android.buildCacheDir=./build-cache複製程式碼
這些工作都做完之後,我們再次 Build 工程,就能成功編譯通過啦。
當然,真實專案中 Manifest 合併時能遇到 tools 規則衝突的情況並不多見,而更多的是,普通屬性的使用衝突。不過,還是值得注意一下,以備不時之需。
關於我:亦楓,部落格地址:yifeng.studio/,新浪微博:IT亦楓
微信掃描二維碼,歡迎關注我的個人公眾號:安卓筆記俠
不僅分享我的原創技術文章,還有程式設計師的職場遐想