จัดการไฟล์ Manifest

หน้านี้อธิบายวิธีการทำงานของการผสานไฟล์ Manifest และวิธีใช้ค่ากำหนดการผสานเพื่อแก้ไขข้อขัดแย้งในการผสาน ดูข้อมูลเบื้องต้นเกี่ยวกับไฟล์ Manifest ของแอปได้ที่ภาพรวมของไฟล์ Manifest ของแอป

ผสานรวมไฟล์ Manifest หลายไฟล์

ไฟล์ APK หรือ Android App Bundle มีไฟล์ AndroidManifest.xml ได้เพียงไฟล์เดียว แต่โปรเจ็กต์ Android Studio อาจมีไฟล์ Manifest หลายไฟล์ที่ได้จากชุดซอร์สหลัก ตัวแปรบิลด์ และไลบรารีที่นำเข้า เมื่อสร้างแอป บิลด์ Gradle จะผสานไฟล์ Manifest ทั้งหมดเป็นไฟล์ Manifest ไฟล์เดียวที่รวมอยู่ในแอป

เครื่องมือผสานไฟล์ Manifest จะรวมองค์ประกอบ XML ทั้งหมดจากแต่ละไฟล์โดยทำตามการเรียนรู้ของระบบการผสานและปฏิบัติตามค่ากำหนดการผสานที่คุณกำหนดไว้ด้วยแอตทริบิวต์ XML พิเศษ

รวมลำดับความสำคัญ

เครื่องมือผสานจะรวมไฟล์ Manifest ทั้งหมดไว้ในไฟล์เดียวตามลำดับ โดยอิงตามลำดับความสำคัญของไฟล์ Manifest แต่ละไฟล์ ตัวอย่างเช่น หากคุณมีไฟล์ Manifest 3 ไฟล์ ระบบจะผสานไฟล์ Manifest ที่มีลำดับความสำคัญต่ำสุดเข้ากับไฟล์ Manifest ที่มีลำดับความสำคัญสูงสุดถัดไป จากนั้นจึงผสานไฟล์ Manifest นั้นเข้ากับไฟล์ Manifest ที่มีลำดับความสำคัญสูงสุด ดังที่แสดงในรูปที่ 1

รูปที่ 1 กระบวนการรวมไฟล์ Manifest 3 ไฟล์ โดยรวมไฟล์ที่มีลำดับความสำคัญต่ำสุดเข้ากับไฟล์ที่มีลำดับความสำคัญสูงสุด

ไฟล์ Manifest มี 3 ประเภทพื้นฐานที่อาจผสานรวมเข้าด้วยกัน และลำดับความสำคัญในการผสานรวมมีดังนี้ (ลำดับความสำคัญสูงสุดก่อน)

  1. ไฟล์ Manifest สำหรับตัวแปรบิลด์

    หากคุณมีชุดซอร์สหลายชุดสำหรับตัวแปร ลำดับความสำคัญของไฟล์ Manifest จะเป็นดังนี้

    • ไฟล์ Manifest ของตัวแปรบิลด์ (เช่น src/demoDebug/)
    • ไฟล์ Manifest ของประเภทบิลด์ (เช่น src/debug/)
    • ไฟล์ Manifest ของเวอร์ชันผลิตภัณฑ์ (เช่น src/demo/)

      หากคุณใช้มิติข้อมูลของเวอร์ชัน ลำดับความสำคัญของไฟล์ Manifest จะสอดคล้องกับลำดับของมิติข้อมูลแต่ละรายการที่แสดงในพร็อพเพอร์ตี้ flavorDimensions (รายการแรกมีลำดับความสำคัญสูงสุด)

  2. ไฟล์ Manifest หลักสำหรับโมดูลแอป

  3. ไฟล์ Manifest จากไลบรารีที่รวมอยู่

    หากคุณมีไลบรารีหลายรายการ ลำดับความสำคัญของไฟล์ Manifest จะตรงกับลำดับที่ปรากฏในบล็อก dependencies ของ Gradle

ตัวอย่างเช่น ไฟล์ Manifest ของไลบรารีจะผสานรวมเข้ากับไฟล์ Manifest หลัก จากนั้นไฟล์ Manifest หลักจะผสานรวมเข้ากับไฟล์ Manifest ของตัวแปรบิลด์ โปรดทราบว่าลำดับความสำคัญในการผสานเหล่านี้เหมือนกันสำหรับชุดซอร์สทั้งหมด ตามที่อธิบายไว้ในสร้างด้วยชุดซอร์ส

การประเมินความขัดแย้งในการผสานรวม

