Android studio Module依賴(lài) 出現(xiàn)Manifest Merge 沖突的詳細(xì)解決方案

安卓開(kāi)發(fā)使用 Gradle 插件管理依賴(lài)包確實(shí)非常方便,開(kāi)發(fā)中,你可能還會(huì)遇到一種情況,就是項(xiàng)目所引用的 AAR 、Library 等第三方庫(kù)所包含的 Manifest 清單文件與主 Module (默認(rèn)名為 app )中定義的 Manifest 內(nèi)容合并時(shí)發(fā)生沖突。

比如在項(xiàng)目中引用的某個(gè) Library 或者M(jìn)odule的 AndroidManifest 文件中,application 標(biāo)簽中內(nèi)容如下:

<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">

并且二者所使用的值不同。這樣,在編譯的時(shí)候就會(huì)發(fā)生合并沖突,錯(cuò)誤信息如下:

Error:Execution failed for task ':app:processDebugManifest'.
> Manifest merger failed with multiple errors, see logs

通過(guò)點(diǎn)擊 Messages 菜單左側(cè)【Show Console Output】選項(xiàng)可以打開(kāi) Gradle Console 控制臺(tái)查看錯(cuò)誤日志和對(duì)應(yīng)的解決方案:

Paste_Image.png
Paste_Image.png

如上圖所示,Library 與 主 Module 在合并 Manifest 時(shí)發(fā)生錯(cuò)誤。其中還包含看到具體錯(cuò)誤信息,注明了是 android:theme 屬性發(fā)生沖突。并且給出了建議的解決方案,使用 tools:replace 方式解決沖突。

我們就按照錯(cuò)誤提示在主 Module 的 Manifest 文件中添加這行設(shè)置試試看:(注意需要聲明 tools 命名空間)

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    package="com.xxx.xxx">

并且在 app module 中的 Manifest 內(nèi)容的 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:theme">

如此這樣,再次 Build 時(shí)便能編譯通過(guò)。其實(shí)原理就是,借助 tools 域名空間設(shè)置 Manifest 的合并優(yōu)先級(jí)問(wèn)題。這里 tools:place 表明合并時(shí)移除低優(yōu)先級(jí) Library 中的相關(guān)屬性,使用高優(yōu)先級(jí) app module 中定義的對(duì)應(yīng)屬性?xún)?nèi)容。

有關(guān) Manifest 合并相關(guān)的知識(shí),在開(kāi)發(fā)者官網(wǎng)上介紹得非常清楚,大家可以訪(fǎng)問(wèn)如下鏈接:

Merge Multiple Manifest Files

像上面這種情況,如果我們腦洞再開(kāi)大一點(diǎn),假設(shè)這個(gè) Library 也使用了 tools:replace 屬性會(huì)發(fā)生什么情況呢。我們不妨試驗(yàn)一下。修改 Library 的 Manifest 內(nèi)容:

<application 
    android:theme="@android:style/Theme.Black"
    tools:replace="android:theme"/>

而此時(shí) app module 中的 Manifest 內(nèi)容的 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">

這里,我故意設(shè)置二者的 tools:replace 屬性為不同值。Build 一下,看看結(jié)果:

Error:Execution failed for task ':app:processDebugManifest'.
> Multiple entries with same key: android:theme=REPLACE and android:theme=REPLACE

如我們所想,合并時(shí)發(fā)生沖突。然而,痛苦的是這種情況下,編譯器也無(wú)解,無(wú)法給出相應(yīng)解決方案!

Paste_Image.png
Paste_Image.png

當(dāng)然,這種情況很極端,但也不是沒(méi)有出現(xiàn)的可能。第三方庫(kù)和我們的 App Module 都想使用自己的屬性,也在情理之中。那么怎么辦呢,如果是本地 Library 依賴(lài)方式的話(huà),還可以手動(dòng)修改 Library 的 Manifest 內(nèi)容。但是如果是遠(yuǎn)程依賴(lài)的 AAR的話(huà),我們是改不了的啊。

這個(gè)時(shí)候,我們就得想辦法在合并的時(shí)候自動(dòng)刪除 Library 的 Manifest 內(nèi)容。但是很幸運(yùn) GitHub網(wǎng)站有一個(gè)插件,能夠幫助我們實(shí)現(xiàn)這個(gè)功能。先上地址:
https://github.com/2BAB/Seal

這個(gè)插件可以幫助我們做到這些:

1、刪除 Application 節(jié)點(diǎn)中的指定屬性;
2、刪除 Application 節(jié)點(diǎn)中 tools:replace 屬性的指定值。
這里我們還用上面的例子,介紹一下 Seal 插件的使用方式。

首先在項(xiàng)目根目錄下的 build.gradle 文件中設(shè)置 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 文件中引用這個(gè)插件:

apply plugin: 'com.android.application'
apply plugin: 'seal'

接著還是修改這個(gè) build.gradle 文件,配置合并時(shí)的刪除規(guī)則:

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 配置下添加自己項(xiàng)目引入并發(fā)生沖突的 Library 名字,例子中使用的是 samplelibrary。同時(shí)在
removeAttrs 和 replaceValues 配置下添加對(duì)應(yīng)沖突的屬性名字,例子中沖突的是 android:theme 屬性。

還有一點(diǎn)需要注意的是,如果 Gradle 插件開(kāi)啟了 build-cache 功能(Gradle 插件 2.3 版本開(kāi)始默認(rèn)開(kāi)啟),還需要在項(xiàng)目根目錄下的 gradle.properties 文件中添加如下內(nèi)容:

android.buildCacheDir=./build-cache

我們?cè)俅?Build 工程,就能成功編譯通過(guò)啦。

當(dāng)然,真實(shí)項(xiàng)目中 Manifest 合并時(shí)能遇到 tools 規(guī)則沖突的情況并不多見(jiàn),而更多的是,普通屬性的使用沖突。不過(guò),還是值得注意一下,以備不時(shí)之需。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀(guān)點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容