Play Feature Delivery 總覽

Google Play 的應用程式提供模型會使用 Android App Bundle,針對每個使用者的裝置設定產生並提供最佳化的 APK,因此使用者只需下載執行應用程式所需的程式碼和資源。

Play Feature Delivery 使用應用程式套件的進階功能,以便視情況提供或隨選下載應用程式的特定功能。為此,您必須先將這些功能從基礎應用程式隔離,並納入功能模組。

功能模組建構設定

使用 Android Studio 建立新的功能模組時,IDE 會將下列 Gradle 外掛程式套用至模組的 build.gradle 檔案。

// The following applies the dynamic-feature plugin to your feature module.
// The plugin includes the Gradle tasks and properties required to configure and build
// an app bundle that includes your feature module.

plugins {
  id 'com.android.dynamic-feature'
}

標準應用程式外掛程式的許多可用屬性也適用於功能模組。以下各節說明應該和不應該納入功能模組建構設定的屬性。

不應納入功能模組建構設定的內容

由於每個功能模組取決於基礎模組,因此也會沿用特定設定。所以您應在功能模組的 build.gradle 檔案中省略下列項目:

  • 簽署設定:應用程式套件是根據您在基礎模組中指定的簽署設定進行簽署。
  • minifyEnabled 屬性:您可以透過基礎模組的建構設定,為整個應用程式專案啟用程式碼縮減功能。因此,您應在功能模組中省略這個屬性。不過,您可以為每個功能模組指定其他 ProGuard 規則
  • versionCodeversionName:建立應用程式套件時,Gradle 會使用基礎模組提供的應用程式版本資訊。您應從功能模組的 build.gradle 檔案中省略這些屬性。

建立與基礎模組的關係

Android Studio 建立功能模組時,為了讓基本模組偵測到該模組,會將 android.dynamicFeatures 屬性新增至基礎模組的 build.gradle 檔案,如下所示:

// In the base module’s build.gradle file.
android {
    ...
    // Specifies feature modules that have a dependency on
    // this base module.
    dynamicFeatures = [":dynamic_feature", ":dynamic_feature2"]
}

此外,Android Studio 也納入基礎模組做為功能模組的依附元件,如下所示:

// In the feature module’s build.gradle file:
...
dependencies {
    ...
    // Declares a dependency on the base module, ':app'.
    implementation project(':app')
}

指定其他 ProGuard 規則

雖然只有基礎模組的建構設定可為應用程式專案啟用程式碼縮減功能,但您可以利用 proguardFiles 屬性,透過每個功能模組提供自訂 ProGuard 規則,如下所示。

android.buildTypes {
     release {
         // You must use the following property to specify additional ProGuard
         // rules for feature modules.
         proguardFiles 'proguard-rules-dynamic-features.pro'
     }
}

請注意,在建構期間,這些 ProGuard 規則會與其他模組 (包括基礎模組) 中的規則合併。因此,雖然每個功能模組可能都會指定一組新規則,但這些規則會套用至應用程式專案中的所有模組。

部署應用程式

在開發支援功能模組的應用程式時,您可以照常為已連結的裝置部署應用程式,方法是從選單列中選取「執行」>「執行」 (或者按一下工具列中的「執行」)。

如果您的應用程式專案包含一或多個功能模組,可以按照下列步驟修改現有的執行/偵錯設定,選擇要在部署應用程式時納入哪些功能:

  1. 在選單列中,依序選取「執行」>「編輯設定」
  2. 在「執行/偵錯設定」對話方塊的左側面板中,選取您要使用的「Android 應用程式」設定。
  3. 在「一般」 分頁標籤的「動態功能部署」下方,找到您要在部署應用程式時納入的每個功能模組,勾選旁邊的方塊。
  4. 按一下「確定」

根據預設,Android Studio 不會使用應用程式套件部署應用程式。相反地,IDE 會在裝置中建構並安裝適當的 APK,從而將部署速度最佳化 (而非 APK 的大小)。若要將 Android Studio 從應用程式套件設定為改用建構和部署 APK 與免安裝體驗,請修改執行/偵錯設定

透過功能模組按照使用者的需求提供功能

使用功能模組特有的好處,就是能自訂將應用程式的不同功能下載到搭載 Android 5.0 (API 級別 21) 以上版本的裝置上的方式和時機。例如,若要縮減應用程式的初始下載大小,您可以設定某些功能,讓使用者視需求隨選下載,或僅透過支援特定功能的裝置下載,例如拍照功能或支援擴增實境功能。