เครื่องมือผสานสามารถจับคู่ทุกองค์ประกอบ XML จากไฟล์ Manifest หนึ่งกับองค์ประกอบที่เกี่ยวข้องในไฟล์ Manifest อีกไฟล์หนึ่งได้อย่างสมเหตุสมผล โปรดดูรายละเอียดเกี่ยวกับวิธีการทำงานของการจับคู่ที่หัวข้อลำดับความสำคัญของการผสานในส่วนก่อนหน้า

หากองค์ประกอบจากไฟล์ Manifest ที่มีลำดับความสำคัญต่ำกว่าไม่ตรงกับองค์ประกอบใดๆ ในไฟล์ Manifest ที่มีลำดับความสำคัญสูงกว่า ระบบจะเพิ่มองค์ประกอบนั้นลงในไฟล์ Manifest ที่ผสานแล้ว อย่างไรก็ตาม หากมีองค์ประกอบที่ตรงกัน เครื่องมือผสานจะพยายามรวมแอตทริบิวต์ทั้งหมดจากแต่ละองค์ประกอบไว้ในองค์ประกอบเดียวกัน หากเครื่องมือพบว่าไฟล์ Manifest ทั้ง 2 ไฟล์มีแอตทริบิวต์เดียวกันแต่มีค่าต่างกัน จะเกิดความขัดแย้งในการผสาน

ตารางที่ 1 แสดงผลลัพธ์ที่เป็นไปได้เมื่อเครื่องมือผสานพยายามรวมแอตทริบิวต์ทั้งหมดไว้ในองค์ประกอบเดียวกัน

ตารางที่ 1 ลักษณะการทำงานของการผสานรวมเริ่มต้นสำหรับค่าแอตทริบิวต์

แอตทริบิวต์ที่มีลำดับความสำคัญสูง แอตทริบิวต์ที่มีลำดับความสำคัญต่ำ ผลลัพธ์จากการผนวกข้อมูลของแอตทริบิวต์
ไม่มีค่า ไม่มีค่า ไม่มีค่า (ใช้ค่าเริ่มต้น)
ค่า B ค่า B
ค่า A ไม่มีค่า ค่า A
ค่า A ค่า A
ค่า B ข้อผิดพลาดเกี่ยวกับความขัดแย้ง - คุณต้องเพิ่มเครื่องหมายกฎการผสาน

อย่างไรก็ตาม มีบางสถานการณ์ที่เครื่องมือผสานทำงานแตกต่างออกไปเพื่อหลีกเลี่ยงความขัดแย้งในการผสาน ดังนี้

  • แอตทริบิวต์ในองค์ประกอบ <manifest> จะไม่ผสานรวมเข้าด้วยกัน ระบบจะใช้เฉพาะแอตทริบิวต์จากไฟล์ Manifest ที่มีลำดับความสำคัญสูงสุดเท่านั้น
  • แอตทริบิวต์ android:required ในองค์ประกอบ <uses-feature> และ <uses-library> จะใช้การผสาน OR หากมีข้อขัดแย้ง ระบบจะใช้ "true" และฟีเจอร์หรือไลบรารีที่ไฟล์ Manifest หนึ่งต้องการจะรวมอยู่เสมอ
  • แอตทริบิวต์ในองค์ประกอบ <uses-sdk> จะใช้ค่าจากไฟล์ Manifest ที่มีลำดับความสำคัญสูงกว่าเสมอ ยกเว้นในสถานการณ์ต่อไปนี้
    • เมื่อไฟล์ Manifest ที่มีลำดับความสำคัญต่ำกว่ามีค่า minSdk สูงกว่า ข้อผิดพลาดจะเกิดขึ้น เว้นแต่คุณจะใช้กฎการผสาน overrideLibrary
    • เมื่อไฟล์ Manifest ที่มีลำดับความสำคัญต่ำกว่ามีค่า targetSdkVersion ต่ำกว่า เครื่องมือผสานจะใช้ค่าจากไฟล์ Manifest ที่มีลำดับความสำคัญสูงกว่า และยังเพิ่มสิทธิ์ของระบบที่จำเป็นเพื่อให้มั่นใจว่าไลบรารีที่นำเข้าจะยังคงทำงานได้อย่างถูกต้อง (สำหรับกรณีที่ Android เวอร์ชันที่สูงกว่ามีข้อจำกัดด้านสิทธิ์เพิ่มขึ้น) ดูข้อมูลเพิ่มเติมเกี่ยวกับลักษณะการทำงานนี้ได้ในส่วนเกี่ยวกับสิทธิ์โดยนัยของระบบ
  • องค์ประกอบ <intent-filter> ไม่ตรงกันระหว่างไฟล์ Manifest ระบบจะถือว่าแต่ละรายการไม่ซ้ำกันและเพิ่มลงในองค์ประกอบหลักที่ใช้ร่วมกันในไฟล์ Manifest ที่ผสานแล้ว

