Quản lý tệp kê khai

Sử dụng bộ sưu tập để sắp xếp ngăn nắp các trang Lưu và phân loại nội dung dựa trên lựa chọn ưu tiên của bạn.

Trang này mô tả cách các công cụ xây dựng của chúng tôi nhập tệp kê khai để xác định giao diện và trải nghiệm cuối cùng cho ứng dụng của bạn.

Để biết giới thiệu về tệp kê khai ứng dụng, hãy xem tổng quan về tệp kê khai ứng dụng.

Hợp nhất nhiều tệp kê khai

Tệp APK hoặc Android App Bundle của bạn chỉ có thể chứa một tệp AndroidManifest.xml, nhưng dự án Android Studio có thể chứa một số tệp – do tập hợp nguồn chính, các biến thể bản dựng và thư viện nhập vào cung cấp. Vì vậy, khi tạo ứng dụng, bản dựng Gradle sẽ hợp nhất tất cả các tệp kê khai vào một tệp kê khai duy nhất được đóng gói vào ứng dụng của bạn.

Công cụ hợp nhất tệp kê khai kết hợp tất cả các phần tử XML từ mỗi tệp bằng cách theo một số phỏng đoán hợp nhất và bằng cách tuân theo các tùy chọn hợp nhất mà bạn đã xác định với các thuộc tính XML đặc biệt. Trang này mô tả cách hoạt động hợp nhất tệp kê khai hoạt động và cách bạn có thể áp dụng tùy chọn hợp nhất để giải quyết các xung đột về việc hợp nhất.

Mẹo: Sử dụngTệp kê khai đã hợp nhất xem để xem trước kết quả của tệp kê khai hợp nhất và tìm các lỗi xung đột.

Hợp nhất các mức độ ưu tiên

Công cụ hợp nhất kết hợp tất cả các tệp kê khai vào một tệp bằng cách hợp nhất các tệp này theo tuần tự dựa trên mức độ ưu tiên của từng tệp kê khai. Ví dụ: nếu bạn có ba tệp kê khai, tệp kê khai có mức độ ưu tiên thấp nhất sẽ được hợp nhất vào mức độ ưu tiên cao nhất tiếp theo và sau đó được hợp nhất vào tệp kê khai có mức độ ưu tiên cao nhất, như được minh họa trong hình 1.

Hình 1. Quá trình hợp nhất ba tệp kê khai, mức độ ưu tiên thấp nhất (bên trái) thành mức độ ưu tiên cao nhất (bên phải)

Có ba loại tệp kê khai cơ bản mà bạn có thể hợp nhất vào từng tệp khác, và mức độ ưu tiên hợp nhất của các tệp đó như sau (ưu tiên cao nhất trước):

  1. Tệp kê khai cho biến thể bản dựng của bạn

    Nếu bạn có nhiều tập hợp nguồn cho biến thể của mình, mức độ ưu tiên của các tệp kê khai sẽ như sau:

    1. Xây dựng tệp kê khai biến thể (chẳng hạn như src/demoDebug/)
    2. Loại tệp kê khai loại bản dựng (chẳng hạn như src/debug/)
    3. Tệp kê khai hương vị sản phẩm (chẳng hạn như src/demo/)

      Nếu bạn đang sử dụng các thứ nguyên hương vị, mức độ ưu tiên của tệp kê khai sẽ tương ứng với thứ tự được liệt kê trong thuộc tính flavorDimensions (thứ nhất là mức độ ưu tiên cao nhất).

  2. Tệp kê khai chính cho mô-đun ứng dụng
  3. Tệp lưu trữ từ một thư viện đi kèm

    Nếu bạn có nhiều thư viện, mức độ ưu tiên của tệp kê khai sẽ khớp với thứ tự phần phụ thuộc (thứ tự xuất hiện trong khối dependencies của Gradle).

Ví dụ: một tệp kê khai thư viện được hợp nhất vào tệp kê khai chính, sau đó tệp kê khai chính được hợp nhất vào tệp kê khai biến thể bản dựng. Lưu ý rằng đây là các ưu tiên hợp nhất cho tất cả các bộ nguồn, như được mô tả trong Tạo bằng bộ nguồn.