根據預設,儘管在您以應用程式套件的形式上傳應用程式時,可獲得高度最佳化的下載內容,但若要使用更進階的客製化功能提供選項,就需要使用「功能模組」,對應用程式的功能進行額外的設定及模組化。也就是說,功能模組提供建立模組化功能的建置組塊,讓您視需要設定至每個下載項目。

假設某個應用程式允許使用者透過線上市集買賣商品。您可以合理地將應用程式的下列各項功能模組化,並納入獨立的功能模組:

  • 帳戶登入和建立
  • 瀏覽市集
  • 上架銷售商品
  • 處理付款

下表說明功能模組支援的不同提供選項,以及如何利用這些選項將市集應用程式範例的初始下載大小最佳化。

提供選項 行為 用途範例 開始使用
安裝時提供 根據預設,若未設定上述任何提供選項,系統會在安裝應用程式時下載功能模組。這是很重要的行為,因為這意味著您可以逐步採用進階提供選項。例如,您可以受益於應用程式功能的模組化,並且僅在透過 Play Core 程式庫徹底實作隨選下載作業後,再啟用隨選提供。

此外,應用程式之後也可以要求解除安裝功能。因此,如果您在安裝應用程式期間 (而非之後) 需要特定功能,可要求從裝置上移除功能,藉此縮減安裝大小。

如果應用程式具備特定的訓練活動,例如有關如何在市集中買賣商品的互動式指南,根據預設,您可在安裝應用程式時納入該功能。

不過,若要縮減應用程式的安裝大小,應用程式可以在使用者完成訓練後,要求刪除功能。

使用未設定進階提供選項的功能模組將應用程式模組化

若要瞭解如何透過移除使用者不再需要的特定功能模組,以縮減應用程式的安裝大小,請參閱管理已安裝的模組

隨選提供 允許應用程式視需要要求和下載功能模組。 如果只有 20% 的市集應用程式使用者上架銷售商品,大多數的使用者想要縮減初始下載大小時,建議以隨選下載項目的形式,提供拍照功能 (含商品說明) 和銷售商品上架功能。也就是說,您可設定應用程式銷售功能的功能模組,僅在使用者有意將銷售商品上架到市集時,才下載該功能模組。

此外,如果使用者在經過一段時間後不再銷售商品,應用程式可以要求解除安裝該功能,以縮減其安裝大小。

建立功能模組並設定隨選提供。接著,您的應用程式可利用 Play Core 程式庫,要求隨選下載模組。
條件式提供 可讓您指定特定使用者裝置需求,例如硬體功能、語言代碼和最低 API 級別,藉此判定是否在安裝應用程式時下載模組化功能。 如果市集應用程式提供全球服務,您可能需要支援特定區域或當地使用者常用的付款方式。為了縮減應用程式的初始下載大小,您可以建立獨立的功能模組,用於處理特定類型的付款方式,並根據其登錄的語言代碼,視情況將其安裝在使用者的裝置上。 建立功能模組並設定條件式提供
免安裝提供 Google Play 免安裝技術可讓使用者無須在裝置上安裝應用程式,就能與應用程式互動。他們只須按 Google Play 商店上的「立即體驗」按鈕,或透過建立的網址,即可體驗應用程式。這種提供內容的方式可讓您更輕鬆地提高應用程式的參與度。

透過免安裝提供,您可運用 Google Play 免安裝技術,讓使用者無須安裝就能立即體驗應用程式的某些功能。

假設遊戲的輕量功能模組中含有遊戲的前幾個關卡。您可以立即啟用該模組,讓使用者無須安裝應用程式,就能透過網址連結或「立即體驗」按鈕立即體驗遊戲。 建立功能模組並設定免安裝提供。接著,您的應用程式可利用 Play Core 程式庫,要求隨選下載模組。

請留意,利用功能模組將應用程式功能模組化只是第一步。若要支援 Google Play 免安裝技術,應用程式基礎模組和特定的免安裝即用功能的下載大小,都必須滿足嚴格的大小限制。詳情請參閱透過縮減應用程式或遊戲大小來啟用免安裝體驗

建立資源的 URI

若要使用 URI 存取儲存在功能模組中的資源,利用 Uri.Builder() 產生功能模組資源 URI 的方式如下:

Kotlin

val uri = Uri.Builder()
                .scheme(ContentResolver.SCHEME_ANDROID_RESOURCE)
                .authority(context.getPackageName()) // Look up the resources in the application with its splits loaded
                .appendPath(resources.getResourceTypeName(resId))
                .appendPath(String.format("%s:%s",
                  resources.getResourcePackageName(resId), // Look up the dynamic resource in the split namespace.
                  resources.getResourceEntryName(resId)
                  ))
                .build()

Java

String uri = Uri.Builder()
                .scheme(ContentResolver.SCHEME_ANDROID_RESOURCE)
                .authority(context.getPackageName()) // Look up the resources in the application with its splits loaded
                .appendPath(resources.getResourceTypeName(resId))
                .appendPath(String.format("%s:%s",
                  resources.getResourcePackageName(resId), // Look up the dynamic resource in the split namespace.
                  resources.getResourceEntryName(resId)
                  ))
                .build().toString();

資源路徑的每個部分都是在執行階段建構,確保在載入分割 APK 後,才會產生正確的命名空間。

為了說明 URI 的產生方式,先假設您有具備下列名稱的應用程式和功能模組:

  • 應用程式套件名稱:com.example.my_app_package
  • 功能的資源套件名稱:com.example.my_app_package.my_dynamic_feature

如果上述程式碼片段中的 resId 參照功能模組中名為「my_video」的原始檔案資源,則上述的 Uri.Builder() 程式碼會輸出下列內容:

android.resource://com.example.my_app_package/raw/com.example.my_app_package.my_dynamic_feature:my_video

然後,應用程式就能利用這個 URI 存取功能模組的資源。

若要驗證 URI 中的路徑,可使用 APK 分析工具來檢查功能模組 APK,並判定套件名稱:

APK 分析工具檢查已編譯資源檔案的內容時的螢幕截圖。

圖 2. 使用 APK 分析工具檢查已編譯資源檔案中的套件名稱。

功能模組的注意事項

您可以透過功能模組,加快建構速度和工程設計速率,並大幅度自訂應用程式功能的提供方式,藉此縮減應用程式的大小。不過,使用功能模組時,請留意相關限制和極端案件:

  • 透過條件式或隨選提供,在單一裝置上安裝 50 個以上的功能模組後,可能會引發效能問題。安裝時間模組 (並未設為可移除) 會自動納入基礎模組,且在每台裝置上只會計為一個功能模組。
  • 將設為可移除的安裝時提供模組數量限制在 10 個以下。否則,可能會延長應用程式的下載和安裝時間。
  • 只有搭載 Android 5.0 (API 級別 21) 以上版本的裝置,才能隨選下載和安裝功能。若要讓功能適用於較舊的 Android 版本,請在建立功能模組時,啟用「融合」功能。
  • 啟用 SplitCompat 後,應用程式就能存取隨選提供的已下載功能模組。
  • android:exported 設為 true 時,功能模組不應在資訊清單中指定活動。原因在於,當其他應用程式嘗試啟動活動時,不保證裝置已下載功能模組。此外,在嘗試存取其程式碼和資源之前,應用程式應確認已下載功能。詳情請參閱管理已安裝的模組
  • 由於 Play Feature Delivery 必須使用應用程式套件發布應用程式,因此務必瞭解應用程式套件的已知問題

功能模組資訊清單參考資料

使用 Android Studio 建立新的功能模組時,IDE 包含模組需要的大部分資訊清單屬性,以便如功能模組般運作。此外,部分屬性會在編譯期間由建構系統插入,因此您無須自行指定或修改這些屬性。下表說明重要的功能模組資訊清單屬性。

屬性 說明
<manifest
...
這是一般的 <manifest> 區塊。
xmlns:dist="http://schemas.android.com/apk/distribution" 指定新的 dist: XML 命名空間 (將於下文說明)。
split="split_name" 當 Android Studio 建立應用程式套件時,其中會包含這項屬性。因此,「請勿自行納入或修改這個屬性」

定義應用程式使用 Play Core 程式庫要求隨選模組時指定的模組名稱。

Gradle 如何判定這個屬性的值:

根據預設,使用 Android Studio 建立功能模組時,IDE 會使用您指定的「模組名稱」,在 Gradle 設定檔中將該模組視為 Gradle 子專案。