สำหรับความขัดแย้งอื่นๆ ทั้งหมดระหว่างแอตทริบิวต์ คุณจะได้รับข้อผิดพลาดและต้องสั่งให้เครื่องมือผสานแก้ไขโดยการเพิ่มแอตทริบิวต์พิเศษในไฟล์ Manifest ที่มีลำดับความสำคัญสูงกว่า ดูส่วนต่อไปนี้เกี่ยวกับเครื่องหมายกฎการผสาน

เครื่องหมายกฎการผสาน

เครื่องหมายกฎการผสานคือแอตทริบิวต์ XML ที่คุณใช้เพื่อแสดงค่ากำหนดเกี่ยวกับวิธีแก้ไขความขัดแย้งในการผสานหรือนำองค์ประกอบและแอตทริบิวต์ที่ไม่ต้องการออกได้ คุณสามารถใช้เครื่องหมายกับทั้งองค์ประกอบหรือเฉพาะแอตทริบิวต์ที่เฉพาะเจาะจงในองค์ประกอบก็ได้

เมื่อผสานไฟล์ Manifest 2 ไฟล์ เครื่องมือผสานจะมองหาเครื่องหมายเหล่านี้ในไฟล์ Manifest ที่มีลำดับความสำคัญสูงกว่า

เครื่องหมายทั้งหมดเป็นของเนมสเปซ 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 ทั้งหมด (กับแอตทริบิวต์ทั้งหมดในองค์ประกอบไฟล์ Manifest หนึ่งๆ และแท็กย่อยทั้งหมด) ให้ใช้แอตทริบิวต์ต่อไปนี้

tools:node="merge"
ผสานแอตทริบิวต์ทั้งหมดในแท็กนี้และองค์ประกอบที่ฝังทั้งหมดเมื่อไม่มีความขัดแย้งโดยใช้การวิเคราะห์พฤติกรรมความขัดแย้งในการผสาน ซึ่งเป็นลักษณะการทำงานเริ่มต้นขององค์ประกอบ

ไฟล์ Manifest ที่มีความสำคัญต่ำ

<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>

ไฟล์ Manifest ที่มีความสำคัญสูง:

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

ผลลัพธ์ของไฟล์ Manifest ที่ผสานแล้ว:

<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="mergeOnlyAttributes"
ผสานแอตทริบิวต์ในแท็กนี้เท่านั้น อย่าผสานองค์ประกอบที่ฝังอยู่

ไฟล์ Manifest ที่มีความสำคัญต่ำ

<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>

ไฟล์ Manifest ที่มีความสำคัญสูง:

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

ผลลัพธ์ของไฟล์ Manifest ที่ผสานแล้ว:

<activity android:name="com.example.ActivityOne"
    android:screenOrientation="portrait"
    android:windowSoftInputMode="stateUnchanged">
</activity>
tools:node="remove"
นำองค์ประกอบนี้ออกจากไฟล์ Manifest ที่ผสานแล้ว ใช้เมื่อคุณพบองค์ประกอบในไฟล์ Manifest ที่ผสานแล้วซึ่งคุณไม่ต้องการ และองค์ประกอบนั้นมาจากไฟล์ Manifest ที่มีลำดับความสำคัญต่ำกว่าซึ่งอยู่นอกเหนือการควบคุมของคุณ (เช่น ไลบรารีที่นำเข้า)

ไฟล์ Manifest ที่มีความสำคัญต่ำ

<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>

ไฟล์ Manifest ที่มีความสำคัญสูง:

<activity-alias android:name="com.example.alias">
  <meta-data android:name="cow"
      tools:node="remove"/>
</activity-alias>

ผลลัพธ์ของไฟล์ Manifest ที่ผสานแล้ว:

<activity-alias android:name="com.example.alias">
  <meta-data android:name="duck"
      android:value="@string/quack"/>
</activity-alias>
tools:node="removeAll"
คล้ายกับ tools:node="remove" แต่จะนำองค์ประกอบทั้งหมดที่ตรงกับประเภทองค์ประกอบนี้ออก (ภายในองค์ประกอบหลักเดียวกัน)

ไฟล์ Manifest ที่มีความสำคัญต่ำ

<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>

ไฟล์ Manifest ที่มีความสำคัญสูง:

<activity-alias android:name="com.example.alias">
  <meta-data tools:node="removeAll"/>
</activity-alias>

ผลลัพธ์ของไฟล์ Manifest ที่ผสานแล้ว:

<activity-alias android:name="com.example.alias">
</activity-alias>
tools:node="replace"
แทนที่องค์ประกอบที่มีลำดับความสำคัญต่ำกว่าโดยสมบูรณ์ กล่าวคือ หากมีองค์ประกอบที่ตรงกันในไฟล์ Manifest ที่มีลำดับความสำคัญต่ำกว่า ให้ละเว้นองค์ประกอบนั้นและใช้องค์ประกอบนี้ตามที่ปรากฏในไฟล์ Manifest นี้ทุกประการ

ไฟล์ Manifest ที่มีความสำคัญต่ำ

<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>

ไฟล์ Manifest ที่มีความสำคัญสูง:

<activity-alias android:name="com.example.alias"
    tools:node="replace">
  <meta-data android:name="fox"
      android:value="@string/dingeringeding"/>
</activity-alias>

ผลลัพธ์ของไฟล์ Manifest ที่ผสานแล้ว:

<activity-alias android:name="com.example.alias">
  <meta-data android:name="fox"
      android:value="@string/dingeringeding"/>
</activity-alias>
tools:node="strict"
สร้างความล้มเหลวในการบิลด์ทุกครั้งที่องค์ประกอบนี้ในไฟล์ Manifest ที่มีลำดับความสำคัญต่ำกว่าไม่ตรงกับองค์ประกอบในไฟล์ Manifest ที่มีลำดับความสำคัญสูงกว่าทุกประการ (เว้นแต่จะแก้ไขโดยเครื่องหมายกฎการผสานอื่นๆ) การดำเนินการนี้จะลบล้างการวิเคราะห์พฤติกรรมความขัดแย้งในการผสาน เช่น หากไฟล์ Manifest ที่มีลำดับความสำคัญต่ำกว่ามีแอตทริบิวต์เพิ่มเติม การสร้างจะดำเนินการไม่สำเร็จ (ในขณะที่ลักษณะการทำงานเริ่มต้นจะเพิ่มแอตทริบิวต์เพิ่มเติมลงในไฟล์ Manifest ที่ผสานแล้ว)

ไฟล์ Manifest ที่มีความสำคัญต่ำ

<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>

ไฟล์ Manifest ที่มีความสำคัญสูง:

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

ซึ่งจะทำให้เกิดข้อผิดพลาดในการผสานไฟล์ Manifest องค์ประกอบไฟล์ Manifest ทั้ง 2 รายการต้องไม่แตกต่างกันเลยในโหมดเข้มงวด คุณต้องใช้เครื่องหมายกฎการผสานอื่นๆ เพื่อแก้ไขความแตกต่างเหล่านี้ (หากไม่มี tools:node="strict" ไฟล์ทั้งสองนี้จะผสานรวมกันได้โดยไม่มีข้อผิดพลาด ดังที่แสดงในตัวอย่างสำหรับ tools:node="merge")

เครื่องหมายระบุแหล่งที่มา

หากต้องการใช้กฎการผสานกับแอตทริบิวต์ที่เฉพาะเจาะจงในแท็กไฟล์ Manifest แทน ให้ใช้แอตทริบิวต์ต่อไปนี้ แอตทริบิวต์แต่ละรายการจะยอมรับชื่อแอตทริบิวต์อย่างน้อย 1 ชื่อ (รวมถึงเนมสเปซแอตทริบิวต์) โดยคั่นด้วยคอมมา

tools:remove="attr, ..."
นำแอตทริบิวต์ที่ระบุออกจากไฟล์ Manifest ที่ผสานแล้ว ใช้เมื่อไฟล์ Manifest ที่มีลำดับความสำคัญต่ำกว่ามีแอตทริบิวต์เหล่านี้ และคุณต้องการตรวจสอบว่าแอตทริบิวต์เหล่านี้จะไม่รวมอยู่ในไฟล์ Manifest ที่ผสานแล้ว

ไฟล์ Manifest ที่มีความสำคัญต่ำ

<activity android:name="com.example.ActivityOne"
    android:windowSoftInputMode="stateUnchanged">

ไฟล์ Manifest ที่มีความสำคัญสูง:

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

ผลลัพธ์ของไฟล์ Manifest ที่ผสานแล้ว:

<activity android:name="com.example.ActivityOne"
    android:screenOrientation="portrait">
tools:replace="attr, ..."
แทนที่แอตทริบิวต์ที่ระบุในไฟล์ Manifest ที่มีลำดับความสำคัญต่ำกว่าด้วยแอตทริบิวต์จากไฟล์ Manifest นี้ กล่าวคือ ให้เก็บค่าของไฟล์ Manifest ที่มีลำดับความสำคัญสูงกว่าไว้เสมอ

ไฟล์ Manifest ที่มีความสำคัญต่ำ

<activity android:name="com.example.ActivityOne"
    android:theme="@oldtheme"
    android:exported="false"
    android:windowSoftInputMode="stateUnchanged">