Quan trọng: Xây dựng cấu hình từ tệp build.gradle sẽ ghi đè mọi thuộc tính tương ứng trong tệp kê khai hợp nhất. Ví dụ: minSdkVersion từ tệp build.gradle sẽ ghi đè thuộc tính so khớp trong phần tử tệp kê khai <uses-sdk>. Để tránh gây nhầm lẫn, bạn chỉ cần bỏ phần tử <uses-sdk> và chỉ xác định các thuộc tính này trong tệp build.gradle. Để biết thêm thông tin chi tiết, hãy xem phần Định cấu hình bản dựng.

Hợp nhất giả mạo xung đột

Công cụ hợp nhất có thể khớp mọi phần tử XML từ một tệp kê khai với phần tử tương ứng trong tệp kê khai khác một cách hợp lý. (Để biết chi tiết về cách hoạt động của tính năng so khớp, hãy xem chính sách hợp nhất).

Nếu một phần tử của tệp kê khai có mức độ ưu tiên thấp hơn không khớp với bất kỳ phần tử nào trong tệp kê khai có mức độ ưu tiên cao hơn thì phần tử đó sẽ được thêm vào tệp kê khai hợp nhất. Tuy nhiên, nếu một phần tử phù hợp, thì công cụ hợp nhất sẽ cố gắng kết hợp tất cả các thuộc tính từ mỗi phần tử đó thành cùng một phần tử. Nếu công cụ này phát hiện thấy cả hai tệp kê khai chứa cùng một thuộc tính có các giá trị khác nhau, thì xung đột hợp nhất sẽ xảy ra.

Bảng 1 mô tả các kết quả có thể xảy ra khi công cụ hợp nhất cố gắng kết hợp tất cả các thuộc tính trong cùng một phần tử.

Bảng 1. Hành vi hợp nhất mặc định cho các giá trị thuộc tính

Thuộc tính mức độ ưu tiên cao Thuộc tính mức độ ưu tiên thấp Kết quả được hợp nhất của thuộc tính
Không có giá trị Không có giá trị Không có giá trị (sử dụng giá trị mặc định)
Giá trị B Giá trị B
Giá trị A Không có giá trị Giá trị A
Giá trị A Giá trị A
Giá trị B Lỗi xung đột – bạn phải thêm một mã đánh dấu quy tắc hợp nhất

Tuy nhiên, có một vài tình huống mà công cụ hợp nhất hoạt động theo cách khác nhau để tránh xung đột hợp nhất:

  • Các thuộc tính trong phần tử <manifest> không bao giờ được hợp nhất với nhau mà chỉ sử dụng các thuộc tính từ tệp kê khai có mức độ ưu tiên cao nhất.
  • Thuộc tính android:required trong các phần tử <uses-feature><uses-library> sử dụng HO nên khi áp dụng xung đột, "true" sẽ được áp dụng và tính năng hoặc thư viện mà một tệp kê khai yêu cầu phải luôn bao gồm.
  • Các thuộc tính trong phần tử <uses-sdk> luôn sử dụng giá trị từ tệp kê khai có mức độ ưu tiên cao hơn, ngoại trừ các trường hợp sau:
    • Khi tệp kê khai có mức độ ưu tiên thấp hơn có giá trị minSdkVersion cao hơn, sẽ xảy ra lỗi trừ khi bạn áp dụng quy tắc hợp nhất overrideLibrary.
    • Khi tệp kê khai có mức độ ưu tiên thấp hơn có giá trị targetSdkVersionthấp hơn, Công cụ sáp nhập sử dụng giá trị từ manifest có mức ưu tiên cao hơn, nhưng nó cũng cho biết thêm bất kỳ khoản hệ thống đó là cần thiết để đảm bảo rằng các thư viện nhập khẩu tiếp tục hoạt động đúng (đối với trường hợp trong đó cao hơn Phiên bản Android đã tăng hạn chế về quyền). Để biết thêm thông tin về hành vi này, hãy xem mục các quyền hệ thống ngầm.
  • Phần tử <intent-filter> không bao giờ khớp giữa các tệp kê khai. Mỗi tệp được xem là duy nhất và được thêm vào phần tử mẹ chung trong tệp kê khai hợp nhất.

Đối với tất cả các xung đột khác giữa các thuộc tính, bạn sẽ gặp lỗi và bạn phải hướng dẫn công cụ hợp nhất cách giải quyết lỗi đó bằng cách thêm thuộc tính đặc biệt vào tệp kê khai có mức độ ưu tiên cao hơn (xem phần tiếp theo vềcác điểm đánh dấu quy tắc hợp nhất ).