當您建立應用程式套件時,Gradle 會使用子專案路徑的最後一個元素,在模組的資訊清單中插入這個資訊清單屬性。例如,如果您在 MyAppProject/features/ 目錄中建立新的功能模組,並將「dynamic_feature1」指定為「模組名稱」,IDE 就會在 settings.gradle 檔案中新增 ':features:dynamic_feature1' 做為子專案。建立應用程式套件時,Gradle 會在模組的資訊清單中插入 <manifest split="dynamic_feature1">

android:isFeatureSplit="true | false"> 當 Android Studio 建立應用程式套件時,其中會包含這項屬性。因此,「請勿手動納入或修改這個屬性」

指定這個模組為功能模組。基礎模組和設定 APK 中的資訊清單可能會省略這個屬性,或是將其設為 false

<dist:module 這個新的 XML 元素會定義相關屬性,判定模組以 APK 的形式包裝和發布的方式。
dist:instant="true | false" 指定是否應透過 Google Play 免安裝技術提供模組,做為免安裝體驗。

如果應用程式包含一或多個免安裝即用的功能模組,您也必須立即啟用基礎模組。使用 Android Studio 3.5 以上版本時,IDE 會在您建立免安裝即用的功能模組時,為您完成這項操作。

您無法將這個 XML 元素設為 true,並同時設定 <dist:on-demand/>。不過,您還是可以使用 Play Core Library,要求隨選下載免安裝即用的功能模組,「做為免安裝體驗」。使用者下載並「安裝」應用程式時,根據預設,裝置會下載並安裝應用程式的免安裝即用功能模組,以及基礎 APK。

dist:title="@string/feature_name" 指定使用者看到的模組標題。例如,裝置要求下載確認時,裝置可能會顯示這個標題。

您必須在基礎模組的 module_root/src/source_set/res/values/strings.xml 檔案中納入這個標題的字串資源。

<dist:fusing dist:include="true | false" />
</dist:module>
指定是否將模組納入適用於搭載 Android 4.4 (API 級別 20) 以下版本的裝置的多個 APK。

此外,當您使用 bundletool 從應用程式套件產生 APK 時,只會在通用 APK 中納入將這個屬性設為 true 的功能模組-這是單體式 APK,內含應用程式支援的所有裝置設定的程式碼和資源。

<dist:delivery> 封裝自訂模組提供的選項,如下所示。請留意,每個功能模組只能設定一種自訂提供選項。
<dist:install-time> 指定應可在安裝期間使用該模組。這是未指定另一種自訂提供選項的功能模組的預設行為。

若要進一步瞭解安裝時的下載作業,請參閱設定安裝時提供功能

這個節點也可以指定特定條件,將模組限定為符合特定需求的裝置,例如裝置功能、使用者國家/地區或最低 API 級別。詳情請參閱設定條件式提供功能

<dist:removable dist:value="true | false" />

如果未設定或設為 false,從套件產生分割 APK 時,bundletool 就會將安裝時間模組融入基礎模組中。由於融合之後的分割 APK 較少,因此這項設定有助於改善應用程式的效能。

removable 設為 true 時:不會將安裝時間模組融入基礎模組。如果日後想要解除安裝模組,請設為 true。不過,如果將過多模組設為可移除,則可能會延長應用程式的安裝時間。

預設為 false。只有在您想停用功能模組的融合功能時,才需要在資訊清單中設定這個值。

注意:只有使用 Android Gradle 外掛程式 4.2 或透過指令列使用 bundletool v1.0 時,才能使用這項功能。

</dist:install-time>  
<dist:on-demand/> 指定模組是否應以隨選下載的形式提供。也就是說,無法在安裝時使用模組,但應用程式之後可能會要求下載該模組。

若要進一步瞭解隨選下載,請參閱設定隨選提供功能

</dist:delivery>
<application
android:hasCode="true | false">
...
</application>
如果功能模組不會產生任何 DEX 檔案,也就是未包含之後編譯至 DEX 檔案格式的任何程式碼,您必須執行下列操作 (否則可能會發生執行階段錯誤):
  1. 在功能模組的資訊清單中,將 android:hasCode 設為 "false"
  2. 將以下內容新增至「基礎」模組的資訊清單:
    
    <application
      android:hasCode="true"
      tools:replace="android:hasCode">
      ...
    </application>
    

其他資源

若要進一步瞭解如何使用功能模組,請參考下列資源。

網誌文章

影片