ไฟล์ Manifest ที่มีความสำคัญสูง:

<activity android:name="com.example.ActivityOne"
    android:theme="@newtheme"
    android:exported="true"
    android:screenOrientation="portrait"
    tools:replace="android:theme,android:exported">

ผลลัพธ์ของไฟล์ Manifest ที่ผสานแล้ว:

<activity android:name="com.example.ActivityOne"
    android:theme="@newtheme"
    android:exported="true"
    android:screenOrientation="portrait"
    android:windowSoftInputMode="stateUnchanged">
tools:strict="attr, ..."
สร้างข้อผิดพลาดในการบิลด์ทุกครั้งที่แอตทริบิวต์เหล่านี้ในไฟล์ Manifest ที่มีลำดับความสำคัญต่ำกว่าไม่ตรงกับแอตทริบิวต์ในไฟล์ Manifest ที่มีลำดับความสำคัญสูงกว่าทุกประการ ลักษณะการทำงานนี้เป็นลักษณะการทำงานเริ่มต้นสำหรับแอตทริบิวต์ทั้งหมด ยกเว้นแอตทริบิวต์ที่มีลักษณะการทำงานพิเศษตามที่อธิบายไว้ในการวิเคราะห์ฮิวริสติกของความขัดแย้งในการผสาน

ไฟล์ Manifest ที่มีความสำคัญต่ำ

<activity android:name="com.example.ActivityOne"
    android:screenOrientation="landscape">
</activity>

ไฟล์ Manifest ที่มีความสำคัญสูง:

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

ซึ่งจะทำให้เกิดข้อผิดพลาดในการผสานไฟล์ Manifest คุณต้องใช้เครื่องหมายกฎการผสานอื่นๆ เพื่อแก้ไขความขัดแย้ง นี่คือลักษณะการทำงานเริ่มต้น ดังนั้นผลลัพธ์เดียวกันจะเกิดขึ้นเมื่อเพิ่ม tools:strict="screenOrientation" อย่างชัดเจน

นอกจากนี้ คุณยังใช้เครื่องหมายหลายรายการกับองค์ประกอบเดียวได้ด้วย ดังที่แสดงในตัวอย่างต่อไปนี้

ไฟล์ Manifest ที่มีความสำคัญต่ำ

<activity android:name="com.example.ActivityOne"
    android:theme="@oldtheme"
    android:exported="false"
    android:allowTaskReparenting="true"
    android:windowSoftInputMode="stateUnchanged">

ไฟล์ Manifest ที่มีความสำคัญสูง:

<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">

ผลลัพธ์ของไฟล์ Manifest ที่ผสานแล้ว:

<activity android:name="com.example.ActivityOne"
    android:theme="@newtheme"
    android:exported="true"
    android:allowTaskReparenting="true"
    android:screenOrientation="portrait">

ตัวเลือกเครื่องหมาย

หากต้องการใช้เครื่องหมายกฎการผสานกับไลบรารีที่นำเข้าที่เฉพาะเจาะจงเท่านั้น ให้เพิ่มแอตทริบิวต์ tools:selector พร้อมชื่อแพ็กเกจไลบรารี

ตัวอย่างเช่น ในไฟล์ Manifest ต่อไปนี้ ระบบจะใช้กฎการผสาน remove เฉพาะเมื่อไฟล์ Manifest ที่มีลำดับความสำคัญต่ำกว่ามาจากไลบรารี com.example.lib1

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

หากไฟล์ Manifest ที่มีลำดับความสำคัญต่ำกว่ามาจากแหล่งที่มาอื่น ระบบจะไม่สนใจกฎการผสาน remove

ลบล้าง <uses-sdk> สำหรับไลบรารีที่นำเข้า

โดยค่าเริ่มต้น เมื่อนำเข้าไลบรารีที่มีค่า minSdk สูงกว่าไฟล์ Manifest หลัก จะเกิดข้อผิดพลาดและนำเข้าไลบรารีไม่ได้

หากต้องการให้เครื่องมือผสานละเว้นความขัดแย้งนี้และนำเข้าไลบรารีขณะที่ยังคงค่า minSdk ที่ต่ำกว่าของแอปไว้ ให้เพิ่มแอตทริบิวต์ overrideLibrary ลงในแท็ก <uses-sdk> ค่าแอตทริบิวต์อาจเป็นชื่อแพ็กเกจไลบรารีอย่างน้อย 1 ชื่อ (คั่นด้วยคอมมา) ซึ่งระบุไลบรารีที่ลบล้าง minSdk ของไฟล์ Manifest หลักได้

