天天看點

Gradle 翻譯 Merge AndroidManifest 合并清單檔案 [MD]

博文位址

我的GitHub 我的部落格 我的微信 我的郵箱
baiqiantao bqt20094 [email protected]

目錄

  • Merge AndroidManifest 合并清單檔案
    • 合并多個清單檔案
    • 合并優先級
    • 合并沖突啟發式算法
    • 合并規則的标記
      • 節點标記
        • merge 合并所有
        • merge-only-attributes 僅合并屬性
        • replace 替換【常用】
        • remove 移除
        • removeAll 移除所有
        • strict 嚴格模式
      • 屬性标記 Attribute markers
        • remove 移除【常用】
        • replace 替換【最常用】
        • 同時使用多個屬性标記
      • 标記選擇器
      • 替換導入庫的 uses-sdk
      • 隐式系統權限
    • 檢查合并清單并查找沖突
    • 附錄:合并政策

官方文檔

Merge multiple manifest files

APK 檔案隻能包含一個

AndroidManifest.xml

檔案,但 Android Studio 項目可以包含多個檔案[may contain several provided by the

main source set

,

build variants

, and

imported libraries

]。是以,在建構應用時,Gradle 建構會将所有清單檔案合并到一個封裝到 APK 的清單檔案中[merges all manifest files into a single manifest file that's packaged into your APK]。

清單合并工具通過遵循某些合并啟發式算法[merge heuristics],并遵守您通過特殊 XML 屬性定義的合并首選項[merge preferences],來合并各個檔案中的所有 XML 元素 。 本頁介紹清單合并的工作方式以及如何應用合并首選項來解決合并沖突[resolve merge conflicts]。

提示: 使用 Merged Manifest 視圖預覽合并清單的效果并找出沖突錯誤。

Merge priorities

合并工具根據每個清單檔案的優先級将所有清單檔案

按順序

合并到一個檔案中。 例如,如果您有 3 個清單檔案,則會先将優先級最低的清單合并到優先級第 2 高的清單中,然後再将合并後的清單合并到優先級最高的清單中(如圖 1 所示)。

Gradle 翻譯 Merge AndroidManifest 合并清單檔案 [MD]

有 3 種基本的清單檔案可以互相合并,它們的合并優先級如下(按優先級由高到低的順序):

1、清單檔案建構變體 Manifest file for your build variant

如果您的變體有多個源集,則其manifest優先級如下:

  • Build variant

    manifest (如 src/demoDebug/)
  • Build type

    manifest (如 src/debug/)
  • Product flavor

    manifest (如 src/demo/)

如果您使用的是 flavor dimensions,清單優先級将與每個次元在 flavorDimensions 屬性中的列示順序(按優先級由高到低的順序排列)對應。

2、app子產品的主清單檔案 Main manifest file for the app module

3、所包括庫中的清單檔案 Manifest file from an included library

如果您有多個庫,則其清單優先級與依賴順序(庫出現在 dependencies 塊中的順序)比對。

重要說明: build.gradle 檔案中的建構配置将替換合并清單檔案中的任何對應屬性。 例如,build.gradle 檔案中的minSdkVersion 将替換 清單元素中的比對屬性。為了避免混淆,您隻需省去 元素并在 build.gradle 檔案中定義這些屬性。For more details, see Configure Your Build.

Merge conflict heuristics

合并工具可以在邏輯上将一個清單中的每個 XML 元素與另一個清單中的對應元素相比對。(有關比對如何進行的詳細資訊,請參閱有關 合并政策 的附錄)。

如果優先級較低的清單中的元素與優先級較高的清單中的任何元素均不比對,則該元素将被添加至合并清單。 但是,如果有比對元素,則合并工具會嘗試将其中的所有屬性合并到相同元素中。如果工具發現

兩個清單包含相同屬性,但值不相同

,則會出現合并沖突。

下表 1 描述了合并工具嘗試将所有屬性合并到同一進制素時可能出現的結果。

高優先級屬性 低優先級屬性 屬性的合并結果
沒有值 沒有值(使用預設值)
值 B
值 A
沖突錯誤—必須添加一個 合并規則标記

但是,在某些情況下,合并工具會采取其他行為方式以避免合并沖突:

  • <manifest>

    元素中的屬性不合并--僅使用

    優先級最高的清單中的屬性

  • <uses-feature>

    <uses-library>

    元素中的

    android:required

    屬性使用 OR 合并,是以如果出現沖突(值不一緻),系統将應用 "true" 并始終包括某個清單所需的功能或庫[the feature or library required by one manifest is always included]。
  • <uses-sdk>

    元素始終使用

    優先級較高的清單中的值

    ,但以下情況除外:
    • 如果低優先級清單的

      minSdkVersion

      值較高,您必須應用

      overrideLibrary

      合并規則[an error occurs unless you apply the overrideLibrary merge rule]。
    • targetSdkVersion

      值較低,合并工具将使用高優先級清單中的值,但

      也會添加任何必要的系統權限

      ,以確定所導入的庫繼續正常工作(适用于較高的 Android 版本具有更多權限限制的情況)。 如需了解有關此行為的詳細資訊,請參閱有關 隐式系統權限[implicit system permissions] 的部分。
  • 絕不會在清單之間比對

    <intent-filter>

    元素。每個元素都被視為唯一進制素,并添加至合并清單中的常用父元素[the common parent element in the merged manifest]。

對于屬性之間的所有其他沖突,您将收到一則錯誤,并且必須通過

在高優先級清單檔案中添加特殊屬性

來訓示合并工具如何解決此錯誤。

不要依賴于預設屬性值 Do not depend on default attribute values

由于所有唯一屬性[unique attributes]都合并到相同元素中,如果高優先級清單實際上依賴于屬性的預設值而不需要聲明,則可能會導緻意外結果。

例如,如果高優先級清單不聲明

android:launchMod

e 屬性,則會使用 "standard" 的預設值;但如果低優先級清單聲明此屬性具有其他值,該值将應用于合并清單(替代預設值)。

是以,您應該按期望明确定義每個屬性。(每個屬性的預設值都會記錄在 Manifest reference 中)。

Merge rule markers

合并規則标記是一個 XML 屬性,可用于表達您對關于如何解決合并沖突或删除不需要的元素和屬性的首選項。您可以對整個元素或隻對元素中的特定屬性應用标記。

合并兩個清單檔案時,合并工具會

在高優先級清單檔案中尋找這些标記

所有标記均屬于 Android tools 命名空間,是以您必須先在

<manifest>

元素中聲明此命名空間,如下文所示:

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

Node markers

要向整個 XML 元素(給定清單元素中的所有元素及其所有子标記)應用合并規則,請使用以下屬性:

tools:node="merge"
           

如果使用

合并沖突啟發式算法

時沒有沖突,則

合并

此标記中的

所有屬性以及所有嵌套元素

。這是元素的預設行為。

低優先級清單:

<activity android:name=”com.example.ActivityOne”
    android:windowSoftInputMode=”stateUnchanged”>
    <intent-filter>
        <action android:name="android.intent.action.SEND" />
        <category android:name="android.intent.category.DEFAULT" />
    </intent-filter>
</activity>
           

高優先級清單:

<activity android:name=”com.example.ActivityOne”
    android:screenOrientation=”portrait”
    tools:node="merge”>
</activity>
           

合并的清單結果:

<activity android:name=”com.example.ActivityOne”
    android:screenOrientation=”portrait”
    android:windowSoftInputMode=”stateUnchanged”>
    <intent-filter>
        <action android:name="android.intent.action.SEND" />
        <category android:name="android.intent.category.DEFAULT" />
    </intent-filter>
</activity>
           

僅合并此标記中的屬性,

不合并嵌套元素

<activity android:name=”com.example.ActivityOne”
    android:windowSoftInputMode=”stateUnchanged”>
    <intent-filter>
        <action android:name="android.intent.action.SEND"/>
        <data android:type="image/*" />
        <category android:name="android.intent.category.DEFAULT"/>
    </intent-filter>
</activity>
           
<activity android:name=”com.example.ActivityOne”
    android:screenOrientation=”portrait”
    tools:node="merge-only-attributes”/>
           
<activity android:name=”com.example.ActivityOne”
    android:screenOrientation=”portrait”
    android:windowSoftInputMode=”stateUnchanged”/>
           

完全替換低優先級元素。 也就是說,如果低優先級清單中有比對元素,請将其忽略并完全按照其在此清單中顯示樣子來使用該元素。

<activity-alias android:name=”com.example.alias”>
  <meta-data android:name=”cow” android:value=”@string/moo”/>
  <meta-data android:name=”duck” android:value=”@string/quack”/>
</activity-alias>
           
<activity-alias android:name=”com.example.alias” tools:node=”replace”>
  <meta-data android:name=”fox” android:value=”@string/dingeringeding”/>
</activity-alias>
           
<activity-alias android:name=”com.example.alias”>
  <meta-data android:name=”fox” android:value=”@string/dingeringeding”/>
</activity-alias>
           

從合并清單中删除此元素。 盡管您似乎應該僅删除此元素,但如果您發現合并清單中有不需要的元素,則必須使用此選項。該選項由不受您控制的低優先級清單(如導入的庫)提供。

<activity-alias android:name=”com.example.alias”>
  <meta-data android:name=”cow” android:value=”@string/moo”/>
  <meta-data android:name=”duck” android:value=”@string/quack”/>
</activity-alias>
           
<activity-alias android:name=”com.example.alias”>
  <meta-data android:name=”cow” tools:node=”remove”/>
</activity-alias>
           
<activity-alias android:name=”com.example.alias”>
  <meta-data android:name=”duck” android:value=”@string/quack”/>
</activity-alias>
           

tools:node="remove"

類似,但它會删除同一父元素内與此元素

類型相比對

的所有元素。

<activity-alias android:name=”com.example.alias”>
  <meta-data android:name=”cow” android:value=”@string/moo”/>
  <meta-data android:name=”duck” android:value=”@string/quack”/>
</activity-alias>
           
<activity-alias android:name=”com.example.alias”>
  <meta-data tools:node=”removeAll”/>
</activity-alias>
           
<activity-alias android:name=”com.example.alias”/>
           

當此元素在低優先級清單中的情況與在高優先級清單中的情況不完全比對時生成建構故障(除非已認證其他合并規則标記解決)。 這将替換預設的合并沖突啟發式算法。 例如,如果低優先級清單僅包括額外屬性[an extra attribute],則建構将會失敗(而預設行為會向合并清單添加額外屬性)。

<activity android:name=”com.example.ActivityOne”
    android:windowSoftInputMode=”stateUnchanged”>
    <intent-filter>
        <action android:name="android.intent.action.SEND"/>
        <category android:name="android.intent.category.DEFAULT"/>
    </intent-filter>
</activity>
           
<activity android:name=”com.example.ActivityOne”
    android:screenOrientation=”portrait”
    tools:node="strict”/>
           

這會生成清單合并錯誤。兩個清單元素在嚴格模式下也完全無法區分。 是以,您必須應用其他合并規則标記來解決這些差異。

要改為僅向清單标記中的 特定屬性[specific attributes] 應用合并規則,請使用屬性标記。每個屬性接受一個或多個屬性名稱(包括屬性命名空間),并以逗号分隔。

從合并清單中删除指定屬性。盡管您似乎可以僅删除這些屬性,但如果低優先級清單檔案不包括這些屬性,而且您希望確定它們不納入合并清單,則必須使用此選項。

<activity android:name=”com.example.ActivityOne” 
    android:windowSoftInputMode=”stateUnchanged”/>
           
<activity android:name=”com.example.ActivityOne”
    android:screenOrientation=”portrait”
    tools:remove=”android:windowSoftInputMode”/>
           
<activity android:name=”com.example.ActivityOne”
    android:screenOrientation=”portrait”/>
           

将低優先級清單中的指定屬性替換為此清單中的屬性。換言之,始終保持高優先級清單的值。

<activity android:name=”com.example.ActivityOne”
    android:theme=”@oldtheme”
    android:exported=”false”
    android:windowSoftInputMode=”stateUnchanged”>
           
<activity android:name=”com.example.ActivityOne”
    android:theme=”@newtheme”
    android:exported=”true”
    android:screenOrientation=”portrait”
    tools:replace=”android:theme,android:exported”>
           
<activity android:name=”com.example.ActivityOne”
    android:theme=”@newtheme”
    android:exported=”true”
    android:screenOrientation=”portrait”
    android:windowSoftInputMode=”stateUnchanged”>
           

當這些屬性在低優先級清單中的情況與在高優先級清單中的不完全比對時生成建構故障。 這是所有屬性的預設行為,具有 合并沖突啟發式算法中介紹的特殊行為的屬性除外。

<activity android:name=”com.example.ActivityOne”
    android:screenOrientation=”landscape”/>
           
<activity android:name=”com.example.ActivityOne”
    android:screenOrientation=”portrait”
    tools:strict="android:screenOrientation”/>
           

這會生成清單合并錯誤。 您必須應用其他合并規則标記來解決沖突。

請謹記:這是預設行為,是以如果您删除

tools:strict="screenOrientation”

,上面的示例将具有相同的結果。

您也可以對一個元素同時應用多個标記,如下所示。

<activity android:name=”com.example.ActivityOne”
    android:theme=”@oldtheme”
    android:exported=”false”
    android:allowTaskReparenting="true"
    android:windowSoftInputMode=”stateUnchanged”>
           
<activity android:name=”com.example.ActivityOne”
    android:theme=”@newtheme”
    android:exported=”true”
    android:screenOrientation=”portrait”
    tools:replace=”android:theme,android:exported”
    tools:remove=”android:windowSoftInputMode”>
           
<activity android:name=”com.example.ActivityOne”
    android:theme=”@newtheme”
    android:exported=”true”
    android:allowTaskReparenting="true"
    android:screenOrientation=”portrait”>
           

Marker selector

如果您想僅對

某個特定的導入庫

應用合并規則标記,請添加具有庫包名稱的

tools:selector

屬性。

例如,對于下面的清單,僅在低優先級清單檔案來自

com.example.lib1

庫時應用

remove

合并規則。

<permission android:name="permissionOne"
    tools:node="remove"
    tools:selector="com.example.lib1">
           

如果低優先級清單來自其他源,系統将會忽略

remove

Tips:如果您将此功能與其中一個屬性标記配合使用,它将應用至标記中指定的所有選項。

預設情況下,導入 minSdkVersion 值高于主清單檔案的庫時會出錯,而且無法導入該庫。 要使合并工具忽略此沖突并導入庫,同時保持應用的低 minSdkVersion 值,請将 overrideLibrary 屬性添加至

<uses-sdk>

标記。屬性值可以是一個或多個庫包名稱(以逗号分隔),指明可能替換主清單的 minSdkVersion 的庫。

例如,如果應用的主清單按如下所示應用 overrideLibrary:

<uses-sdk android:targetSdkVersion="22"
    android:minSdkVersion="2"
    tools:overrideLibrary="com.example.lib1, com.example.lib2"/>
           

則下面這個清單(來自com.example.lib1)可以合并,并且不會出現與

<uses-sdk>

标記相關的錯誤,且合并清單将保留應用清單中的

minSdkVersion="2"

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
          package="com.example.lib1">
   <uses-sdk android:minSdkVersion="4"/>
           

Implicit system permissions

在最近的 Android 版本中,應用曾經可以自由通路的某些 Android API 已受 系統權限 限制。為了避免中斷預期會通路這些 API 的應用[To avoid breaking apps that expect access to these API],最近的 Android 版本允許應用在無權限的情況下繼續通路這些 API,前提是它們已将

targetSdkVersion

設定為低于添加限制的版本的值。此行為有效地向應用授予了_隐式權限_,以允許通路 API。 是以,這可能會對具有不同

targetSdkVersion

值的合并清單産生以下影響。

如果低優先級清單檔案提供隐式權限的 `targetSdkVersion` 值較低,而且高優先級清單_沒有_相同的隐式權限(由于其 `targetSdkVersion` 等于或高于添加限制的版本),合并工具将向合并清單_顯式_添加系統權限。
           

例如,如果您的應用将

targetSdkVersion

設定為 4 或更高值,并且導入了将

targetSdkVersion

設定為 3 或更低值的庫,合并工具會将 WRITE_EXTERNAL_STORAGE 權限添加至合并清單。 表 2 列出了可以添加至合并清單的所有可能權限。

注:如果您将應用的

targetSdkVersion

設定為 23 或更高值,則必須在應用嘗試通路受這些權限保護的 API 時為任何危險權限執行運作時權限請求。 如需獲得更多指導,請參閱 使用系統權限。

表 2. 合并工具可添加至合并清單的權限清單

低優先級清單聲明 添加至合并清單的權限
targetSdkVersion 是 3 或更低值 WRITE_EXTERNAL_STORAGE, READ_PHONE_STATE
targetSdkVersion 是 15 或更低值,并且使用 READ_CONTACTS READ_CALL_LOG
targetSdkVersion 是 15 或更低值,并且使用 WRITE_CONTACTS WRITE_CALL_LOG

Inspect the merged manifest and find conflicts

即使在建構 APK 之前,也可以

預覽合并清單

,具體方法是:在 Android Studio 中打開您的 AndroidManifest.xml 檔案,然後單擊編輯器底部的

Merged Manifest

頁籤。

Merged Manifest 視圖在左側顯示合并清單的

結果

,在右側顯示每個合并清單檔案的相關

資訊

從低優先級檔案中合并的元素在左側以不同顔色突出顯示,每種顔色的關鍵字在右側的 Manifest Sources 下方指定。

Gradle 翻譯 Merge AndroidManifest 合并清單檔案 [MD]

屬于建構的一部分但不構成元素或屬性的清單檔案列在右側的 Other Manifest Files 下方。

要檢視有關元素

來源

的資訊,請在左側單擊元素,詳細資訊将顯示在右側的 Merging Log 下方。

如果發生任何沖突,它們将顯示在右側的 Merging Errors 下方,并且包含有關如何使用

合并規則标記

解決沖突的建議。錯誤也會列印在 Event Log 視窗中(請選擇 View > Tool Windows > Event Log)。

如果您想要檢視

合并決策樹

的完整日志,則可以在【各個子產品】的

build/outputs/logs/manifest-merger-【buildVariant】-report.txt

中查找該日志檔案。

Appendix: Merge policies

清單合并工具可以在邏輯上将某個清單中的每個 XML 元素與其他清單中的對應元素相比對。 合并工具會使用

比對關鍵字[match key]

比對每個元素,比對關鍵字可以是唯一的屬性值(如

android:name

或标記本身的天然唯一性(例如,隻能有一個

<supports-screen>

元素)。 如果兩個清單具有相同的 XML 元素,工具将采用三種合并政策中的一種來合并這兩個元素:

  • 合并[Merge]

    :将所有非沖突屬性合并到同一标記中,然後按其各自的合并政策合并子元素。 如果任何屬性互相沖突,請使用合并規則标記将它們合并在一起。
  • 僅合并子項[Merge children only]

    :不整合或合并屬性(僅保留優先級最高的清單檔案提供的屬性),并按照其合并政策合并子項。
  • 保留[Keep]

    :将元素“按原樣”保留,然後将其添加至合并檔案中的同一父元素。此政策僅在可接受相同元素的多個聲明時使用。

表 3. 清單元素合并政策和合并關鍵字 Manifest element merge policies and match keys

元素 合并政策 合并關鍵字

<action>

合并

android:name

屬性

<activity>

android:name

<application>

每個

<manifest>

僅一個

<category>

android:name

<data>

<intent-filter>

僅 1 個

<grant-uri-permission>

<provider>

<instrumentation>

android:name

<intent-filter>

保留 不比對;允許父元素内的多個聲明

<manifest>

每個檔案僅 1 個

<meta-data>

android:name

<path-permission>

<provider>

<permission-group>

android:name

<permission>

android:name

<permission-tree>

android:name

<provider>

android:name

<receiver>

android:name

<screen>

android:screenSize

<service>

android:name

<supports-gl-texture>

android:name

<supports-screen>

<manifest>

<uses-configuration>

<manifest>

<uses-feature>

android:name

屬性(如果不存在,則使用

android:glEsVersion

屬性)

<uses-library>

android:name

<uses-permission>

android:name

<uses-sdk>

<manifest>

自定義元素 無比對;合并工具不了解這些資訊,是以它們始終包括在合并清單中

2019-6-29

本文來自部落格園,作者:白乾濤,轉載請注明原文連結:https://www.cnblogs.com/baiqiantao/p/11108339.html