Không phụ thuộc vào giá trị thuộc tính mặc định. Bởi vì tất cả các thuộc tính duy nhất được kết hợp thành cùng một phần tử, nên điều này có thể gây ra kết quả không mong muốn nếu tệp kê khai có mức độ ưu tiên cao hơn thực sự phụ thuộc vào giá trị mặc định của thuộc tính mà không khai báo. Ví dụ: nếu tệp kê khai có mức độ ưu tiên cao hơn không khai báo thuộc tính android:launchMode, thì tệp kê khai sẽ sử dụng giá trị mặc định là "standard"; nhưng nếu tệp kê khai có mức độ ưu tiên thấp hơn khai báo thuộc tính này với giá trị khác thì giá trị đó sẽ được áp dụng cho tệp kê khai hợp nhất (ghi đè giá trị mặc định). Vì vậy bạn nên xác định rõ ràng từng thuộc tính theo ý muốn. (Giá trị mặc định cho mỗi thuộc tính được đề cập trong Tài liệu tham khảo tại mục.)

Hợp nhất các điểm đánh dấu quy tắc

Điểm đánh dấu quy tắc hợp nhất là một thuộc tính XML mà bạn có thể sử dụng để thể hiện mong muốn của mình về cách giải quyết các xung đột hợp nhất hoặc xóa các phần tử và thuộc tính không mong muốn. Bạn có thể áp dụng một điểm đánh dấu cho toàn bộ phần tử hoặc chỉ các thuộc tính cụ thể trong một phần tử.

Khi hợp nhất hai tệp kê khai, công cụ hợp nhất tìm các điểm đánh dấu này trong tệp kê khai có mức độ ưu tiên cao hơn.

Tất cả các điểm đánh dấu đều thuộc tools vùng chứa tên Android, do đó, trước tiên bạn phải khai báo không gian tên này trong phần tử <manifest> như minh họa dưới đây:

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.example.myapp"
    xmlns:tools="http://schemas.android.com/tools">

Điểm đánh dấu nút

Để áp dụng quy tắc hợp nhất cho toàn bộ phần tử XML (đối với tất cả các thuộc tính trong một phần tử tệp kê khai nhất định và cho tất cả các thẻ con của phần tử đó), hãy sử dụng các thuộc tính sau:

tools:node="merge"
Hợp nhất tất cả các thuộc tính trong thẻ này và tất cả các phần tử lồng nhau khi không có xung đột bằng cách sử dụng hợp nhất phỏng đoán xung đột. Đây là hành vi mặc định cho các phần tử.

Tệp kê khai có mức độ ưu tiên thấp:

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

Tệp kê khai có mức độ ưu tiên cao:

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

Kết quả tệp kê khai hợp nhất:

<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"
Chỉ hợp nhất các thuộc tính trong thẻ này; không hợp nhất các phần tử lồng nhau.

Tệp kê khai có mức độ ưu tiên thấp:

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

Tệp kê khai có mức độ ưu tiên cao:

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

Kết quả tệp kê khai hợp nhất:

<activity android:name="com.example.ActivityOne"
    android:screenOrientation="portrait"
    android:windowSoftInputMode="stateUnchanged">
</activity>
tools:node="remove"
Xóa phần tử này khỏi tệp kê khai đã hợp nhất. Mặc dù có vẻ như bạn chỉ nên xóa phần tử này, nhưng việc này là cần thiết khi bạn phát hiện một phần tử trong tệp kê khai hợp nhất mà bạn không cần và phần tử đó do mức độ ưu tiên thấp hơn cung cấp tệp kê khai nằm ngoài tầm kiểm soát của bạn (chẳng hạn như một thư viện đã nhập).

Tệp kê khai có mức độ ưu tiên thấp:

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

Tệp kê khai có mức độ ưu tiên cao:

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

Kết quả tệp kê khai hợp nhất:

<activity-alias android:name="com.example.alias">
  <meta-data android:name="duck"
      android:value="@string/quack"/>
</activity-alias>
tools:node="removeAll"
Cũng giống như tools:node="remove", nhưng loại bỏ tất cả các phần tử khớp với loại phần tử này (trong cùng một phần tử mẹ).