ตัวอย่างเช่น หากไฟล์ Manifest หลักของแอปใช้ overrideLibrary ดังนี้

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
          package="com.example.app"
          xmlns:tools="http://schemas.android.com/tools">
  <uses-sdk tools:overrideLibrary="com.example.lib1, com.example.lib2"/>
...

จากนั้นไฟล์ Manifest ต่อไปนี้จะผสานรวมได้โดยไม่มีข้อผิดพลาดเกี่ยวกับแท็ก <uses-sdk> และไฟล์ Manifest ที่ผสานแล้วจะเก็บ minSdk="2" จากไฟล์ Manifest ของแอปไว้

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

สิทธิ์ของระบบโดยนัย

Android API บางรายการที่แอปเคยเข้าถึงได้อย่างอิสระถูกจำกัดโดยสิทธิ์ของระบบใน Android เวอร์ชันล่าสุด

เพื่อหลีกเลี่ยงไม่ให้แอปที่คาดว่าจะเข้าถึง API เหล่านี้หยุดทำงาน Android เวอร์ชันล่าสุดจึงอนุญาตให้แอปเข้าถึง API เหล่านั้นต่อไปได้โดยไม่ต้องขอสิทธิ์ หากตั้งค่า targetSdkVersion เป็นค่าที่ต่ำกว่าเวอร์ชันที่มีการเพิ่มข้อจำกัด ลักษณะการทำงานนี้จะให้สิทธิ์แอปโดยนัยเพื่ออนุญาตให้เข้าถึง API ไฟล์ Manifest ที่ผสานแล้วซึ่งมีค่า targetSdkVersion ต่างกันอาจได้รับผลกระทบ

หากไฟล์ Manifest ที่มีลำดับความสำคัญต่ำกว่ามีค่า targetSdkVersion ต่ำกว่าซึ่งให้สิทธิ์โดยนัย และไฟล์ Manifest ที่มีลำดับความสำคัญสูงกว่าไม่มีสิทธิ์โดยนัยเดียวกัน (เนื่องจาก targetSdkVersion มีค่าเท่ากับหรือสูงกว่าเวอร์ชันที่มีการเพิ่มข้อจำกัด) เครื่องมือผสานจะเพิ่มสิทธิ์ของระบบลงในไฟล์ Manifest ที่ผสานแล้วอย่างชัดแจ้ง

ตัวอย่างเช่น หากแอปตั้งค่า targetSdkVersion เป็น 4 ขึ้นไปและนำเข้าไลบรารีที่ตั้งค่า targetSdkVersion เป็น 3 หรือต่ำกว่า เครื่องมือผสานจะเพิ่มสิทธิ์ WRITE_EXTERNAL_STORAGE ลงในไฟล์ Manifest ที่ผสานแล้ว

ตารางที่ 2 แสดงสิทธิ์ทั้งหมดที่เป็นไปได้ซึ่งสามารถเพิ่มลงในไฟล์ Manifest ที่ผสานแล้ว

ตารางที่ 2 รายการสิทธิ์ที่เครื่องมือผสานอาจเพิ่มลงในไฟล์ Manifest ที่ผสานแล้ว

ไฟล์ Manifest ที่มีลำดับความสำคัญต่ำกว่าจะประกาศ เพิ่มสิทธิ์ลงในไฟล์ Manifest ที่ผสานแล้ว
targetSdkVersion มีค่าเท่ากับ 3 หรือต่ำกว่า WRITE_EXTERNAL_STORAGE, READ_PHONE_STATE
targetSdkVersion มีอายุไม่เกิน 15 ปีและใช้ READ_CONTACTS READ_CALL_LOG
targetSdkVersion มีอายุไม่เกิน 15 ปีและใช้ WRITE_CONTACTS WRITE_CALL_LOG

ตรวจสอบไฟล์ Manifest ที่ผสานแล้วและค้นหาความขัดแย้ง

คุณสามารถดูตัวอย่างไฟล์ Manifest ที่ผสานแล้วได้ก่อนที่จะสร้างแอป หากต้องการดูตัวอย่าง ให้ทำดังนี้

  1. เปิดไฟล์ AndroidManifest.xml ใน Android Studio
  2. คลิกแท็บ ไฟล์ Manifest ที่ผสานแล้ว ที่ด้านล่างของเครื่องมือแก้ไข

มุมมองไฟล์ Manifest ที่ผสานแล้วจะแสดงผลลัพธ์ของไฟล์ Manifest ที่ผสานแล้วทางด้านซ้ายและข้อมูลเกี่ยวกับไฟล์ Manifest ที่ผสานแล้วแต่ละไฟล์ทางด้านขวา ดังที่แสดงในรูปที่ 2

