整合資產提供功能 (原生)

請按照本指南的步驟,透過 C 和 C++ 程式碼存取應用程式資產包。

您可以在 GitHub 找到整合代碼範例。

針對原生環境進行建構

請按照下列步驟將 Play Asset Delivery 建構到專案的 Android App Bundle 中。執行時不必使用 Android Studio。

  1. 將專案 build.gradle 檔案中 Android Gradle 外掛程式的版本更新為 4.0.0 以上版本。

  2. 在專案的頂層目錄中,為資產包建立目錄。系統會使用這個目錄名稱做為資產包名稱。資產包名稱開頭須為英文字母,而且只能使用英文字母、數字和底線。

  3. 在資產包目錄中建立 build.gradle 檔案,並新增下列程式碼。請務必指定資產包的名稱,且只使用一種提交類型:

    // 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 ]"
        }
    }
  4. 在專案的應用程式 build.gradle 檔案中,新增專案中每個資產包的名稱,如下所示:

    // In the app build.gradle file:
    android {
        ...
        assetPacks = [":asset-pack-name", ":asset-pack2-name"]
    }
  5. 在專案的 settings.gradle 檔案中,納入專案中的所有資產包,如下所示:

    // In the settings.gradle file:
    include ':app'
    include ':asset-pack-name'
    include ':asset-pack2-name'
  6. 在資產包目錄中建立下列子目錄:src/main/assets

  7. 將資產放在 src/main/assets 目錄中。您也可以在其中建立子目錄。目前應用程式的目錄結構應如下所示:

    • build.gradle
    • settings.gradle
    • app/
    • asset-pack-name/build.gradle
    • asset-pack-name/src/main/assets/your-asset-directories
  8. 使用 Gradle 建構 Android App Bundle。在產生的應用程式套件中,根層級目錄現在包含下列項目:

    • asset-pack-name/manifest/AndroidManifest.xml:設定資產包的 ID 和提供模式
    • asset-pack-name/assets/your-asset-directories:此目錄包含透過資產包提供的所有資產

    Gradle 為每個資產包產生資訊清單,並為您輸出 assets/ 目錄。

  9. (選用) 調整應用程式套件的設定,支援不同的紋理壓縮格式

整合 Play Asset Delivery 程式庫

您可以根據所要存取資產包的提交類型實作 API。步驟請參見下方流程圖。

原生程式碼的資產包流程圖

圖 1. 存取資產包的流程圖

Play Core 原生 SDK 提供 C 標頭檔案 play/asset_pack.h,可用於要求資產包、管理下載內容及存取資產。

設定 Play Core 原生 SDK 的開發環境

下載 Play Core Native SDK

下載前,請先同意以下條款及細則。

條款及細則

Last modified: September 24, 2020
  1. By using the Play Core Software Development Kit, you agree to these terms in addition to the Google APIs Terms of Service ("API ToS"). If these terms are ever in conflict, these terms will take precedence over the API ToS. Please read these terms and the API ToS carefully.
  2. For purposes of these terms, "APIs" means Google's APIs, other developer services, and associated software, including any Redistributable Code.
  3. “Redistributable Code” means Google-provided object code or header files that call the APIs.
  4. Subject to these terms and the terms of the API ToS, you may copy and distribute Redistributable Code solely for inclusion as part of your API Client. Google and its licensors own all right, title and interest, including any and all intellectual property and other proprietary rights, in and to Redistributable Code. You will not modify, translate, or create derivative works of Redistributable Code.
  5. Google may make changes to these terms at any time with notice and the opportunity to decline further use of the Play Core Software Development Kit. Google will post notice of modifications to the terms at https://developer.android.com/guide/playcore/license. Changes will not be retroactive.
下載 Play Core Native SDK