Tệp kê khai có mức độ ưu tiên thấp:

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

Tệp kê khai có mức độ ưu tiên cao:

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

Kết quả tệp kê khai hợp nhất:

<activity-alias android:name="com.example.alias">
</activity-alias>
tools:node="replace"
Thay thế hoàn toàn phần tử có mức độ ưu tiên thấp hơn. Nghĩa là nếu có một phần tử phù hợp trong tệp kê khai có mức độ ưu tiên thấp hơn, hãy bỏ qua và sử dụng phần tử này chính xác như phần tử đó trong tệp kê khai.

Tệp kê khai có mức độ ưu tiên thấp:

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

Tệp kê khai có mức độ ưu tiên cao:

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

Kết quả tệp kê khai hợp nhất:

<activity-alias android:name="com.example.alias">
  <meta-data android:name="fox"
      android:value="@string/dingeringeding"/>
</activity-alias>
tools:node="strict"
Tạo lỗi trong bản dựng bất cứ khi nào phần tử này trong tệp kê khai có mức độ ưu tiên thấp hơn không khớp chính xác với tệp đó trong tệp kê khai có mức độ ưu tiên cao hơn (trừ khi đã được giải quyết bằng các điểm đánh dấu quy tắc hợp nhất khác). Thao tác này sẽ ghi đè các phỏng đoán xung đột hợp nhất. Ví dụ: nếu tệp kê khai có mức độ ưu tiên thấp hơn chỉ chứa thuộc tính bổ sung, thì bản dựng sẽ không hoạt động (trong khi đó, hành vi mặc định sẽ thêm thuộc tính bổ sung vào tệp kê khai hợp nhất).

Tệp kê khai có mức độ ưu tiên thấp:

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

Tệp kê khai có mức độ ưu tiên cao:

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

Thao tác này tạo ra lỗi hợp nhất tệp kê khai. Hai phần tử tệp kê khai này không thể khác nhau khi ở chế độ nghiêm ngặt. Vì vậy, bạn phải áp dụng các điểm đánh dấu quy tắc hợp nhất khác để giải quyết các khác biệt này. (Thông thường, hai tệp này sẽ hợp nhất với nhau như như minh hoạ trong ví dụ trên về tools:node="merge".)

Điểm đánh dấu thuộc tính

Thay vào đó, để chỉ áp dụng quy tắc hợp nhất cho các thuộc tính cụ thể trong thẻ tệp kê khai, hãy sử dụng các thuộc tính sau. Mỗi thuộc tính chấp nhận một hoặc nhiều tên thuộc tính (bao gồm cả không gian tên của thuộc tính), phân cách bằng dấu phẩy.

tools:remove="attr, ..."
Xóa các thuộc tính đã chỉ định khỏi tệp kê khai đã hợp nhất. Mặc dù có vẻ như bạn có thể chỉ cần xóa các thuộc tính này, nhưng bạn cần sử dụng thuộc tính này khi tệp kê khai có mức độ ưu tiên thấp hơn các thuộc tính này và bạn cần đảm bảo rằng chúng không chuyển vào tệp kê khai hợp nhất.

Tệp kê khai có mức độ ưu tiên thấp:

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

Tệp kê khai có mức độ ưu tiên cao:

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

Kết quả tệp kê khai hợp nhất:

<activity android:name="com.example.ActivityOne"
    android:screenOrientation="portrait">
tools:replace="attr, ..."
Thay thế các thuộc tính được chỉ định trong tệp kê khai có mức độ ưu tiên thấp hơn bằng các thuộc tính từ tệp kê khai này. Nói cách khác, hãy luôn giữ các giá trị của tệp kê khai có mức độ ưu tiên cao hơn.

Tệp kê khai có mức độ ưu tiên thấp:

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

Tệp kê khai có mức độ ưu tiên cao:

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

Kết quả tệp kê khai hợp nhất:

<activity android:name="com.example.ActivityOne"
    android:theme="@newtheme"
    android:exported="true"
    android:screenOrientation="portrait"
    android:windowSoftInputMode="stateUnchanged">