องค์ประกอบที่ผสานจากไฟล์ Manifest ที่มีลำดับความสำคัญต่ำกว่าจะไฮไลต์ด้วยสีต่างๆ ทางด้านซ้าย คีย์ของแต่ละสีจะระบุไว้ในส่วนแหล่งที่มาของไฟล์ Manifest

มุมมองไฟล์ Manifest ที่ผสานแล้ว
รูปที่ 2 มุมมองไฟล์ Manifest ที่ผสานแล้ว

ไฟล์ Manifest ที่เป็นส่วนหนึ่งของบิลด์แต่ไม่ได้มีส่วนร่วมในองค์ประกอบหรือแอตทริบิวต์จะแสดงอยู่ในส่วนไฟล์ Manifest อื่นๆ

หากต้องการดูข้อมูลเกี่ยวกับแหล่งที่มาขององค์ประกอบ ให้คลิกองค์ประกอบนั้นในแผงด้านซ้าย แล้วรายละเอียดจะปรากฏในส่วนบันทึกการผสาน

หากเกิดความขัดแย้ง ระบบจะแสดงความขัดแย้งนั้นในส่วนข้อผิดพลาดในการผสานพร้อมคำแนะนำเกี่ยวกับวิธีแก้ไขความขัดแย้งโดยใช้เครื่องหมายกฎการผสาน

นอกจากนี้ ข้อผิดพลาดยังแสดงในหน้าต่างบันทึกเหตุการณ์ด้วย หากต้องการดู ให้เลือกดู > หน้าต่างเครื่องมือ > บันทึกเหตุการณ์

หากต้องการดูบันทึกทั้งหมดของแผนผังการตัดสินใจในการผสาน คุณจะดูไฟล์บันทึกได้ในไดเรกทอรี build/outputs/logs/ ของโมดูลซึ่งมีชื่อว่า manifest-merger-buildVariant-report.txt

นโยบายการผสานรวม

เครื่องมือผสานไฟล์ Manifest สามารถจับคู่ทุกองค์ประกอบ XML จากไฟล์ Manifest หนึ่งกับองค์ประกอบที่เกี่ยวข้องในอีกไฟล์หนึ่งได้อย่างสมเหตุสมผล เครื่องมือผสานจะจับคู่แต่ละองค์ประกอบโดยใช้คีย์การจับคู่ ซึ่งเป็นค่าแอตทริบิวต์ที่ไม่ซ้ำกัน (เช่น android:name) หรือลักษณะเฉพาะตามธรรมชาติของแท็กเอง (เช่น มีองค์ประกอบ <supports-screen> ได้เพียงรายการเดียว)

หากไฟล์ Manifest 2 ไฟล์มีองค์ประกอบ XML เดียวกัน เครื่องมือจะผสานองค์ประกอบทั้งสองเข้าด้วยกันโดยใช้นโยบายการผสานอย่างใดอย่างหนึ่งจาก 3 นโยบายต่อไปนี้

รวม
รวมแอตทริบิวต์ทั้งหมดที่ไม่ขัดแย้งกันไว้ในแท็กเดียวกัน และผสานองค์ประกอบย่อยตามนโยบายการผสานที่เกี่ยวข้อง หากแอตทริบิวต์ขัดแย้งกัน ให้รวมแอตทริบิวต์เข้าด้วยกันโดยใช้เครื่องหมายกฎการรวม
ผสานเฉพาะรายการย่อย
อย่ารวมหรือผสานแอตทริบิวต์ (เก็บเฉพาะแอตทริบิวต์ที่ระบุโดยไฟล์ Manifest ที่มีลำดับความสำคัญสูงสุด) และผสานองค์ประกอบย่อยตามนโยบายการผสาน
Keep
ปล่อยองค์ประกอบไว้ตามเดิมและเพิ่มลงในองค์ประกอบระดับบนที่ใช้ร่วมกันในไฟล์ที่ผสานรวม ซึ่งจะใช้เฉพาะเมื่อยอมรับได้ว่ามีการประกาศองค์ประกอบเดียวกันหลายรายการ

ตารางที่ 3 แสดงองค์ประกอบแต่ละประเภท ประเภทของนโยบายการผสานที่ใช้ และคีย์ที่ใช้เพื่อระบุการจับคู้องค์ประกอบระหว่างไฟล์ Manifest 2 รายการ

ตารางที่ 3 นโยบายการผสานองค์ประกอบไฟล์ Manifest และคีย์การจับคู่