play-core-native-sdk-1.15.3.zip

  1. 執行下列其中一項操作:

  2. 使用 SDK Manager 安裝最新的 CMake 和 Android Native Development Kit (NDK),讓 Android Studio 準備進行原生開發。如要進一步瞭解如何建立或匯入原生專案,請參閱 NDK 入門指南

  3. 下載 ZIP 檔案,然後將其連同專案一起解壓縮。

    下載連結 大小 SHA-256 總和檢查碼
    37.8 MiB 9db60185185342f28d2c278b60222333608c67bc022e458a25224eaea8c4c4b7
  4. 請更新應用程式的 build.gradle 檔案,如下所示:

    Groovy

        // App build.gradle
    
        plugins {
          id 'com.android.application'
        }
    
        // Define a path to the extracted Play Core SDK files.
        // If using a relative path, wrap it with file() since CMake requires absolute paths.
        def playcoreDir = file('../path/to/playcore-native-sdk')
    
        android {
            defaultConfig {
                ...
                externalNativeBuild {
                    cmake {
                        // Define the PLAYCORE_LOCATION directive.
                        arguments "-DANDROID_STL=c++_static",
                                  "-DPLAYCORE_LOCATION=$playcoreDir"
                    }
                }
                ndk {
                    // Skip deprecated ABIs. Only required when using NDK 16 or earlier.
                    abiFilters 'armeabi-v7a', 'arm64-v8a', 'x86', 'x86_64'
                }
            }
            buildTypes {
                release {
                    // Include Play Core Library proguard config files to strip unused code while retaining the Java symbols needed for JNI.
                    proguardFile '$playcoreDir/proguard/common.pgcfg'
                    proguardFile '$playcoreDir/proguard/gms_task.pgcfg'
                    proguardFile '$playcoreDir/proguard/per-feature-proguard-files'
                    ...
                }
                debug {
                    ...
                }
            }
            externalNativeBuild {
                cmake {
                    path 'src/main/CMakeLists.txt'
                }
            }
        }
    
        dependencies {
            // Import these feature-specific AARs for each Google Play Core library.
            implementation 'com.google.android.play:app-update:2.1.0'
            implementation 'com.google.android.play:asset-delivery:2.2.2'
            implementation 'com.google.android.play:integrity:1.4.0'
            implementation 'com.google.android.play:review:2.0.2'
    
            // Import these common dependencies.
            implementation 'com.google.android.gms:play-services-tasks:18.0.2'
            implementation files("$playcoreDir/playcore-native-metadata.jar")
            ...
        }
        

    Kotlin

    // App build.gradle
    
    plugins {
        id("com.android.application")
    }
    
    // Define a path to the extracted Play Core SDK files.
    // If using a relative path, wrap it with file() since CMake requires absolute paths.
    val playcoreDir = file("../path/to/playcore-native-sdk")
    
    android {
        defaultConfig {
            ...
            externalNativeBuild {
                cmake {
                    // Define the PLAYCORE_LOCATION directive.
                    arguments += listOf("-DANDROID_STL=c++_static", "-DPLAYCORE_LOCATION=$playcoreDir")
                }
            }
            ndk {
                // Skip deprecated ABIs. Only required when using NDK 16 or earlier.
                abiFilters.clear()
                abiFilters += listOf("armeabi-v7a", "arm64-v8a", "x86", "x86_64")
            }
        }
        buildTypes {
            release {
                // Include Play Core Library proguard config files to strip unused code while retaining the Java symbols needed for JNI.
                proguardFile("$playcoreDir/proguard/common.pgcfg")
                proguardFile("$playcoreDir/proguard/gms_task.pgcfg")
                proguardFile("$playcoreDir/proguard/per-feature-proguard-files")
                ...
            }
            debug {
                ...
            }
        }
        externalNativeBuild {
            cmake {
                path = "src/main/CMakeLists.txt"
            }
        }
    }
    
    dependencies {
        // Import these feature-specific AARs for each Google Play Core library.
        implementation("com.google.android.play:app-update:2.1.0")
        implementation("com.google.android.play:asset-delivery:2.2.2")
        implementation("com.google.android.play:integrity:1.4.0")
        implementation("com.google.android.play:review:2.0.2")
    
        // Import these common dependencies.
        implementation("com.google.android.gms:play-services-tasks:18.0.2")
        implementation(files("$playcoreDir/playcore-native-metadata.jar"))
        ...
    }
  5. 請更新應用程式的 CMakeLists.txt 檔案,如下所示:

    cmake_minimum_required(VERSION 3.6)
    
    ...
    
    # Add a static library called “playcore” built with the c++_static STL.
    include(${PLAYCORE_LOCATION}/playcore.cmake)
    add_playcore_static_library()
    
    // In this example “main” is your native code library, i.e. libmain.so.
    add_library(main SHARED
            ...)
    
    target_include_directories(main PRIVATE
            ${PLAYCORE_LOCATION}/include
            ...)
    
    target_link_libraries(main
            android
            playcore
            ...)
    

資料收集

Play Core 原生 SDK 可收集版本相關資料,讓 Google 能夠改善產品,這包括:

  • 應用程式的套件名稱
  • 應用程式的套件版本
  • Play Core 原生 SDK 版本

當您將應用程式套件上傳至 Play 管理中心時,系統就會收集這類資料。如要選擇退出此資料收集程序,請移除 build.gradle 檔案中的 $playcoreDir/playcore-native-metadata.jar 匯入項目。

請注意,上述與使用 Play Core 原生 SDK 相關的資料收集行為,以及 Google 使用所收集資料的行為,與您在上傳應用程式套件至 Play 管理中心時,Google 收集 Gradle 中宣告的程式庫依附元件無關。

安裝時提交

設定為 install-time 的資產包可在應用程式啟動時立即使用。使用 NDK AAssetManager API 存取在此模式下提供的資產:

#include <android/asset_manager.h>
#include <android_native_app_glue.h>
...
AAssetManager* assetManager = app->activity->assetManager;
AAsset* asset = AAssetManager_open(assetManager, "asset-name", AASSET_MODE_BUFFER);
size_t assetLength = AAsset_getLength(asset);
char* buffer = (char*) malloc(assetLength + 1);
AAsset_read(asset, buffer, assetLength);

