請按照本指南中的步驟,透過 Java 程式碼存取應用程式的資產包。
專為 Kotlin 和 Java 建構
請按照下列步驟將 Play Asset Delivery 建構到專案的 Android App Bundle 中。執行時不必使用 Android Studio。
將專案
build.gradle
檔案中 Android Gradle 外掛程式的版本更新為4.0.0
以上版本。在專案的頂層目錄中,為資產包建立目錄。系統會使用這個目錄名稱做為資產包名稱。資產包名稱開頭須為英文字母,而且只能使用英文字母、數字和底線。
在資產包目錄中建立
build.gradle
檔案,並新增下列程式碼。請務必指定資產包的名稱,並且僅指定一種提供類型:Groovy
// In the asset pack's build.gradle file: plugins { id 'com.android.asset-pack' } assetPack { packName = "asset-pack-name" // Directory name for the asset pack dynamicDelivery { deliveryType = "[ install-time | fast-follow | on-demand ]" } }
Kotlin
// In the asset pack's build.gradle.kts file: plugins { id("com.android.asset-pack") } assetPack { packName.set("asset-pack-name") // Directory name for the asset pack dynamicDelivery { deliveryType.set("[ install-time | fast-follow | on-demand ]") } }
在專案的應用程式
build.gradle
檔案中,新增專案中每個資產包的名稱,如下所示:Groovy
// In the app build.gradle file: android { ... assetPacks = [":asset-pack-name", ":asset-pack2-name"] }
Kotlin
// In the app build.gradle.kts file: android { ... assetPacks += listOf(":asset-pack-name", ":asset-pack2-name") }
在專案的
settings.gradle
檔案中,納入專案中的所有資產包,如下所示:Groovy
// In the settings.gradle file: include ':app' include ':asset-pack-name' include ':asset-pack2-name'
Kotlin
// In the settings.gradle.kts file: include(":app") include(":asset-pack-name") include(":asset-pack2-name")
在資產包目錄中建立以下子目錄:
src/main/assets
。將資產放在
src/main/assets
目錄中。您也可以在其中建立子目錄。目前應用程式的目錄結構應如下所示:build.gradle
settings.gradle
app/
asset-pack-name/build.gradle
asset-pack-name/src/main/assets/your-asset-directories
使用 Gradle 建構 Android App Bundle。在產生的應用程式套件中,根層級目錄現在包含下列項目:
asset-pack-name/manifest/AndroidManifest.xml
:設定資產包的 ID 和提供模式asset-pack-name/assets/your-asset-directories
:此目錄包含透過資產包提供的所有資產
Gradle 為每個資產包產生資訊清單,也會替您輸出
assets/
目錄。(選用) 如果您打算使用快速追蹤和隨選提供功能,請加入 Play Asset Delivery 程式庫
Groovy
implementation "com.google.android.play:asset-delivery:2.2.2" // For Kotlin use asset-delivery-ktx implementation "com.google.android.play:asset-delivery-ktx:2.2.2"
Kotlin
implementation("com.google.android.play:asset-delivery:2.2.2") // For Kotlin use core-ktx implementation("com.google.android.play:asset-delivery-ktx:2.2.2")
(選用) 調整應用程式套件的設定,支援不同的紋理壓縮格式。
與 Play Asset Delivery API 整合
Play Asset Delivery Java API 提供 AssetPackManager
類別,可用來要求資產包、管理下載內容以及存取資產。請務必先將 Play Asset Delivery 程式庫加入專案。
您可以根據想要存取的資產包提供類型實作此 API。步驟請參見下方流程圖。
安裝時提供
設定為 install-time
的資產包可在應用程式啟動時立即使用。使用 Java AssetManager API 存取在此模式下提供的資產:
Kotlin
import android.content.res.AssetManager ... val context: Context = createPackageContext("com.example.app", 0) val assetManager: AssetManager = context.assets val stream: InputStream = assetManager.open("asset-name")
Java
import android.content.res.AssetManager; ... Context context = createPackageContext("com.example.app", 0); AssetManager assetManager = context.getAssets(); InputStream is = assetManager.open("asset-name");
快速追蹤及隨選提供
以下各節將說明如何在下載前取得資產包的相關資訊、如何呼叫 API 以啟動下載作業,以及如何存取已下載的資產包。這部分的內容適用於 fast-follow
及 on-demand
資產包。
檢查狀態
每個資產包會儲存在應用程式內部儲存空間的獨立資料夾中。使用 getPackLocation()
方法來判定資產包的根資料夾。這個方法會回傳下列值:
傳回值 | 狀態 |
---|---|
有效的 AssetPackLocation 物件 |
隨時可在 assetsPath() 立即存取資產包根資料夾 |
null |
無法使用未知的資產包或資產 |
取得資產包的下載資訊
應用程式必須先揭露下載大小,才能擷取資產包。使用 requestPackStates()
或 getPackStates()
方法來判定下載內容的大小,以及是否正在下載資產包。
Kotlin
suspend fun requestPackStates(packNames: List<String>): AssetPackStates
Java
Task<AssetPackStates> getPackStates(List<String> packNames)
requestPackStates()
是用來回傳 AssetPackStates
物件的暫停函式,getPackStates()
則是回傳 Task<AssetPackStates>
的非同步方法。AssetPackStates
物件的 packStates()
方法會回傳 Map<String,
AssetPackState>
。此對應包含每個要求資產包的狀態,並以名稱做為索引鍵:
Kotlin
AssetPackStates#packStates(): Map<String, AssetPackState>
Java
Map<String, AssetPackState> AssetPackStates#packStates()
最終要求如下所示:
Kotlin
const val assetPackName = "assetPackName" coroutineScope.launch { try { val assetPackStates: AssetPackStates = manager.requestPackStates(listOf(assetPackName)) val assetPackState: AssetPackState = assetPackStates.packStates()[assetPackName] } catch (e: RuntimeExecutionException) { Log.d("MainActivity", e.message) } }
Java
final String assetPackName = "myasset"; assetPackManager .getPackStates(Collections.singletonList(assetPackName)) .addOnCompleteListener(new OnCompleteListener<AssetPackStates>() { @Override public void onComplete(Task<AssetPackStates> task) { AssetPackStates assetPackStates; try { assetPackStates = task.getResult(); AssetPackState assetPackState = assetPackStates.packStates().get(assetPackName); } catch (RuntimeExecutionException e) { Log.d("MainActivity", e.getMessage()); return; })
下列 AssetPackState
方法提供資產包的大小、目前為止的下載數量 (如有要求),以及已轉移到應用程式的數量:
如要取得資產包的狀態,請使用 status()
方法傳回整數形式的狀態,而此整數會對應至 AssetPackStatus
類別中的特定常數欄位。尚未安裝的資產包處於 AssetPackStatus.NOT_INSTALLED
狀態。
如果要求失敗,請使用 errorCode()
方法,其傳回值會對應至 AssetPackErrorCode
類別中的特定常數欄位。
安裝
若是第一次下載資產包,或若要呼叫以完成資產包更新作業,請使用 requestFetch()
或 fetch()
方法:
Kotlin
suspend fun AssetPackManager.requestFetch(packs: List<String>): AssetPackStates
Java
Task<AssetPackStates> fetch(List<String> packNames)
這個方法會回傳 AssetPackStates
物件,其中包含資產包清單,以及其初始下載狀態和大小。如果正在下載透過 requestFetch()
或 fetch()
要求的資產包,系統會回傳下載狀態,且不會開始其他下載作業。
監控下載狀態
您應實作 AssetPackStateUpdatedListener
以追蹤資產包的安裝進度。系統會按照每個資產包細分狀態更新內容,支援個別資產包的狀態追蹤功能。您可以開始使用可用的資產包,不必等到要求的所有其他下載作業完成。
Kotlin
fun registerListener(listener: AssetPackStateUpdatedListener) fun unregisterListener(listener: AssetPackStateUpdatedListener)
Java
void registerListener(AssetPackStateUpdatedListener listener) void unregisterListener(AssetPackStateUpdatedListener listener)
大型下載內容
如果下載檔案大小超過 200 MB,且使用者未連上 Wi-Fi 網路,則只有在使用者明確同意使用行動數據連線進行下載時,下載作業才會開始。同樣,如果下載內容較大,且使用者未連接 Wi-Fi 網路,則系統會暫停下載並明確取得同意,才能透過行動數據連線進行下載。已暫停的資產包處於 WAITING_FOR_WIFI
狀態。如要觸發使用者介面流程來提示使用者同意聲明,請使用 showConfirmationDialog()
方法。
請注意,如果應用程式未呼叫這個方法,下載作業就會暫停,且只在使用者再次連上 Wi-Fi 網路時,才會自動繼續下載。
需要使用者確認
如果套件具有 REQUIRES_USER_CONFIRMATION
狀態,使用者必須接受顯示 showConfirmationDialog()
的對話方塊,下載作業才會繼續進行。如果應用程式未獲得 Play 認證 (例如應用程式是側載的),就可能會出現這個狀態。請注意,在這種情況下呼叫 showConfirmationDialog()
會導致應用程式更新。更新後,您必須再次要求資產。
以下是事件監聽器的實作範例:
Kotlin
private val activityResultLauncher = registerForActivityResult( ActivityResultContracts.StartIntentSenderForResult() ) { result -> if (result.resultCode == RESULT_OK) { Log.d(TAG, "Confirmation dialog has been accepted.") } else if (result.resultCode == RESULT_CANCELED) { Log.d(TAG, "Confirmation dialog has been denied by the user.") } } assetPackManager.registerListener { assetPackState -> when(assetPackState.status()) { AssetPackStatus.PENDING -> { Log.i(TAG, "Pending") } AssetPackStatus.DOWNLOADING -> { val downloaded = assetPackState.bytesDownloaded() val totalSize = assetPackState.totalBytesToDownload() val percent = 100.0 * downloaded / totalSize Log.i(TAG, "PercentDone=" + String.format("%.2f", percent)) } AssetPackStatus.TRANSFERRING -> { // 100% downloaded and assets are being transferred. // Notify user to wait until transfer is complete. } AssetPackStatus.COMPLETED -> { // Asset pack is ready to use. Start the game. } AssetPackStatus.FAILED -> { // Request failed. Notify user. Log.e(TAG, assetPackState.errorCode()) } AssetPackStatus.CANCELED -> { // Request canceled. Notify user. } AssetPackStatus.WAITING_FOR_WIFI, AssetPackStatus.REQUIRES_USER_CONFIRMATION -> { if (!confirmationDialogShown) { assetPackManager.showConfirmationDialog(activityResultLauncher); confirmationDialogShown = true } } AssetPackStatus.NOT_INSTALLED -> { // Asset pack is not downloaded yet. } AssetPackStatus.UNKNOWN -> { Log.wtf(TAG, "Asset pack status unknown") } } }
Java
assetPackStateUpdateListener = new AssetPackStateUpdateListener() { private final ActivityResultLauncher<IntentSenderRequest> activityResultLauncher = registerForActivityResult( new ActivityResultContracts.StartIntentSenderForResult(), new ActivityResultCallback<ActivityResult>() { @Override public void onActivityResult(ActivityResult result) { if (result.getResultCode() == RESULT_OK) { Log.d(TAG, "Confirmation dialog has been accepted."); } else if (result.getResultCode() == RESULT_CANCELED) { Log.d(TAG, "Confirmation dialog has been denied by the user."); } } }); @Override public void onStateUpdate(AssetPackState assetPackState) { switch (assetPackState.status()) { case AssetPackStatus.PENDING: Log.i(TAG, "Pending"); break; case AssetPackStatus.DOWNLOADING: long downloaded = assetPackState.bytesDownloaded(); long totalSize = assetPackState.totalBytesToDownload(); double percent = 100.0 * downloaded / totalSize; Log.i(TAG, "PercentDone=" + String.format("%.2f", percent)); break; case AssetPackStatus.TRANSFERRING: // 100% downloaded and assets are being transferred. // Notify user to wait until transfer is complete. break; case AssetPackStatus.COMPLETED: // Asset pack is ready to use. Start the game. break; case AssetPackStatus.FAILED: // Request failed. Notify user. Log.e(TAG, assetPackState.errorCode()); break; case AssetPackStatus.CANCELED: // Request canceled. Notify user. break; case AssetPackStatus.WAITING_FOR_WIFI: case AssetPackStatus.REQUIRES_USER_CONFIRMATION: if (!confirmationDialogShown) { assetPackManager.showConfirmationDialog(activityResultLauncher); confirmationDialogShown = true; } break; case AssetPackStatus.NOT_INSTALLED: // Asset pack is not downloaded yet. break; case AssetPackStatus.UNKNOWN: Log.wtf(TAG, "Asset pack status unknown") break; } } }
您也可以使用 getPackStates()
方法取得目前下載作業的狀態。AssetPackStates
包含下載進度、下載狀態和任何失敗錯誤代碼。
存取資產包
在下載要求達到 COMPLETED
狀態後,您可以使用檔案系統呼叫來存取資產包。使用 getPackLocation()
方法取得資產包的根資料夾。
資產會儲存在資產包根目錄的 assets
目錄中。您可以使用便利的方法 assetsPath()
,取得 assets
目錄的路徑。請使用以下方法取得特定資產的路徑:
Kotlin
private fun getAbsoluteAssetPath(assetPack: String, relativeAssetPath: String): String? { val assetPackPath: AssetPackLocation = assetPackManager.getPackLocation(assetPack) // asset pack is not ready ?: return null val assetsFolderPath = assetPackPath.assetsPath() // equivalent to: FilenameUtils.concat(assetPackPath.path(), "assets") return FilenameUtils.concat(assetsFolderPath, relativeAssetPath) }
Java
private String getAbsoluteAssetPath(String assetPack, String relativeAssetPath) { AssetPackLocation assetPackPath = assetPackManager.getPackLocation(assetPack); if (assetPackPath == null) { // asset pack is not ready return null; } String assetsFolderPath = assetPackPath.assetsPath(); // equivalent to: FilenameUtils.concat(assetPackPath.path(), "assets"); String assetPath = FilenameUtils.concat(assetsFolderPath, relativeAssetPath); return assetPath; }
其他 Play Asset Delivery API 方法
以下列出一些您可能想在應用程式中使用的其他 API 方法。
取消要求
使用 cancel()
取消啟用的資產包要求。請注意,我們會盡可能履行這項要求。
移除資產包
使用 requestRemovePack()
或 removePack()
安排資產包的移除作業。
取得多個資產包的位置
使用 getPackLocations()
查詢多個資產包的狀態,進而回傳資產包及其位置的對應圖。getPackLocations()
回傳的對應圖包含每個已下載且處於最新狀態的資產包的輸入項。
下一步
在本機和 Google Play 中測試 Play Asset Delivery。