tools:strict="attr, ..."
Không tạo được bản dựng bất cứ khi nào các thuộc tính này trong tệp kê khai có mức độ ưu tiên thấp hơn không khớp chính xác với thuộc tính đó trong tệp kê khai có mức độ ưu tiên cao hơn. Đây là hành vi mặc định cho tất cả các thuộc tính, ngoại trừ những thuộc tính có hành vi đặc biệt như mô tả trong phương pháp phỏng đoán xung đột.

Tệp kê khai có mức độ ưu tiên thấp:

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

Tệp kê khai có mức độ ưu tiên cao:

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

Thao tác này tạo ra lỗi hợp nhất tệp kê khai. Bạn phải áp dụng các điểm đánh dấu quy tắc hợp nhất khác để giải quyết xung đột. (Lưu ý: Đây là hành vi mặc định, do đó, ví dụ ở trên sẽ có kết quả tương tự nếu bạn xóa tools:strict="screenOrientation".)

Bạn cũng có thể áp dụng nhiều điểm đánh dấu cho một phần tử như sau.

Tệp kê khai có mức độ ưu tiên thấp:

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

Tệp kê khai có mức độ ưu tiên cao:

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

Kết quả tệp kê khai hợp nhất:

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

Bộ chọn điểm đánh dấu

Nếu bạn chỉ muốn áp dụng các điểm đánh dấu quy tắc hợp nhất cho một thư viện nhập cụ thể, hãy thêm thuộc tính tools:selector cùng với tên gói thư viện.

Ví dụ: với tệp kê khai sau đây, quy tắc hợp nhất remove chỉ được áp dụng khi tệp kê khai có mức độ ưu tiên thấp hơn nằm trong thư viện com.example.lib1.

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

Nếu tệp kê khai có mức độ ưu tiên thấp hơn đến từ bất kỳ nguồn nào khác, thì quy tắc hợp nhất remove sẽ bị bỏ qua.

Lưu ý: Nếu bạn sử dụng thuộc tính này với một trong các điểm đánh dấu thuộc tính, thì thuộc tính này sẽ áp dụng cho tất cả các thuộc tính được chỉ định trong điểm đánh dấu.

Ghi đè <uses-sdk> cho các thư viện đã nhập

Theo mặc định, khi nhập một thư viện có giá trị minSdkVersion cao hơn so với tệp kê khai chính, sẽ xảy ra lỗi và không thể nhập thư viện. Để công cụ hợp nhất bỏ qua xung đột này và nhập thư viện trong khi vẫn giữ giá trị minSdkVersion dưới của ứng dụng, hãy thêm thuộc tính overrideLibrary vào thẻ <uses-sdk>. Giá trị thuộc tính có thể là một hoặc nhiều tên gói thư viện (được phân tách bằng dấu phẩy), cho biết các thư viện có thể ghi đè minSdkVersion của tệp kê khai chính.

Ví dụ: nếu tệp kê khai chính của ứng dụng áp dụng overrideLibrary như sau:

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

Sau đó, bạn có thể hợp nhất tệp kê khai sau mà không gặp lỗi liên quan đến thẻ <uses-sdk> và tệp kê khai đã hợp nhất giữ lại minSdkVersion="2" khỏi tệp kê khai ứng dụng.

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

Các quyền ngầm của hệ thống ngầm ẩn

Một số API Android từng là ứng dụng có thể truy cập miễn phí đã bị giới hạn theo quyền hệ thống trong các phiên bản Android gần đây. Để tránh làm hỏng ứng dụng sẽ không cho phép truy cập vào các API này, các phiên bản Android gần đây sẽ cho phép ứng dụng tiếp tục truy cập vào các API đó mà không cần sự cho phép nếu các ứng dụng đó đã đặt targetSdkVersion thành giá trị thấp hơn so với phiên bản mà quy định hạn chế đã được thêm vào. Hành vi này cấp cho ứng dụng một quyền ngầm ẩn để cho phép ứng dụng truy cập vào các API. Do đó, điều này có thể ảnh hưởng đến các tệp kê khai đã hợp nhất có các giá trị khác nhau cho targetSdkVersion theo cách sau.

Nếu tệp kê khai có mức độ ưu tiên thấp hơn có giá trị thấp hơn cho targetSdkVersion cung cấp cho nó quyền truy cập ngầm định và tệp kê khai có mức độ ưu tiên cao hơnkhông có cùng quyền ngầm định (vìtargetSdkVersion bằng hoặc cao hơn phiên bản mà bạn đã thêm quy định hạn chế) thì công cụ hợp nhất một cách rõ ràng thêm quyền hệ thống vào tệp kê khai đã hợp nhất.