快速追蹤及隨選傳遞

以下各節將說明如何初始化 API、如何在下載前取得資產包相關資訊、如何呼叫 API 開始下載作業,以及如何存取所下載的套件。這部分的內容適用於 fast-followon-demand 資產包。

應用程式開始運作

請務必先呼叫 AssetPackManager_init() 來初始化資產包 API,然後再呼叫任何其他函式。檢查是否有任何資產包錯誤代碼

#include "play/asset_pack.h"
...
AssetPackErrorCode AssetPackManager_init(JavaVM* jvm, jobject android_context);

請記得在 ANativeActivityCallbacksonPause()onResume() 中呼叫下列函式:

取得資產包的下載資訊

應用程式必須先揭露下載大小,才能擷取資產包。使用 AssetPackManager_requestInfo() 函式啟動有關下載大小和資產包下載狀態的非同步要求。然後使用 AssetPackManager_getDownloadState() 輪詢下載狀態,例如在遊戲迴圈的每個影格呼叫此函式一次。如果要求失敗,請查看資產包錯誤代碼

AssetPackErrorCode AssetPackManager_requestInfo();      // Call once
AssetPackErrorCode AssetPackManager_getDownloadState(); // Call once per frame in your game loop

AssetPackManager_getDownloadState() 函式會回傳不透明類型 AssetPackDownloadState 做為輸出指標。使用該指標呼叫下列函式:

AssetPackDownloadState* state;
AssetPackErrorCode error_code = AssetPackManager_getDownloadState(asset-pack-name, &state);
AssetPackDownloadStatus status = AssetPackDownloadState_getStatus(state);
uint64_t downloadedBytes = AssetPackDownloadState_getBytesDownloaded(state);
uint64_t totalBytes = AssetPackDownloadState_getTotalBytesToDownload(state));
AssetPackDownloadState_destroy(state);

安裝

首次使用 AssetPackManager_requestDownload() 開始下載資產包,或要求更新資產包:

AssetPackErrorCode AssetPackManager_requestDownload();  // Call once
AssetPackErrorCode AssetPackManager_getDownloadState(); // Call once per frame in your game loop

AssetPackManager_getDownloadState() 函式會回傳不透明類型 AssetPackDownloadState。如要進一步瞭解如何使用此類型,請參閱「取得下載資訊」。

大型下載內容

如果下載檔案大小超過 200 MB,且使用者未連上 Wi-Fi 網路,則只有在使用者明確同意使用行動數據連線進行下載時,下載作業才會開始。同樣的,如果下載內容較大,且使用者的 Wi-Fi 網路中斷,則系統會暫停下載並明確取得同意,才能透過行動數據連線進行下載。已暫停的資產包處於 WAITING_FOR_WIFI 狀態。如要觸發使用者介面流程來提示使用者同意聲明,請使用下列指令:

需要使用者確認

如果套件具有 REQUIRES_USER_CONFIRMATION 狀態,使用者必須接受顯示 AssetPackManager_showConfirmationDialog() 的對話方塊,下載作業才會繼續進行。如果 Play 無法辨識應用程式,就會出現這個狀態。請注意,在這種情況下呼叫 AssetPackManager_showConfirmationDialog() 會導致應用程式更新。更新完成後,請再次要求素材資源。

存取資產包

在下載要求達到 COMPLETED 狀態後,您可以使用檔案系統呼叫來存取資產包。每個資產包儲存在應用程式內部儲存空間的獨立目錄中。使用 AssetPackManager_getAssetPackLocation() 取得特定資產包的 AssetPackLocation。在該位置上使用 AssetPackLocation_getStorageMethod() 來判斷儲存空間方法:

  • ASSET_PACK_STORAGE_APK:資產包是以 APK 的形式安裝。請參閱安裝時提供以存取這些資產。
  • ASSET_PACK_STORAGE_FILES:使用 AssetPackLocation_getAssetsPath() 取得包含資產的目錄檔案路徑;如果尚未下載資產,則回傳空值。請勿修改此檔案路徑中已下載的檔案。
AssetPackLocation* location;

AssetPackErrorCode error_code = AssetPackManager_getAssetPackLocation(asset-pack-name, &location);

if (error_code == ASSET_PACK_NO_ERROR) {
    AssetPackStorageMethod storage_method = AssetPackLocation_getStorageMethod(location);
    const char* assets_path = AssetPackLocation_getAssetsPath(location);
    AssetPackLocation_destroy(location);
}

找到資產後,請使用 fopenifstream 等函式來存取檔案。

其他 Play Core API 方法

以下列出一些其他您可能想在應用程式中使用的 API 方法。

取消要求

使用 AssetPackManager_cancelDownload() 取消啟用的資產包要求。請注意,我們會盡可能履行這項要求。

提出移除要求

使用 AssetPackManager_requestRemoval() 安排移除資產包。

後續步驟

在本機和 Google Play 中測試 Play Asset Delivery