APKファイルはAndroidManifest.xml
ファイルを一つだけ含めることができますが、
Android Studio のプロジェクトは複数のマニフェストファイルを含めることができます(メインソースセットのもの、ビルドバリアントのもの、インポートするライブラリのもの)。
そのためアプリをビルドする際、Gradle のビルドは全てのマニフェストファイルを一つに結合してAPKにパッケージします。
マニフェスト結合ツールは、いくつかの結合ヒューリスティクスとあなたが定義した結合設定に従って各ファイルの全てのXML要素を設定します。 このページでは、マニフェスト結合がどのように動作するか、結合時の競合を解決するためにどのようにして結合設定を適用するかについて説明します。
ヒント: Merged Manifest ビュー を使用すると、 結合後のマニフェストをプレビューして競合エラーを見つけることができます。
結合ツールはマニフェストファイルの優先順位に基づいて順番にそれらを結合することで、 全てのマニフェストファイルを一つのファイルにまとめます。 例えば三つのマニフェストファイルがある場合、図 1で図解しするように、最も優先度の低いマニフェストファイルが次に優先度の高いファイル内に結合され、 それから最も優先度の高いマニフェストに結合されます。
図 1. 優先度の最も低いファイル(左)から最も高いファイル(右)へ、三つのマニフェストファイルが結合されるプロセス
お互いに結合されるマニフェストファイルには三つの基本的な種類があり、それらの結合優先順位は以下の通りです(優先順位の高い順):
バリアント用のソースセットが複数ある場合、それのマニフェストの優先順位は以下の通りです:
src/demoDebug/
のような)src/debug/
のような)src/demo/
のような)
フレーバーディメンションを使用している場合、マニフェストファイルの優先順位は、flavorDimensions
プロパティ内で一覧にされている各ディメンションの順番に対応します(一覧の最初のものほど優先順位が高い)。
ライブラリが複数ある場合、そのマニフェストの優先順位は依存関係の順番と同じです。(Gradleのdependencies
ブロック内に記述されている順序)
例えば、ライブラリのマニフェストがメインマニフェスト内へ結合され、それからメインマニフェストがビルドバリアントマニフェスト内へ結合されます。
重要:
build.gradle
ファイルのビルド設定によって、結合されたマニフェストファイル内の対応する属性は上書きされます。
例えば、build.gradle
ファイルのminSdkVersion
によって、 <uses-sdk>
マニフェスト要素内の一致する属性は上書きされます。
競合を避けるために、<uses-sdk>
要素の内容は変更せず、build.gradle
ファイル内でのみこれらのプロパティを定義するようにしてください。
詳細についてはビルド構成を設定するを参照してください。
結合ツールは、マニフェストの全てのXML 要素を他のマニフェストの対応する要素と論理的に一致させることができます。 (一致処理をどのように行うかの詳細については、付録の 結合のポリシーを参照してください)。
優先度の低いマニフェストの要素が優先度の高いマニフェスト内に存在しない場合、その要素は結合マニフェストに追加されます。 しかし、一致する要素が存在する場合、その要素内に定義されている全ての属性を同一要素内に結合します。 両方のマニフェストに同じ属性があり、その属性が違う設定値を持っていることをツールが検知した場合、 結合の競合が発生します。
表1では、結合ツールが全ての属性を同一要素に結合しようとした時に起こりうる結果を示しています。
表 1. 属性値ごとの既定の結合動作
優先度の高いマニフェストの属性 | 優先度の低いマニフェストの属性 | 属性の結合結果 |
---|---|---|
値無し | 値無し | 値無し (既定値を使います) |
値は B | 値は B | |
値は A | 値無し | 値は A |
値は A | 値は A | |
値は B | 競合が発生—結合ルールマーカーを追加する必要があります |
しかし、結合の競合を避けるためにマージツールが上記とは異なる動作を行う場合があります。:
<manifest>
要素内の属性は一つに結合されることはありません—最も優先度の高いマニフェストの属性のみが使用されます。<uses-feature>
要素と <uses-library>
要素のandroid:required
属性ではOR結合が使用されます。
例えば、競合が発生した場合には "true" が適用され、どれか一つのマニフェストからでも必要となっている機能やライブラリは常にインクルードされます。
<uses-sdk>
要素内の属性は常に最も優先度の高いマニフェストファイルの値を使います。:
minSdkVersion
値の方が高い場合、overrideLibrary
結合ルールを適用しなければエラーが発生します。targetSdkVersion
値の方が低い場合、結合ツールは優先度の高いマニフェストの値を使用しますが、
インポートするライブラリが正常に動作するために必要なシステム権限も追加します (高バージョンのAndroidで権限の制限が増えている場合のため)。この動作の詳細については暗黙的なシステム権限に関する項目を参照してください。<intent-filter>
要素はマニフェスト間で一致することはありません。
それぞれは一意のものとして扱われ、結合後のマニフェスト内の共通の親要素に追加されます。
属性間で起こる他の全ての競合についてはエラーが発生するので、優先度の高いマニフェストファイルに特別な属性を追加して、結合ツールに競合を解決する方法を指示する必要があります。 (結合ルールマーカーについては、次の項目を参照してください)。
既定の属性値に依存しないでください。
全ての固有属性が同一の要素内に結合されるので、優先度の高いマニフェストで属性値を宣言せずに既定の属性値に依存していた場合、予期せぬ結果を引き起こす可能性があります。
例えば、優先度の高いマニフェストでandroid:launchMode
属性を宣言 せずに、既定値の"standard"
を使用しており、
優先度の低いマニフェストでこの属性に異なる値を宣言していた場合、その値が結合されたマニフェストに適用されます(既定値を上書きします)。
そのため、使用する各属性については明示的に定義を行う必要があります。 (各属性の既定値については マニフェストのリファレンスに記載されています。)
結合ルールマーカーは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">
結合ルールを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>
tools:node="merge-only-attributes"
優先度の低いマニフェスト:
<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>
マニフェストの結合結果:
<activity android:name=”com.example.ActivityOne” android:screenOrientation=”portrait” android:windowSoftInputMode=”stateUnchanged”> </activity>
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 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="removeAll"
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”> </activity-alias>
tools:node="replace"
低い優先度のマニフェスト:
<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>
tools:node="strict"
優先度の低いマニフェスト:
<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”> </activity>
この場合はマニフェスト結合エラーが発生します。
strict モードでは、二つのマニフェストの要素は一切差異があってはいけません。
そのため、他の結合ルールマーカーを適用してこの差異を解決しなければなりません。
(通常だと、これら二つのマニフェストは、上記 tools:node="merge"
の例で示しているようにちゃんと結合されます。)
他にも、マニフェストファイル内の特定の属性にのみ結合ルールを適用するには、以下の属性を使用します。 各属性は、コンマで区切られた複数の属性名(属性の名前空間を含む)を設定できます。
tools:remove="attr, ..."
優先度の低いマニフェスト:
<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”>
tools:replace="attr, ..."
優先度の低いマニフェスト:
<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”>
tools:strict="attr, ..."
優先度の低いマニフェスト:
<activity android:name=”com.example.ActivityOne” android:screenOrientation=”landscape”> </activity>
優先度の高いマニフェスト:
<activity android:name=”com.example.ActivityOne” android:screenOrientation=”portrait” tools:strict="android:screenOrientation”> </activity>
この場合はマニフェスト結合エラーが発生します。
他の結合ルールマーカーを適用して競合を解決する必要があります。
(忘れないでください。: これは既定の動作なので、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”>
特定のインポートライブラリにのみ結合ルールマーカーを適用したい場合は、ライブラリパッケージ名が設定されたtools:selector
属性を追加します。
例えば以下のようなマニフェストでは、優先度の低いマニフェストファイルがcom.example.lib1
ライブラリのものだった場合にのみ、
remove
結合ルールが適用されます。
<permission android:name="permissionOne" tools:node="remove" tools:selector="com.example.lib1">
優先度の低いマニフェストが他のソースのものだった場合は、remove
結合ルールは無視されます。
メモ: これを属性マーカーと一緒に使用する場合、そのマーカーで指定された全ての属性に適用されます。
既定では、メインマニフェストファイルよりも 高い minSdkVersion
値を持つライブラリをインポートした時、
エラーが発生してライブラリはインポートできません。
結合ツールにこの競合を無視させて、アプリの低い方のminSdkVersion
値を維持したままライブラリをインポートさせるには、
<uses-sdk>
タグにoverrideLibrary
属性を追加します。
この属性値には、複数のライブラリパッケージ名を設定でき(コンマ区切りで)、それらはメインマニフェストのminSdkVersion
を上書きできるライブラリを示します。
例えば、以下のようにアプリのメインマニフェストがoverrideLibrary
を適用する場合:
<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.example.app" xmlns:tools="http://schemas.android.com/tools"> <uses-sdk android:targetSdkVersion="22" android:minSdkVersion="2" tools:overrideLibrary="com.example.lib1, com.example.lib2"/> ...
すると以下のマニフェストは<uses-sdk>
に関してエラーは発生せずに結合され、
結合後のマニフェストはアプリマニフェストのminSdkVersion="2"
を維持します。
<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.example.lib1"> <uses-sdk android:minSdkVersion="4" /> ...
かつてはアプリから自由にアクセスできたAndroid APIのいくつかは、最新版のAndroidではシステム権限 によって制限されています。
これらAPIへのアクセスを想定しているアプリを停止させないために、
制限が追加されたバージョンより低い値をtargetSdkVersion
に設定した場合は、
最新版のAndroidでもアプリはこれらのAPIを権限なしに使い続けることができます。
この動作で、APIアクセスできる 暗黙的な権限 をアプリに効果的に付与します。
そのため、異なる targetSdkVersion
値を持つマニフェストを結合した場合に以下のような影響が発生する可能性があります。
優先度の低いマニフェストファイルが暗黙的なシステム権限を付与されるくらいの低いtargetSdkVersion
値を持っており、
優先度の高いマニフェストでは暗黙的システム権限が付与されない場合 (制限が追加され他バージョンよりも高いtargetSdkVersion
値を持っているなどの理由で)、
結合ツールは結合後のマニフェストファイルに明示的に システム権限を追加します。
例えば、アプリでは targetSdkVersion
に4以上の値を設定し、 targetSdkVersion
が3以下のライブラリをインポートする場合、
結合ツールはWRITE_EXTERNAL_STORAGE
権限を結合後のマニフェストへ追加します。
表2は、結合後のマニフェストに追加される可能性がある権限の一覧です。
表 2。 結合ツールが結合後のマニフェストに追加する可能性がある権限の一覧
優先度の低いマニフェストでの宣言 | 結合後のマニフェストに追加される権限 |
---|---|
targetSdkVersion が3以下 |
WRITE_EXTERNAL_STORAGE , READ_PHONE_STATE |
targetSdkVersion が 15 以下でREAD_CONTACTS を使用している |
READ_CALL_LOG |
targetSdkVersion 15 以下で WRITE_CONTACTS を使用している |
WRITE_CALL_LOG |
APKをビルドする前でも、
Android Studioの AndroidManifest.xml
ファイルを開いてエディター下部のMerged Manifest タブをクリックすることで、
結合後のマニフェストがどのようになるかプレビューを見ることができます。
図2で示すように、Merged Manifest ビューは画面左側にはマニフェストの結合結果を表示し、画面右側には各結合ファイルに関する情報を表示します。 画面左側では、優先度の低いマニフェストファイルから結合された要素は違う色で強調表示されます。 各色のキーについては、画面右側の Manifest Sources 配下で設定します。
図 2. Merged Manifest ビュー
ビルドの一部ではあったが要素や属性には関与していないマニフェストファイルは、画面右側の Other Manifest Files配下に一覧表示されます。
要素の由来に関する情報を参照するには、画面左側でその要素をクリックすると画面右側の Merging Log 配下に詳細が表示されます。
競合が発生した場合、それらは画面右側の Merging Errors配下に表示されます。 結合ルールマーカーを使ってどのように競合を解決するのか、その推奨方法も合わせて表示されます。 エラーは Event Log ウィンドウにも出力されます(View > Tool Windows > Event Logと進んで表示します)。
結合の決定木の完全なログが見たい場合は、モジュールのbuild/outputs/logs/
ディレクトリ内にあるmanifest-merger-buildVariant-report.txt
という名前のファイルログファイルで見ることができます。
マニフェスト結合ツールはマニフェストファイルの全ての要素を他のマニフェストファイルの対応する用途と論理的に合致させます。
この結合処理では、"match key"を使って各要素を合致させます。:
"match key"とは、重複のない属性値 (android:name
のような)やタグそれ自体の独自性(例えば、<supports-screen>
要素が一つしか存在しない)
です。
二つのマニフェストが同じXML 要素を持っていた場合、ツールは以下三つの結合ポリシーの中の一つを使って二つの要素を結合します。:
表3では、各要素タイプ・使用される結合ポリシーの種類・二つのマニフェスト間で合致する要素を決めるのに使われるキー、の一覧を表示しています。
表 3. マニフェスト要素の結合ポリシーとmatch keys
要素 | 結合ポリシー | 合致キー |
---|---|---|
<action>
|
Merge | android:name 属性
|
<activity>
|
Merge | android:name 属性
|
<application>
|
Merge | <manifest> ごとに一つしかありません。
|
<category>
|
Merge | android:name 属性
|
<data>
|
Merge | <intent-filter> ごとに一つしかありません。
|
<grant-uri-permission>
|
Merge | <provider> ごとに一つしかありません。
|
<instrumentation>
|
Merge | android:name 属性
|
<intent-filter>
|
Keep | 一致処理は行いません; 親要素内に複数宣言されることが認められています。 |
<manifest>
|
Merge children only | ファイルごとに一つしかありません。 |
<meta-data>
|
Merge | android:name 属性
|
<path-permission>
|
Merge | <provider> ごとに一つしかありません。
|
<permission-group>
|
Merge | android:name 属性
|
<permission>
|
Merge | android:name 属性
|
<permission-tree>
|
Merge | android:name 属性
|
<provider>
|
Merge | android:name 属性
|
<receiver>
|
Merge | android:name 属性
|
<screen>
|
Merge | android:screenSize 属性
|
<service>
|
Merge | android:name 属性
|
<supports-gl-texture>
|
Merge | android:name 属性
|
<supports-screen>
|
Merge | <manifest> ごとに一つしかありません。
|
<uses-configuration>
|
Merge | <manifest> ごとに一つしかありません。
|
<uses-feature>
|
Merge | android:name 属性(ない場合はandroid:glEsVersion 属性)
|
<uses-library>
|
Merge | android:name 属性
|
<uses-permission>
|
Merge | android:name 属性
|
<uses-sdk>
|
Merge | <manifest> ごとに一つしかありません。
|
カスタム要素 | Merge | 一致処理は行われません; 結合ツールはこれらが何なのかを知らないため、常に結合後のマニフェストへ含めます。 |