集成 Asset Delivery(原生)

您可以按照本指南中的步骤,从 C 和 C++ 代码获取应用的资源包。

GitHub 上提供了集成代码示例。

针对原生代码构建

您可以按照以下步骤将 Play Asset Delivery 内置到项目的 Android App Bundle 中。您无需使用 Android Studio 即可执行这些步骤。

  1. 在项目的 build.gradle 文件中将 Android Gradle 插件的版本更新为 4.0.0 或更高版本。

  2. 在项目的顶级目录中,为资源包创建一个目录。此目录名称将用作 Asset Pack 名称。资源包名称必须以字母开头,并且只能包含字母、数字和下划线。

  3. 在 Asset Pack 目录中,创建一个 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。在生成的 app bundle 中,根级目录现在包含以下内容:

    • asset-pack-name/manifest/AndroidManifest.xml:配置资源包的标识符和分发模式
    • asset-pack-name/assets/your-asset-directories:此目录包含作为资源包的一部分分发的所有资产

    Gradle 会为每个资源包生成清单,并为您输出 assets/ 目录。

  9. (可选)配置 app bundle 以支持不同的纹理压缩格式

与 Play Asset Delivery 库集成

您可以根据希望获取的资源包的分发类型来实现此 API。这些步骤如以下流程图所示。

针对原生代码的 Asset Pack 流程图

图 1. 获取资源包的流程图

Play Core 原生 SDK 提供了用于请求资源包、管理下载内容以及获取资产的 C 头文件 play/asset_pack.h

为 Play Core 原生 SDK 设置开发环境

下载 Play Core Native SDK

您必须先接受以下条款及条件才能下载。

条款及条件

上次修改时间:2020 年 9 月 24 日
  1. 使用 Play Core 软件开发套件,即表示您同意在遵守 Google API 服务条款(以下简称“API 服务条款”)的同时,也遵守这些条款。如果这些条款与 API 服务条款存在冲突,以这些条款为准。请仔细阅读这些条款和 API 服务条款。
  2. 就这些条款而言,“API”是指 Google 的 API、其他开发者服务和相关软件(包括任何可再分发代码)。
  3. “可再分发代码”是指 Google 提供的调用 API 的对象代码或头文件。
  4. 根据这些条款和 API 服务条款的规定,您可以复制和分发可再分发代码,但只能纳入为您的 API 客户端的一部分。Google 及其许可人拥有可再分发代码的所有权利、所有权和利益,包括任何及所有知识产权和其他专有权利。您不得修改、翻译可再分发代码,也不得创作可再分发代码的衍生作品。
  5. Google 随时可变更这些条款,如有变更,Google 会向您发出通知,同时您可以选择不继续使用 Play Core 软件开发套件。Google 会在 https://developer.android.com/guide/playcore/license 上发布条款修改通知。变更不具有追溯效力。
下载 Play Core Native SDK

play-core-native-sdk-1.12.1.zip

  1. 执行以下其中一项操作:

    • 安装 Android Studio 4.0 或更高版本。使用 SDK 管理器界面安装 Android SDK Platform 版本 10.0(API 级别 29)。
    • 安装 Android SDK 命令行工具,然后使用 sdkmanager 安装 Android SDK Platform 版本 10.0(API 级别 29)。
  2. 使用 SDK 管理器安装最新的 CMake 和 Android 原生开发套件 (NDK),让 Android Studio 做好原生开发准备。如需详细了解如何创建或导入原生项目,请参阅 NDK 入门指南

  3. 下载 zip 文件并将其解压缩到您的项目所在位置。

    下载链接 大小 SHA-256 校验和
    70.9 MiB 84c9e9579f05d6e29bbbd9c9cde2fde8210947f2007866b0045f4c40fabb7368
  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.0.0'
            implementation 'com.google.android.play:asset-delivery:2.0.0'
            implementation 'com.google.android.play:integrity:1.0.1'
            implementation 'com.google.android.play:review:2.0.0'
    
            // 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.0.0")
        implementation("com.google.android.play:asset-delivery:2.0.0")
        implementation("com.google.android.play:integrity:1.0.1")
        implementation("com.google.android.play:review:2.0.0")
    
        // 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 Asset Pack。

应用启动

始终先调用 AssetPackManager_init() 以初始化 Asset Pack API,然后再调用其他任何函数。应检查是否存在任何资源包错误代码

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

此外,还应确保调用 ANativeActivityCallbacksonPause()onResume() 中的以下函数:

获取有关资源包的下载信息

在提取资源包之前,应用必须披露下载内容的大小。使用 AssetPackManager_requestInfo() 函数启动异步请求以确定下载内容的大小,以及 Asset Pack 是否已在下载。然后,使用 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。如需了解如何使用此类型,请参阅获取下载信息

下载内容较大

如果下载内容超过 150MB 并且用户未连接到 WLAN,那么在用户明确同意使用移动网络连接继续下载前,下载不会开始。同样,如果下载内容较大并且用户的 WLAN 连接断开,那么下载会暂停,需要用户明确同意才能使用移动网络连接继续下载。已暂停的资源包状态为 WAITING_FOR_WIFI。如需触发界面流程来提示用户同意,请使用以下函数:

获取资源包

在下载请求达到 COMPLETED 状态后,您可以使用文件系统调用获取资源包。每个资源包都存储于应用的内部存储空间内单独的目录中。您可以使用 AssetPackManager_getAssetPackLocation() 获取指定 Asset Pack 的 AssetPackLocation。您还可以对该位置使用 AssetPackLocation_getStorageMethod() 以确定存储方法:

  • ASSET_PACK_STORAGE_APK:资源包以 APK 的形式进行安装。如需获取这些资产,请参阅安装时分发
  • ASSET_PACK_STORAGE_FILES:使用 AssetPackLocation_getAssetsPath() 获取包含相应资产的目录的文件路径,如果尚未下载相应资产,则为 null。请勿修改此文件路径中已下载的文件。
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