Ví dụ: nếu ứng dụng của bạn đặt targetSdkVersion thành 4 trở lên và nhập thư viện có targetSdkVersion được đặt thành 3 trở xuống, thì công cụ hợp nhất sẽ thêm WRITE_EXTERNAL_STORAGE đối với tệp kê khai đã hợp nhất. Bảng 2 liệt kê tất cả các quyền có thể có mà bạn có thể thêm vào tệp kê khai hợp nhất.

Bảng 2. Danh sách các quyền mà công cụ hợp nhất có thể thêm vào tệp kê khai đã hợp nhất

Khai báo tệp kê khai có mức độ ưu tiên thấp hơn Đã thêm các quyền vào tệp kê khai hợp nhất
targetSdkVersion từ 3 trở xuống WRITE_EXTERNAL_STORAGE, READ_PHONE_STATE
targetSdkVersion từ 15 trở xuống và sử dụng READ_CONTACTS READ_CALL_LOG
targetSdkVersion từ 15 trở xuống và sử dụng WRITE_CONTACTS WRITE_CALL_LOG

Kiểm tra tệp kê khai đã hợp nhất và tìm xung đột

Ngay cả trước khi tạo ứng dụng, bạn có thể xem trước giao diện của tệp kê khai đã hợp nhất bằng cách mở tệp AndroidManifest.xml trong Android Studio, sau đó nhấp vào thẻ Tệp kê khai hợp nhất ở cuối trình chỉnh sửa.

Chế độ xem Tệp kê khai hợp nhất hiển thị kết quả của tệp kê khai đã hợp nhất ở bên trái và thông tin về từng tệp kê khai đã hợp nhất ở bên phải, như được hiển thị trong hình 2. Các phần tử được hợp nhất từ các tệp kê khai có mức độ ưu tiên thấp hơn được làm nổi bật bằng các màu khác nhau ở bên trái. Khóa cho mỗi màu được chỉ định trong Nguồn kê khai ở bên phải.

Hình 2. Chế độ xem tệp kê khai hợp nhất

Các tệp kê khai là một phần của bản dựng nhưng không đóng góp cho các phần tử hoặc thuộc tính nào được liệt kê trong phần Tệp kê khai khác ở bên phải.

Để xem thông tin về nguồn gốc của một phần tử, hãy nhấp vào phần tử đó ở bên trái và chi tiết xuất hiện trong Nhật ký hợp nhất ở bên phải.

Nếu có xung đột xảy ra, các xung đột đó xuất hiện trong mục Lỗi hợp nhất ở bên phải và đề xuất cách giải quyết xung đột bằng cách sử dụng các điểm đánh dấu quy tắc hợp nhất. Các lỗi cũng được in trong cửa sổ Nhật ký sự kiện (chọn Xem > Cửa sổ công cụ > Nhật ký sự kiện).

Nếu muốn xem nhật ký đầy đủ của cây quyết định hợp nhất, bạn có thể tìm tệp nhật ký trong thư mục build/outputs/logs/ của mô-đun có tên manifest-merger-buildVariant-report.txt.

Hợp nhất các chính sách

Công cụ hợp nhất tệp kê khai có thể khớp mọi thành phần XML từ một tệp kê khai với một phần tử tương ứng trong một tệp khác một cách hợp lý. Việc hợp nhất khớp mỗi yếu tố bằng cách sử dụng "khoá so khớp": giá trị thuộc tính duy nhất (chẳng hạn như android:name) hoặc giá trị duy nhất tự nhiên của thẻ (ví dụ: chỉ có thể có một phần tử <supports-screen>). Nếu hai tệp kê khai có cùng một phần tử XML, thì công cụ sẽ hợp nhất hai phần tử lại với nhau bằng một trong ba chính sách hợp nhất:

Hợp nhất
Kết hợp tất cả các thuộc tính không xung đột vào cùng một thẻ rồi hợp nhất các phần tử con theo chính sách hợp nhất tương ứng. Nếu có bất kỳ thuộc tính nào xung đột với nhau, hãy hợp nhất các thuộc tính đó với các điểm đánh dấu quy tắc hợp nhất.
Chỉ hợp nhất phần tử con
Không kết hợp hoặc hợp nhất các thuộc tính (chỉ giữ lại các thuộc tính do tệp kê khai có mức độ ưu tiên cao nhất cung cấp) và hợp nhất các phần tử con theo chính sách hợp nhất.
Giữ
Giữ nguyên phần tử "nguyên trạng" và thêm phần tử đó vào phần tử mẹ chung trong tệp đã hợp nhất. Bạn chỉ nên sử dụng thuộc tính này khi có thể chấp nhận một số nội dung khai báo của cùng một phần tử.

Bảng 1 liệt kê từng loại phần tử, loại chính sách hợp nhất được sử dụng, và khóa được dùng để xác định mức trùng khớp phần tử giữa hai tệp kê khai.

Bảng 3. Chính sách hợp nhất phần tử tệp kê khai và khóa trùng khớp

Phần tử Chính sách hợp nhất Khóa trùng khớp
<action> Hợp nhất Thuộc tính android:name
<activity> Hợp nhất Thuộc tính android:name
<application> Hợp nhất Chỉ có một cho mỗi <manifest>
<category> Hợp nhất Thuộc tính android:name
<data> Hợp nhất Chỉ có một cho mỗi <intent-filter>
<grant-uri-permission> Hợp nhất Chỉ có một cho mỗi <provider>
<instrumentation> Hợp nhất Thuộc tính android:name
<intent-filter> Giữ Không khớp; cho phép nhiều khai báo trong phần tử mẹ
<manifest> Chỉ hợp nhất phần tử con Chỉ có một cho mỗi tệp
<meta-data> Hợp nhất Thuộc tính android:name
<path-permission> Hợp nhất Chỉ có một cho mỗi <provider>
<permission-group> Hợp nhất Thuộc tính android:name
<permission> Hợp nhất Thuộc tính android:name
<permission-tree> Hợp nhất Thuộc tính android:name
<provider> Hợp nhất Thuộc tính android:name
<receiver> Hợp nhất Thuộc tính android:name
<screen> Hợp nhất Thuộc tính android:screenSize
<service> Hợp nhất Thuộc tính android:name
<supports-gl-texture> Hợp nhất Thuộc tính android:name
<supports-screen> Hợp nhất Chỉ có một cho mỗi <manifest>
<uses-configuration> Hợp nhất Chỉ có một cho mỗi <manifest>
<uses-feature> Hợp nhất Thuộc tính android:name (nếu không có thì thuộc tính android:glEsVersion)
<uses-library> Hợp nhất Thuộc tính android:name
<uses-permission> Hợp nhất Thuộc tính android:name
<uses-sdk> Hợp nhất Chỉ có một cho mỗi <manifest>
Phần tử tùy chỉnh Hợp nhất Không khớp; những dữ liệu này không xác định với công cụ hợp nhất do đó chúng luôn có trong tệp kê khai hợp nhất

Chèn biến bản dựng vào tệp manifest

Nếu cần chèn các biến vào tệp AndroidManifest.xml được xác định trong tệp build.gradle, bạn có thể thực hiện bằng thuộc tính manifestPlaceholders. Thuộc tính này sẽ liên kết các cặp khóa-giá trị, như hiển thị ở đây:

Groovy

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

Kotlin

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

Sau đó, bạn có thể chèn một trong các phần giữ chỗ vào tệp kê khai dưới dạng giá trị thuộc tính như sau:

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

Theo mặc định, các công cụ bản dựng cũng cung cấp ID ứng dụng của ứng dụng trong trình giữ chỗ ${applicationId}. Giá trị luôn khớp với mã ứng dụng cuối cùng cho bản dựng hiện tại (bao gồm các thay đổi theo biến thể bản dựng. Điều này sẽ hữu ích khi bạn muốn sử dụng một vùng chứa tên duy nhất cho các giá trị nhận dạng, chẳng hạn như hành động theo ý định, ngay cả giữa các biến thể bản dựng.

Ví dụ: nếu tệp build.gradle của bạn trông giống như sau:

Groovy

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

Kotlin

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

Sau đó, bạn có thể chèn ID ứng dụng vào tệp kê khai như sau:

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

Và kết quả tệp kê khai khi bạn tạo phiên bản sản phẩm "miễn phí" là:

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

Để biết thêm thông tin, hãy đọc Đặt mã ứng dụng.