サイトのトップへ戻る

Android Studio ドキュメント 日本語訳

サイト内検索

複数のマニフェストファイルを結合する

APKファイルはAndroidManifest.xml ファイルを一つだけ含めることができますが、 Android Studio のプロジェクトは複数のマニフェストファイルを含めることができます(メインソースセットのもの、ビルドバリアントのもの、インポートするライブラリのもの)。 そのためアプリをビルドする際、Gradle のビルドは全てのマニフェストファイルを一つに結合してAPKにパッケージします。

マニフェスト結合ツールは、いくつかの結合ヒューリスティクスとあなたが定義した結合設定に従って各ファイルの全てのXML要素を設定します。 このページでは、マニフェスト結合がどのように動作するか、結合時の競合を解決するためにどのようにして結合設定を適用するかについて説明します。

ヒント: Merged Manifest ビュー を使用すると、 結合後のマニフェストをプレビューして競合エラーを見つけることができます。



結合の優先順位

結合ツールはマニフェストファイルの優先順位に基づいて順番にそれらを結合することで、 全てのマニフェストファイルを一つのファイルにまとめます。 例えば三つのマニフェストファイルがある場合、図 1で図解しするように、最も優先度の低いマニフェストファイルが次に優先度の高いファイル内に結合され、 それから最も優先度の高いマニフェストに結合されます。

図 1. 優先度の最も低いファイル(左)から最も高いファイル(右)へ、三つのマニフェストファイルが結合されるプロセス

お互いに結合されるマニフェストファイルには三つの基本的な種類があり、それらの結合優先順位は以下の通りです(優先順位の高い順):

  1. ビルドバリアント用のマニフェストファイル

    バリアント用のソースセットが複数ある場合、それのマニフェストの優先順位は以下の通りです:

    1. ビルドバリアントのマニフェスト (src/demoDebug/のような)
    2. ビルドタイプのマニフェスト ( src/debug/のような)
    3. プロダクトフレーバーのマニフェスト ( src/demo/のような)

      フレーバーディメンションを使用している場合、マニフェストファイルの優先順位は、flavorDimensions プロパティ内で一覧にされている各ディメンションの順番に対応します(一覧の最初のものほど優先順位が高い)。

  2. アプリモジュール用のメインマニフェストファイル
  3. インクルードしたライブラリのマニフェストファイル

    ライブラリが複数ある場合、そのマニフェストの優先順位は依存関係の順番と同じです。(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">


Node マーカー

結合ルールを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 結合ルールは無視されます。

メモ: これを属性マーカーと一緒に使用する場合、そのマーカーで指定された全ての属性に適用されます。



インポートするライブラリの <uses-sdk> を上書きする

既定では、メインマニフェストファイルよりも 高い 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 要素を持っていた場合、ツールは以下三つの結合ポリシーの中の一つを使って二つの要素を結合します。:

Merge
競合のない全ての属性を同一タグ内に結合し、それぞれの結合ポリシーに従って子要素を結合します。 属性が競合する場合、結合ルールマーカーを使ってそれらを結合します。
Merge children only
属性の組み合わせや結合は行わず (最も優先度の高いマニフェストファイルの属性のみを維持します)、それぞれの結合ポリシーに従って子要素を結合します。
Keep
要素を“そのまま"にし、結合されたファイルの共通する親要素に追加します。これは、同じ要素の宣言が複数あることが許される場合にのみ使用します。

表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 一致処理は行われません; 結合ツールはこれらが何なのかを知らないため、常に結合後のマニフェストへ含めます。