องค์ประกอบ นโยบายการผสานรวม คีย์การจับคู่
<action> รวม แอตทริบิวต์ android:name
<activity> รวม แอตทริบิวต์ android:name
<application> รวม โดยจะมีเพียง 1 รายการต่อ <manifest>
<category> รวม แอตทริบิวต์ android:name
<data> รวม โดยจะมีเพียง 1 รายการต่อ <intent-filter>
<grant-uri-permission> รวม โดยจะมีเพียง 1 รายการต่อ <provider>
<instrumentation> รวม แอตทริบิวต์ android:name
<intent-filter> Keep ไม่มีการจับคู่ อนุญาตให้มีการประกาศหลายรายการภายในองค์ประกอบหลัก
<manifest> ผสานเฉพาะรายการย่อย โดยจะมีเพียง 1 รายการต่อไฟล์
<meta-data> รวม แอตทริบิวต์ android:name
<path-permission> รวม โดยจะมีเพียง 1 รายการต่อ <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> รวม โดยจะมีเพียง 1 รายการต่อ <manifest>
<uses-configuration> รวม โดยจะมีเพียง 1 รายการต่อ <manifest>
<uses-feature> รวม แอตทริบิวต์ android:name (หากไม่มี ให้ใช้แอตทริบิวต์ android:glEsVersion)
<uses-library> รวม แอตทริบิวต์ android:name
<uses-permission> รวม แอตทริบิวต์ android:name
<uses-sdk> รวม โดยจะมีเพียง 1 รายการต่อ <manifest>
องค์ประกอบที่กำหนดเอง รวม ไม่มีการจับคู่ เครื่องมือผสานจะไม่รู้จักองค์ประกอบเหล่านี้และจะรวมไว้ในไฟล์ Manifest ที่ผสานแล้วเสมอ

แทรกตัวแปรบิวด์เข้าไปในไฟล์ Manifest

หากต้องการแทรกตัวแปรลงในไฟล์ AndroidManifest.xml ที่กำหนดไว้ในไฟล์ build.gradle คุณสามารถทำได้โดยใช้พร็อพเพอร์ตี้ manifestPlaceholders พร็อพเพอร์ตี้นี้ใช้แมปของคู่คีย์-ค่า ดังที่แสดงที่นี่

ดึงดูด

android {
    defaultConfig {
        manifestPlaceholders = [hostName:"www.example.com"]
    }
    ...
}

Kotlin

android {
    defaultConfig {
        manifestPlaceholders["hostName"] = "www.example.com"
    }
    ...
}

จากนั้นคุณสามารถแทรกตัวยึดตำแหน่งรายการใดรายการหนึ่งลงในไฟล์ Manifest เป็นค่าแอตทริบิวต์ได้ ดังนี้

<intent-filter ... >
    <data android:scheme="https" android:host="${hostName}" ... />
    ...
</intent-filter>

โดยค่าเริ่มต้น เครื่องมือสร้างจะระบุรหัสแอปพลิเคชันของแอปในตัวยึดตำแหน่ง ${applicationId} ด้วย ค่านี้จะตรงกับรหัสแอปพลิเคชันสุดท้ายสำหรับบิลด์ปัจจุบันเสมอ รวมถึงการเปลี่ยนแปลงตามตัวแปรบิลด์ ซึ่งจะมีประโยชน์เมื่อคุณต้องการใช้เนมสเปซที่ไม่ซ้ำกันสำหรับตัวระบุ เช่น การดำเนินการตาม Intent แม้จะอยู่ระหว่างตัวแปรบิลด์ก็ตาม

เช่น หากไฟล์ build.gradle มีลักษณะดังนี้

ดึงดูด

android {
    defaultConfig {
        applicationId "com.example.myapp"
    }
    flavorDimensions "type"
    productFlavors {
        free {
            applicationIdSuffix ".free"
            dimension "type"
        }
        pro {
            applicationIdSuffix ".pro"
            dimension "type"
        }
    }
}

Kotlin

android {
    defaultConfig {
        applicationId = "com.example.myapp"
    }
    flavorDimensions += "type"
    productFlavors {
        create("free") {
            applicationIdSuffix = ".free"
            dimension = "type"
        }
        create("pro") {
            applicationIdSuffix = ".pro"
            dimension = "type"
        }
    }
}

จากนั้นคุณจะแทรกรหัสแอปพลิเคชันในไฟล์ Manifest ได้ดังนี้

<intent-filter ... >
    <action android:name="${applicationId}.TRANSMOGRIFY" />
    ...
</intent-filter>

และผลลัพธ์ของไฟล์ Manifest เมื่อคุณสร้างเวอร์ชันผลิตภัณฑ์ "ฟรี" จะเป็นดังนี้

<intent-filter ... >
   <action android:name="com.example.myapp.free.TRANSMOGRIFY" />
    ...
</intent-filter>

อ่านข้อมูลเพิ่มเติมได้ที่หัวข้อตั้งค่ารหัสแอปพลิเคชัน