目錄html
Markdown版本筆記 | 個人GitHub首頁 | 個人博客 | 個人微信 | 個人郵箱 |
---|---|---|---|---|
MyAndroidBlogs | baiqiantao | baiqiantao | bqt20094 | baiqiantao@sina.com |
官方文檔node
Merge multiple manifest filesandroid
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]。git
清單合併工具經過遵循某些合併啓發式算法[merge heuristics],並遵照您經過特殊 XML 屬性定義的合併首選項[merge preferences],來合併各個文件中的全部 XML 元素 。 本頁介紹清單合併的工做方式以及如何應用合併首選項來解決合併衝突[resolve merge conflicts]。github
提示: 使用 Merged Manifest 視圖預覽合併清單的效果並找出衝突錯誤。算法
Merge priorities微信
合併工具根據每一個清單文件的優先級將全部清單文件按順序
合併到一個文件中。 例如,若是您有 3 個清單文件,則會先將優先級最低的清單合併到優先級第 2 高的清單中,而後再將合併後的清單合併到優先級最高的清單中(如圖 1 所示)。app
有 3 種基本的清單文件能夠互相合並,它們的合併優先級以下(按優先級由高到低的順序):less
一、清單文件構建變體 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 屬性中的列示順序(按優先級由高到低的順序排列)對應。
二、app模塊的主清單文件 Main manifest file for the app module
三、所包括庫中的清單文件 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 | 值 B |
值 A | 沒有值 | 值 A |
值 A | 值 A | 值 A |
值 A | 值 B | 衝突錯誤—必須添加一個 合併規則標記 |
可是,在某些狀況下,合併工具會採起其餘行爲方式以免合併衝突:
<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 下方指定。
屬於構建的一部分但不構成元素或屬性的清單文件列在右側的 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> 僅 1 個 |
<instrumentation> |
合併 | android:name 屬性 |
<intent-filter> |
保留 | 不匹配;容許父元素內的多個聲明 |
<manifest> |
合併 | 每一個文件僅 1 個 |
<meta-data> |
合併 | android:name 屬性 |
<path-permission> |
合併 | 每一個 <provider> 僅 1 個 |
<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> 僅 1 個 |
<uses-configuration> |
合併 | 每一個 <manifest> 僅 1 個 |
<uses-feature> |
合併 | android:name 屬性(若是不存在,則使用 android:glEsVersion 屬性) |
<uses-library> |
合併 | android:name 屬性 |
<uses-permission> |
合併 | android:name 屬性 |
<uses-sdk> |
合併 | 每一個 <manifest> 僅 1 個 |
自定義元素 | 合併 | 無匹配;合併工具不瞭解這些信息,所以它們始終包括在合併清單中 |
2019-6-29