Android官方技術文件翻譯——清單合併
本文譯自Android官方技術文件《Manifest Merger》,原文地址:http://tools.android.com/tech-docs/new-build-system/user-guide/manifest-merger。
翻譯不易,轉載請註明CSDN部落格上的出處:
http://blog.csdn.net/maosidiaoxian/article/details/42671999
翻譯工作耗時費神,如果你覺得本文翻譯得還OK,請點選文末的“頂”;如有錯訛,敬請指正。謝謝。
清單合併
目錄
- 1 清單檔案順序
- 3 元素和屬性的合併處理
- 4 屬性標記示例
- 5 元素標記示例
本文件主要介紹新的清單合併工具。
這個新的合併工具是gradle android 外掛的 0.10 版中引入的。截至 0.11 版本,該 gradle 外掛預設情況下都是使用此合併工具。
如果想恢復使用舊的清單合併工具,可以在你的 build.gradle 中新增以下配置:
android {
useOldManifestMerger true
}
Manifest 檔案排序
一般情況下,有三種類型的清單檔案需要合併成一個最終的應用程式清單,這裡按照優先順序順序列出:
-
Product flavors 和構建型別所指定的清單檔案。
-
應用程式的主清單檔案。
-
類庫的清單檔案。
第一種型別的清單檔案通常會重寫清單的內容,因為它專門提供用於特定的交付的應用程式。然後,第三種類型的清單檔案通常會被合併入上一步產生的主清單中。合併的規則取決於每個節點型別,可以使用“tools:”名稱空間屬性來更改。
由於多個product flavors和構建型別,這些清單檔案合併的結果的組合可能會是一個矩陣。然而,對於每一個組裝的步驟,每一個flavor group和構建型別的值都只能選擇一個,導致出現了潛伏的覆蓋主清單檔案的清單檔案的排序列表。
例如,下面的 FlavorGroups: abi,density,API,Prod/Internal 導致形成了以下可能的flavors的矩陣:
ABI |
Density |
API |
Prod/Internal |
x86 |
mdpi |
9 |
prod |
arm |
high |
14 |
internal |
mips |
xhigh |
15 |
|
xxhigh |
這樣就形成了3x4x3x2 種可能的組合。然而,對於每一次執行組裝,只能是一個group裡的falvor,定義在原始的 build.gradle 的flavor group 的屬性形成了一個可能要合併的清單 檔案的列表,並且這個列表由高優先順序到低優先順序排序。
例如,構建 x86-high-15-prod 的 variant ,會查詢以下的清單檔案以進行合併
-
x86/AndroidManifest.xml
-
high/AndroidManifest.xml
-
15/AndroidManifest.xml
-
internal/AndroidManifest.xml
在這個有序列表中,每個檔案根據其在列表中的順序都有一個優先順序,合併工具將使用這個優先順序來決定哪個XML元素或屬性將覆蓋一個較低優先順序的設定。
因此,合併工具的輸入將如下:
-
由flavor或編譯型別的清單檔案按優先順序組成的有序列表,這些會被經常引用作為 flavors的清單。
-
主清單檔案
-
由一些類庫宣告(或依賴傳遞而有的)的清單檔案組成的有序列表。
-
用於佔位符和XML產生的注入值
Android 清單檔案合併
在一個清單檔案中的每個元素都可以根據它的元素型別(比如activity,intent-filter)和一個可選的鍵值(key value)來識別。一些元素,如“activity”,必須有一個key,因為在一個AndroidManifest.xml中可能存在多個這樣的元素。其他的元素,像“application”,就不要求一定要有一個key,因為只能有一個這樣的元素。
元素的型別和鍵值對代表了一個清單元素的身份。
合併的activity的始終是兩個相同型別的元素之間,一個來自更高優先順序的清單檔案,一個來自較低優先順序的清單檔案。每一個合併的activity都有一個預設的行為,這一點將在隨後進行描述。此外,在節點或一個指定的屬性上的每個預設合併的activity都可能會被including工具的指定標記所覆蓋。
合併過程中還會把對每一個節點的合併結果記錄下來,這將在“日誌”章節描述。
元素和屬性的合併過程
隱式宣告
一些屬性都會有預設值(預設定義在線上 文件中)。當一個較高優先順序的元素沒有定義預設值為X的屬性時,如果一個較低優先順序的元素也恰好定義了一個值同樣是X的屬性,那麼這個屬性仍然會被新增到合併的元素中 (當然它的值是X), 因為它表示了類庫對這個屬性的值的一個明確的選擇,從而不去考慮把預設值作為它的值,而是為某特性設定一個正確的值(以防預設值發生改變)。
大多數有預設值的屬性,如果在低優先順序的屬性中已經定義了值,那麼預設值將被manifest合併工具忽略;定義的值會被合併,因為在預設值 和一個設定的值之間,並沒有衝突。在生成的合併的元素中,這個屬性會被設定為設定的值。
然而,下面列出的幾種情況例外:
<uses-feature android:required> |
預設值為 true。在與其他屬性合併時,將使用“或”的合併策略。這時因為如果任何一個庫需要該特性,那麼生成的應用程式也將需要此特性。 |
<uses-library android:required> |
同 uses-feature:required。 |
<uses-sdk android:minSdkVersion> |
預設值為 1。 將使用更高優先順序檔案的版本,但匯入一個較新版本的庫時將會產生錯誤。 |
<uses-sdk android:maxSdkVersion> |
同 uses-sdk:minSdkVersion |
<uses-sdk android:targetSdkVersion> |
同 uses-sdk:minSdkVersion |
自動升級
當匯入一個target SDK 版本比專案低的庫時,它可能需要顯式宣告地授予許可權 (可能還需要進行其他更改),以使得類庫在以後執行時能正常執行。這將由清單合併工具自動進行。
佔位符支援
當屬性值包含一個佔位符 (見下面的格式)時,合併工具將把此佔位符的值換成一個注入的值。注入的值是在build.gradle裡面定義的。
佔位符值的語法是 ${name},因為@符號已經預留給了連結。在最後的檔案合併發生之後,並且生成合並後的 android 的清單檔案輸出之前,帶有佔位符的所有值將都會被替換為注入的值。如果變數名是未知的,將導致構建失敗。
佔位符字串可以有一個字首或字尾,以實現只替換部分的內容。
示例:
android:authority="${applicationId}.foo"
android:authority=”com.acme.${localApplicationId}”
android:authority=”com.acme.${localApplicationId}.foo”
隱式佔位符 ${applicationId} 的值將由現有的build.gradle的 applicationId值自動提供。
示例:
<activity
android:name=".Main">
<intent-filter>
<action android:name="${applicationId}.foo">
</action>
</intent-filter>
</activity>
通過以下的gradle的宣告:
android {
compileSdkVersion 19
buildToolsVersion "19.0.2"
productFlavors {
flavor1 {
applicationId = "com.android.tests.flavorlib.app.flavor1"
}
}
一旦合併,<action android:name> 將會是
<action android:name=“com.android.tests.flavorlib.app.flavor1.foo”>
對於自定義的佔位符替換,可以使用以下的 DSL 來配置佔位符的值:
android { defaultConfig { manifestPlaceholders = [ activityLabel:"defaultName"] } productFlavors { free { } pro { manifestPlaceholders = [ activityLabel:"proName" ] } }
它將替換下面宣告中的佔位符:
<activity android:name=".MainActivity"android:label="${activityLabel}" >
合併策略的常見描述
XML的合併可能是在節點級別上的合併,也可能是在屬性級別上的合併。
在節點級別上,預設的合併策略是,只要沒有衝突就合併屬性和子元素。當兩個相同標識的元素具有相同的屬性,並且屬性的值不同時,就會出現衝突。
舉個例子:
<activity
android:name="com.foo.bar.ActivityOne"
android:theme=”@theme1”/>
與下面的宣告進行合併時不會產生衝突:
<activity
android:name="com.foo.bar.ActivityOne"
android:screenOrientation=”landscape/>
同樣
<activity
android:name="com.foo.bar.ActivityOne"
android:theme=”@theme1”/>
與下面的宣告合併時也不會產生衝突,因為在這兩個元素中都定義的"theme"屬性具有相同的值。
<activity
android:name="com.foo.bar.ActivityOne"
android:theme="@theme1"
android:screenOrientation=”landscape/>
但是,
<activity
android:name="com.foo.bar.ActivityOne"
android:theme=”@theme1”/>
與下面的宣告合併時就會產生衝突,因為在這兩個元素中定義的"theme"屬性的值並不相同。
<activity
android:name="com.foo.bar.ActivityOne"
android:theme=”@theme2”
android:screenOrientation=”landscape/>
現在,每個元素都可以有子元素和規則,用於匹配那些將遵循同樣的基本原則的屬性,它們具有相同標識的子元素將匹配在一起。如果一個子元素僅存在於其中一個父元素,並不會衝突。
標記
標記是在工具(tools)名稱空間中的一個特別的屬性,用來描述對如何解決衝突所採取的決定。
所有標記都屬於 Android 工具名稱空間,因此您必須包含至少一個標記的任何 AndroidManifest.xml 中宣告該名稱空間:
xmlns:tools="http://schemas.android.com/tools"
示例:
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.android.tests.flavorlib.app"
xmlns:tools="http://schemas.android.com/tools">
<application
android:icon="@drawable/icon"
android:label="@string/app_name"
tools:replace=”icon, label”/>
</manifest>
當要合併的元素之間存在衝突時,必須顯式新增一些標記來指導清單合併工具(Manifest Marger)。在節點級別,應使用 tools:node 屬性,在屬性級別,應使用 tools:attr 屬性。
tools:node 標記
當一個節點存在衝突並且需要解決時,就應該要有一個 tools:node="maker_value" 的屬性存在。
<tools:node> 屬性值 |
清單合併工具的行為 |
<tools:node="merge"> |
這是節點合併的隱式的預設模式,節點只要不衝突就會被合併。 |
<tools:node="replace"> |
用註解的那一個替換低優先順序的宣告。 |
<tools:node="strict"> |
當另一個具有相同標識的節點存在並且不是嚴格相等時,將導致構建失敗。 |
<tools:node="merge-only-attributes"> |
只合並較低優先順序的宣告中的屬性。 |
<tools:node="remove"> |
從生成的 XML 中刪除所註解的元素。不論是否可能衝突,低優先順序的宣告都不會被合併進去。 |
<tools:node="removeAll"> |
移除所有相同節點型別(不是關鍵必須的)的元素 。 |
tools:attr 標記
在任何特別的元素中,可能會有許多與標記相關的屬性,用於解決所有屬性的衝突問題。
<tools:strict=”x, y, z”> |
屬性預設的隱式模式,會在當嘗試合併有不同值的低優先順序屬性宣告時產生錯誤。 |
<tools:remove=”x, y, z”> |
當合並時,從任何較低優先順序的宣告中刪除 x、 y、 z 屬性。 |
<tools:replace=”x, y, z”> |
把任何低優先順序宣告的x,y,z屬性的值替換為所提供的值(必須是在同一節點上)。 |
選擇器
每一個 tools:node 或 tools:attr 宣告都可以通過一個 tools:selector 的屬性進行擴充套件,這個屬性是合併策略是否應該被應用到當前的低優先順序的 XML 的描述的上下文資訊。例如,當僅在一個特定的庫,而不是任何的庫,才需要刪除一個許可權時,它會非常有用:
<permission
android:name="permissionOne"
tools:node="remove"
tools:selector="com.example.lib1">
tools:overrideLibrary 標記
這是一個特殊的標記,僅與use-sdk的宣告一起使用,用於在匯入的庫的最小SDK版本比應用程式的最小SDK版本還要新時,對這個庫是否匯入進行重寫。
如果沒有這樣的標誌,清單合併就會失敗。這個標記將允許使用者忽略最低的SDK版本而選擇哪些庫可以被匯入。
例如,在main android 清單中:
<uses-sdk android:targetSdkVersion="14" android:minSdkVersion="2"
tools:overrideLibrary="com.example.lib1, com.example.lib2"/>
將允許具有以下清單的庫被匯入而不出錯:
<manifestxmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.lib1"> <uses-sdk android:minSdkVersion="4" /> </manifest>
日誌
在清單合併期間的每個操作或決策都需要是
-
被記錄的
-
棖式化以讓計算機能夠解析的
-
按節點排序的 (因為一個節點的多個屬性可能會產生好幾種合併決定)
日誌記錄不會被組織為最終產生輸出檔案的事件和決策的線性集合。相反,為了方便開發人員,日誌檔案將按輸入檔案中發生衝突的頂級XML節點來進行組織 (無論當我們想想檔案的節點刪除的時候,它是否存在 於輸出檔案中)。
日誌記錄:
一個日誌記錄既是一個節點記錄 (描述在該特定節點上採取的所有操作),也是一個包含錯誤訊息和警告的訊息記錄。
日誌檔案 = 日誌記錄*
日誌記錄 = 節點記錄 | 訊息
訊息=檔案:行號:列號 嚴重性:\n描述
描述=(\t內容\n)*
名稱 |
值 |
file |
生成日誌條目的輸入檔案 |
line-number |
生成日誌條目的輸入的檔案行號 |
column-number |
生成日誌條目的輸入檔案列號 |
嚴重性 |
Error, Warning, Info |
description |
日誌條目有效載荷(譯者注:有效載荷即記載著資訊的那部分資料) |
示例
/Users/jedo/src/app/src/main/AndroidManifest.xml:3:9 Error:
Attribute [email protected] value=(portrait) from AndroidManifest.xml:3:9
is also present at flavorlib:lib1:unspecified:3:18 value=(landscape)
Suggestion: add 'tools:replace="icon"' to <activity> element at AndroidManifest.xml:1:5 to override
名稱 |
值 |
node-type |
XML 節點型別 |
node-key |
節點的鍵的屬性值 |
record-type |
[Action |Log ] * |
node-type#node-key\n
\t(node_action:Action)*
\t\t(attribute_action:Action)*
名稱 |
值 |
action-type |
added | rejected | implied |
target |
node | attribute |
target-name |
節點的鍵名稱或屬性名稱 |
origin |
原始值的位置 |
application
ADDED from AndroidManifest.xml:10:5
MERGED from flavorlib:lib2:unspecified:3:5
android:label
ADDED from AndroidManifest.xml:12:9
REJECTED from flavorlib:lib2:unspecified:3:55
android:icon
ADDED from AndroidManifest.xml:11:9
REJECTED from flavorlib:lib2:unspecified:3:18
receiver#com.example.WidgetReceiver
ADDED from ManifestMerger2Test0_main.xml:60:9
android:labelADDED from ManifestMerger2Test0_main.xml:61:13
android:iconADDED from ManifestMerger2Test0_main.xml:62:13
android:nameADDED from ManifestMerger2Test0_main.xml:63:13
構建錯誤
當發生構建錯誤時,則應顯示特定節點失敗的日誌,並在後面有一段向用戶描述的錯誤訊息。舉個例子:
更高優先順序的宣告
<activity
android:name="com.foo.bar.ActivityOne"
android:screenOrientation="portrait"
android:theme=”@theme1”/>
和一個較低優先順序的宣告:
<activity
android:name="com.foo.bar.ActivityOne"
android:screenOrientation=”landscape/>
會同時產生日誌檔案和輸出結果(能讓人類,計算機以及IDE都能識別的確切格式還未確定)。
/Users/jedo/src/app/src/main/AndroidManifest.xml:3:9 Error:
Attribute [email protected] value=(portrait) from AndroidManifest.xml:3:9
is also present at flavorlib:lib1:unspecified:3:18 value=(landscape)
Suggestion: add 'tools:replace="icon"' to <activity> element at AndroidManifest.xml:1:5 to override
Blame
通過元素或屬性所在的一些指示,可以獲取一個“blame”型別的輸出,以限制合併的XML中產生的每一個元素和屬性。
合併策略
每個元素型別都有一特定的預設合併策略附屬於它。例如,大部分的元素型別,像activity或application都有一個預設合併策略,這個策略是所有屬性和子元素都會被合併(假設沒有衝突)到所生產的元素當中。不過,其他元素,如頂級的manifest,預設合併策略是隻合併子元素。這意味著較低優先順序的 AndroidManifest.xml 的manifest 元素的屬性都沒有資格能夠合併進去。
每個元素也可以擁有或不擁有一個與它關聯的鍵。例如,application沒有鍵,在每一個 AndroidManifest.xml 中只能有一個 <application>元素。 大部分帶鍵的元素都使用“ android:name 屬性”來表示它們的鍵值,或其他內容等
合併
沒有衝突的屬性會被合併,子元素也會依據它們各自的合併策略進行合併。
只合並子元素
屬性不會被合併,只有子元素會根據它們各自的合併政策進行合併。
總是合併
始終保持元素的“原樣”,並新增到生成的合併檔案裡的共同的父元素中。
元素合併策略和鍵的列表
節點型別 |
合併策略 |
鍵 |
action |
合併 |
android:name 屬性 |
activity |
合併 |
android:name 屬性 |
application |
合併 |
沒有鍵 |
category |
合併 |
android:name 屬性 |
data |
合併 |
沒有鍵 |
grant-uri-permission |
合併 |
沒有鍵 |
instrumentation |
合併 |
android:name 屬性 |
intent-filter |
總是合併 |
子元素的action和categories android: name attribute。允許相同的鍵有多個宣告。 |
manifest |
只合並子元素 |
沒有鍵 |
meta-data |
合併 |
android:name 屬性 |
path-permission |
合併 |
沒有鍵 |
permission-group |
合併 |
android:name 屬性 |
permission |
合併 |
android:name 屬性 |
permission-tree |
合併 |
android:name 屬性 |
provider |
合併 |
android:name 屬性 |
receiver |
合併 |
android:name 屬性 |
screen |
合併 |
屬性 screenSize |
service |
合併 |
android:name 屬性 |
supports-gl-texture |
合併 |
android:name 屬性 |
supports-screen |
合併 |
沒有鍵 |
uses-configuration |
合併 |
沒有鍵 |
uses-feature |
合併 |
首先是屬性名稱,如果不存在,則接著是 glEsVersion 屬性 |
uses-library |
合併 |
android:name 屬性 |
uses-permission |
合併 |
android:name 屬性 |
uses-sdk |
合併 |
沒有鍵 |
自定義元素 |
合併 |
沒有鍵 |
包名稱智慧替換
有些屬性是包依賴屬性,意思就是說這些屬性支援通過由清單節點中的package屬性提供的包設定,對部分完全限定的類名稱的智慧替換。
下面描述的每個屬性都可以有一個區域性的類名稱,這個類名稱是以一個點或不包含任何點開頭的。
示例:
<manifest
xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.app1">
<application>
<activity android:name=".Main" />
</application>
</manifest>
將被擴充成:
<application>
<activity android:name="com.example.app1.Main" />
</application>
這是獨立於build.gradle裡的任何包設定的(譯者注,關於package和applicationId可以看一下我的系列部落格中的另一篇文章的介紹)。例如,你build.gradle 包含以下內容:
android {
compileSdkVersion 19
buildToolsVersion "19.0.2"
productFlavors {
flavor1 {
applicationId = "com.android.tests.flavorlib.app.flavor1"
}
}
擴充的結果仍然是
<activity android:name=”com.example.app1.Main”>
如果你需要讓注入的值作為擴充的屬性值,可以使用 ${applicationId} 佔位符,例如:
<manifest
xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.app1">
<application>
<activity android:name="${applicationId}.Main" />
</application>
</manifest>
下面是可以使用這種智慧替換的能力包獨立屬性的列表:
節點型別 |
屬性的本地名稱 |
activity |
name, parentActivityName |
activity-alias |
name, targetActivity |
application |
name, backupAgent |
instrumentation |
name |
provider |
name |
receiver |
name |
service |
name |
屬性標記示例
重寫來自庫的屬性
使用 tools:replace="x, y, z" 將會重寫從外部庫的activity 的XML宣告中匯入的 x,y,z 屬性。
更高級別的宣告
<activity
android:name="com.foo.bar.ActivityOne"
android:screenOrientation="portrait"
android:theme="@theme1"
tools:replace=”theme”/>
和一個較低優先順序的宣告:
<activity
android:name="com.foo.bar.ActivityOne"
android:theme="@olddogtheme"
android:windowSoftInputMode="stateUnchanged"
android:exported="true" >
將產生:
<activity
android:name="com.foo.bar.ActivityOne"
android:screenOrientation="portrait"
android:theme="@theme1"
android:windowSoftInputMode="stateUnchanged"
android:exported="true"/>
刪除來自庫的屬性。
使用 tools:remove="x, y, z" 將會在產生的XML中刪除 x,y,z 屬性的宣告。
更高優先順序的宣告
<activity
android:name="com.foo.bar.ActivityOne"
android:hardwareAccelerated="true"
tools:remove="android:theme,android:screenOrientation" />
和一個較低優先順序的宣告:
<activity
android:name="com.foo.bar.ActivityOne"
android:screenOrientation="landscape"
android:theme="@olddogtheme"
android:windowSoftInputMode="stateUnchanged"
android:exported="true"/>
將產生:
<activity
android:name="com.foo.bar.ActivityOne"
android:hardwareAccelerated="true"
android:windowSoftInputMode="stateUnchanged"
android:exported="true"/>
強制更新屬性值
毫無疑問,所有宣告屬性幾乎都帶有“strict”的合併策略,所以如果兩個要合併的元素都有一個同樣名稱的屬性但值卻不同,就是一個需要明確解決的衝突。
所以,一個較高優先順序的宣告
<activity
android:name="com.foo.bar.ActivityOne"
android:theme=”@newdogtheme”/>
和一個較低優先順序的宣告:
<activity
android:name="com.foo.bar.ActivityOne"
android:theme=”@olddogtheme”/>
與一個較高優先順序的宣告
<activity
android:name="com.foo.bar.ActivityOne"
android:theme=”@newdogtheme”
tools:strict=”theme”/>
和一個較低優先順序的宣告:
<activity
android:name="com.foo.bar.ActivityOne"
android:theme=”@olddogtheme”/>
是完全等價的,並且都將不能正確地合併,除非新增一個 tools:replace="theme" 的屬性。
混合操作
如果使用者想要刪除某些屬性且重寫其他屬性同時儲存另一組的原始屬性,只需依次新增所有標記。
例如:
<activity
android:name="com.foo.bar.ActivityOne"
android:windowSoftInputMode="stateUnchanged"
android:theme="@theme1"
tools:remove="android:exported, android:screenOrientation"
tools:replace="android:theme"/>
和一個較低優先順序的宣告:
<activity
android:name="com.foo.bar.ActivityOne"
android:screenOrientation="landscape"
android:theme="@olddogtheme"
android:exported="true"/>
將產生:
<activity
android:name="com.foo.bar.ActivityOne"
android:theme="@theme1"
android:windowSoftInputMode="stateUnchanged" />
需要注意的是,如果低優先順序的宣告中包含 android:windowSoftInputMode或者未明確標記為刪除或替換的任何屬性,將生成一個構建錯誤。
元素標記示例
移除元素
如果要刪除任何一個庫的某個元素,需要在更高優先順序的檔案中宣告
<activity-alias
android:name="foo.bar.alias">
<meta-data
android:name="zoo"
tools:node="remove"/>
</activity-alias>
與下面進行合併
<activity-alias
android:name="foo.bar.alias">
<meta-data
android:name="zoo"
android:value="@string/bear"/>
</activity-alias>
將產生:
<activity-alias
android:name="foo.bar.alias">
</activity-alias>
移除所有元素
如果要作任何一個庫的一個特定型別的所有元素,需要在更高優先順序的檔案中宣告
<activity-alias
android:name="foo.bar.alias">
<meta-data
tools:node="removeAll" />
</activity-alias>
與下面進行合併
<activity-alias
android:name="foo.bar.alias">
<meta-data
android:name="zoo"
android:value="@string/bear"/>
<meta-data
android:name="cage"
android:value="@string/iron"/>
</activity-alias>
將產生:
<activity-alias
android:name="foo.bar.alias"
</activity-alias>
元素替換
<activity-alias
android:name="foo.bar.alias"
tools:node="replace">
<meta-data
android:name="zoo"/>
</activity-alias>
與下面進行合併
<activity-alias
android:name="foo.bar.alias">
<meta-data
android:name="cage"
android:value="@string/iron"/>
</activity-alias>
將產生:
<activity-alias
android:name="foo.bar.alias">
<meta-data
android:name="zoo"
tools:node="remove"/>
</activity-alias>
選擇器示例
使用包名稱來選擇庫這裡,我們有三個庫要合併進一個主清單檔案中。
主清單
<manifest
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
package="com.example.main">
<permission
android:name="permissionOne"
tools:node="remove"
tools:selector="com.example.lib1">
</permission>
<permission
tools:node="removeAll"
tools:selector="com.example.lib3">
</permission>
<permission
android:name="permissionThree"
android:protectionLevel="signature"
tools:node="replace">
</permission>
</manifest>
與庫1
<manifest
xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.lib1">
<permission android:name="permissionOne"
android:protectionLevel="signature">
</permission>
<permission android:name="permissionTwo"
android:protectionLevel="signature">
</permission>
</manifest>
和庫2
<manifest
xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.lib2">
<permission android:name="permissionThree"
android:protectionLevel="normal">
</permission>
<permission android:name="permissionFour"
android:protectionLevel="normal">
</permission>
</manifest>
及庫3
<manifest
xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.lib2">
<permission android:name="permissionFive"
android:protectionLevel="normal">
</permission>
</manifest>
將產生:
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.main" >
<permission
android:name="permissionThree"
android:protectionLevel="signature" >
</permission>
<permission
android:name="permissionTwo"
android:protectionLevel="signature" >
</permission>
<permission
android:name="permissionFour"
android:protectionLevel="normal" >
</permission>
</manifest>