Play 资产分发(Play Asset Delivery)

news2024/11/29 21:45:05

一、前言

    Google Play发布应用,只支持上传aab格式文件。在之前,Google Play对上传apk文件限制大小不超过150MB,对于新的aab格式文件,也要求生成的目标apk不超过150MB(GooglePlay后台使用bundletool生成apk文件,更多关于bundletool的介绍和使用,请参考:bundletool 工具使用详解),如果发布的是游戏,Google Play也已经不再支持旧版的扩展文件(OBB文件),但是游戏可能会需要大量的游戏资源一并发布,于是Google就推出了资产分发的功能,这样一来,就可以实现超过150MB的单个aab文件上传到Google Play,并且使得生成的单个基础apk不超过150MB。Play资产分发提供了比较灵活的分发模式、自动更新、压缩和增量修补功能,重要的是这些还是免费的。使用 Play资产分发,所有的资源包都在Google Play上托管和提供,因此开发者无需内容分发网络向玩家提供游戏资源。

    Play资产分发使用资源包的形式,资源包由资源(如纹理、着色器、声音等)组成,但是其中不可包含可执行的代码

注意事项:使用Play资产分发,不允许资源中包含可执行代码

二、Play资产分发的模式

    Play资产分发可以分为三种模式,每种模式的分发时机和资源加载方式都有所不同。

2.1 安装时分发(install-time)

    顾名思义,安装时分发就是资源包在用户安装应用时(下载)分发。这些资源包以拆分 APK(APK 集合的一部分,资源包也会生成一个apk文件)的形式提供。它们也称为“预先”资源包;安装时分发的一个好处就是可以在应用启动时立即使用这些资源包,而无需等待资源的下载。这些资源包会增加 Google Play 商店上列出的应用大小,用户在安装应用时同时下载安装,用户无法修改或删除这些资源包。更新应用时,安装时分发的资源包也会作为基础应用更新的一部分进行更新(开发者无需执行任何操作)。

2.2 快速跟进分发(fast-follow)

    快速跟进分发,资源包会在用户安装应用后立即自动下载;用户无需打开应用即可开始 fast-follow 下载(下载由GooglePlay商店完成)。此类下载不会阻止用户访问应用。这些资源包也会增加 Google Play 商店上列出的应用大小。

2.3 按需分发(on-demand)

    按需分发,也就是资源包会在应用运行时按需下载资源包。用户首次只下载安装基础apk,在程序运行过程中,根据场景和功能,按需下载对应的资源包。这些资源包不会增加 Google Play 商店上列出的应用大小。

    Google Play 商店会以归档文件(而非拆分 APK)的形式提供配置为快速跟进分发按需分发的资源包。这些资源包随后会在应用的内部存储空间中展开。您可以使用 Play Core API 查询以这l两种方分发式提供的资源包的位置。应用无法假设这些文件的存在和其存储位置,因为它们可能会被用户删除,或由 Play Core SDK 在游戏会话之间移动。尽管这些文件可由应用写入,您也应将其视为只读文件,因为资源包补丁程序依赖于这些文件的完整性。

    对于快速跟进分发按需分发资源包的应用更新,则遵循以下步骤:

  • 系统将应用的补丁程序(包括所有资产)下载到设备上的安全位置。
  • 更新应用二进制文件;这包括所有安装时分发的资源包。
  • 之前下载的所有资源包均变为无效。
  • 将资产的补丁程序复制并应用到存储在应用内部存储空间中的资产。

三、Google Play下载大小上限

    资产分发的资源包因具有较高的大小上限而成为大型游戏的理想之选:

  • 每个快速跟进分发按需分发资源包的下载大小上限为 512 MB。
  • 所有 安装时分发资源包的总下载大小上限为 1 GB。
  • 一个 Android App Bundle 中的所有资产资源包的总下载大小上限为 2 GB。
  • 一个 Android App Bundle 中最多可以使用 50 个资源包。

四、安装时分发(install-time)详解

    安装时分发是最简单的一种,你无需对应用做任何更改即可实现。应用在下载安装时,会将安装时分发的资产包一并安装,分发的资源包在Assets资产中,应用可以直接通过 AssetManager 资产访问api对资源进行访问。虽说不需要对应用做任何更改,但前提是你的应用原本是主将资源放在Assets资产中(如果不是,则需要修改资源访问加载的代码)。另外,项目也需要做相应的配置变更,生成aab时将会为安装时分发的资源包生成单独的apk。下面我们来详细介绍一下步骤:

注意事项:配置安装时分发,需要项目中的Android Gradle 插件的版本为 4.0.0 或更高版本。可在项目的根目录下 build.gradle文件检查项目中的Android Gradle 插件的版本。

4.1 创建产资源包模块

    首先,需要新建一个模块,用来存放需要在安装时分发的资产资源。下面将详细介绍配置步骤:

  1. 在项目的顶级目录中,为资源包创建一个目录,此目录以资产资源包名称命名,资源包名称必须以字母开头,并且只能包含字母、数字和下划线。
  2. 在创建的资源包目录中,创建一个 build.gradle 文件。在配置文件中引入com.android.asset-pack插件,并指定资源包的名称以及分发类型。如下示例:
// 资产资源包目录下的 build.gradle 配置文件
plugins {
    id 'com.android.asset-pack' // 插件名称
}

assetPack {
    packName = "asset_res_pack" // 指定资产资源包名称(资源目录会以此命名)
    dynamicDelivery {
        deliveryType = "install-time"//指定分发类型,install-time 即为安装时分发,可配置类型有[ install-time | fast-follow | on-demand ],其他类型以后再进行介绍
    }
}
  1. 在项目的应用级 build.gradle 配置文件中,在 android 块内部添加项目中每个资源包的名称,如下所示:
// 在应用级 build.gradle 配置文件中
android {
    ...
    assetPacks = [":asset_res_packe"] // 指定资源包名称,如果有多个资源包,可在数组中添加多个,用半角逗号隔开
}
  1. 在项目级的 settings.gradle 配置文件中定义资源包模块,如下所示
//  在项目级的 settings.gradle 配置文件中定义资源包模块
include ':assets_res_pack'

4.2 添加资产资源

    接下来,就需要将安装时分发的资产,添加到新建的模块中。

  1. 在新建资源包目录中,创建src/main/assets子目录。
  2. 将需要安装时分发的资产资源(原应用级src/main/assets目录下的资产资源),移动到新建的资源包目录下对应的子目录中。此时目录结构大致如下:
|-Project-Root/
    |-build.gradle
    |-settings.gradle
    |-app/
    |-asset_res_packe/
        |-build.gradle
        |-src/
            |-main/
                |-assets/
                    |-asset-resource-directories/(资产资源目录)

4.3 构建Android App Bundle(aab)

    在生成的aab文件中,每个资产资源包都会在根目录下以资源包名为目录名称新建一个目录,如下图所示:
aab文件中资源包的目录结构

    asset_res_pack 目录为资源包目录,在资源包目录内部,主要有两部份内容:

  1. AndroidManifest.xml清单文件(此文件是构建aab时自动生成),所在目录为 asset-pack-name/manifest/AndroidManifest.xml,清单文件中主要是配置资源包的标识符和分发模式。如下示例:
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:dist="http://schemas.android.com/apk/distribution"
    package="com.rastargame.sdk.oversea.demo"
    platformBuildVersionCode="31"
    platformBuildVersionName="12"
    split="asset_res_pack"
    android:compileSdkVersion="31"
    android:compileSdkVersionCodename="12" >

    <dist:module dist:type="asset-pack" > <!-- 资源包类型,asset资产资源包 -->
        <dist:fusing dist:include="true" />

        <dist:delivery>
            <dist:install-time /> <!-- 分发模式:安装时分发 -->
        </dist:delivery>
    </dist:module>

</manifest>
  1. 资产资源文件,这些资源文件存放在asset-pack-name/assets/your-asset-directories内部,内部的资源文件会在安装时同时安装到对应的asset资产目录中,程序通过 AssetManager API访问相应目录下的资源。

4.4 资源访问

    安装时分发(install-time)是最简单的一种分发模式,无需借助特定的API库(如:Play Asset Delivery 库),使用原生的 AssetManager API 即可访问资产资源。如下示例:

final AssetManager assetManager = getAssets();
try {
    String [] rootFiles = assetManager.list("text/");
    for(int i = 0; i < rootFiles.length; i++) {
        LogUtils.e(rootFiles[i]);
    }
} catch (Exception e) {
    e.printStackTrace();
    LogUtils.e(e.toString());
}

资源访问效果(成功读取资源文件列表):
访问资产资源包中的资源文件

4.5 本地测试

    对于安装时分发的资源包,本地测试也是相当简单,只需要使用bundletool工具,对构建的aab生成apk集合(.apks),安装到设备中,测试资源包中的资源是否可以正常加载。

说明:本文中bundletool命令,已添加环境变量,对于没有添加环境变量,请使用 java -jar bundletool.jar 命令执行对应的 jar 文件。更多关于bundletool的使用,请参考:bundletool 工具使用详解

  1. 使用bundletool从aab文件生成apk集合(.apks),如下示例:
bundletool build-apks --bundle=demo-en-release.aab --output demo-en-local-test.apks --local-testing 

说明:--local-testing 参数是用来标记apk集合是用于本地测试。请参考:bundletool 工具使用详解 – 3.3 为 Android App Bundle 生成 APK 集合

    在生产的apk集合中(.apks文件),splits/目录下存放着各种apk文件,其中base-master.apk是基础apk,如果使用压缩工具打开,可以发现内部没有任何资产资源包的资源文件。资产资源包,在 asset-slices/ 目录下的apk文件,每一个资源包对应一个apk文件。

  1. 将生成的apk集合安装到设备,如下示例:
bundletool install-apks --apks=demo-en-local-test.apks --device-id=28d612e

命令执行效果:

E:\RastarGameSDK\RastarGameSdkOverseaX\demo\en\release>java -jar D:\Android\bundletool\bundletool-all-1.7.1.jar install-apks --apks=demo-en-local-test.apks --device-id=28d612e
The APKs have been extracted in the directory: C:\Users\ADMINI~1.P-2\AppData\Local\Temp\5384271713776026715
The APKs have been extracted in the directory: C:\Users\ADMINI~1.P-2\AppData\Local\Temp\5384271713776026715
ADB << rm -rf '/sdcard/Android/data/com.rastargame.sdk.oversea.demo/files/local_testing'
ADB >> OK
ADB << mkdir -p '/sdcard/Android/data/com.rastargame.sdk.oversea.demo/files/local_testing' && rmdir '/sdcard/Android/data/com.rastargame.sdk.oversea.demo/files/local_testing' && mkdir -p '/sdcard/Android/data/com.rastargame.sdk.oversea.demo/files/local_testing'
ADB >> OK
Pushed "/sdcard/Android/data/com.rastargame.sdk.oversea.demo/files/local_testing/base-xxhdpi.apk"
Pushed "/sdcard/Android/data/com.rastargame.sdk.oversea.demo/files/local_testing/base-master.apk"
Pushed "/sdcard/Android/data/com.rastargame.sdk.oversea.demo/files/local_testing/base-th.apk"
Pushed "/sdcard/Android/data/com.rastargame.sdk.oversea.demo/files/local_testing/base-vi.apk"
Pushed "/sdcard/Android/data/com.rastargame.sdk.oversea.demo/files/local_testing/base-id.apk"
Pushed "/sdcard/Android/data/com.rastargame.sdk.oversea.demo/files/local_testing/base-zh.apk"

说明:使用bundletool工具向指定设备安装apk集合,会根据设备选择指定的apk进行安装(并不是安装apk集合中的所有apk)。

  1. 运行程序,验证资源是否正常读取。

五、快速跟进分发(fast-follow)与按需分发(on-demand)详解

    快速跟进分发与按需分发比较类似,只是资源包的下载时机不一样。在资产分发配置,跟安装时分发是一致的(详细的请参考本文的4.1~4.3章节),不一样的地方就是资源的访问方式不一样。快速跟进分发和按需分发,资源包都不会随着程序下载安装而下载,而是程序下载安装之后才下载。下面将详细讲解一下集成步骤。

5.1 集成 Play Asset Delivery 库

    快速跟进分发和按需分发,需要通过Play Asset Delivery 库来管理以及下载资源包。在程序级别的 build.gradle 文件的依赖声明处,添加以下依赖:

dependencies {
    // This dependency is downloaded from the Google’s Maven repository.
    // So, make sure you also include that repository in your project's build.gradle file.
    implementation 'com.google.android.play:asset-delivery:2.1.0' // Java开发语言

    // For Kotlin users also add the Kotlin extensions library for Play Asset Delivery:
    implementation 'com.google.android.play:asset-delivery-ktx:2.1.0' // Kotlin开发语言
}

注意:以上两个依赖,跟进项目开发语言,选择其中一个即可。

5.2 Play Asset Delivery 库的使用

5.2.1 创建有效的AssetPackManager对象

    AssetPackManager对象是所有API的入口,首先要创建一个有效的AssetPackManager对象。如下示例:

// 有AssetPackManagerFactory工厂类创建一个AssetPackManager 实例对象
final AssetPackManager mAssetPackManager = AssetPackManagerFactory.getInstance(getApplicationContext());

5.2.1 查看资源包位置信息

    每个资源包都存储在应用内部存储空间中的单独文件夹中,可以使用 getPackLocation() 方法确定资源包的根文件夹位置,此方法将会返回以下值:

返回值状态
null未知 Asset Pack 或资产无法使用
有效的 AssetPackLocation 对象资源包根文件夹位于AssetPackLocation 对象 assetsPath() 指定的路径,现已可立即获取。

注意事项:应用应在每次启动时始终检查资源包是否有效(是否存在以及是否可用),请勿缓存资源包的路径信息,因为资源包可能会因应用更新或用户清除应用数据而变为无效。

5.2.2 获取资源包的状态信息

    如果资源包无效(不可用),那么就需要获取资源包的状态信息,可以通过getPackStates() (Java) 或 requestPackStates()(Kotlin) 方法获取资源包的状态信息。状态信息包括资源包的大小、是否已下载(或者下载中的状态)等(Google也要求应用在提取资源包之前,必须向用户披露下载内容的大小)。本文基于 Java 开发语言,因此主要介绍 getPackStates() 方法的使用。getPackStates() 方法返回一个 Task<AssetPackStates> 的异步方法,AssetPackStates 对象的 packStates() 方法会返回一个映射表 Map<String, AssetPackState>,此映射包含所请求的每个资源包的状态,按资源包名称进行键控。如下示例

final String assetPackName = "asset_res_pack"; // 资源包名称
mAssetPackManager.getPackStates(Arrays.asList(assetPackName))
    .addOnCompleteListener(new OnCompleteListener<AssetPackStates>() {
        @Override
        public void onComplete(@NonNull Task<AssetPackStates> task) {
            try {
                LogUtils.e("Get asset pack states onComplete");
                AssetPackStates assetPackStates = task.getResult();
                AssetPackState assetPackState = assetPackStates.packStates().get(assetPackName);
                if(null == assetPackState) {
                    LogUtils.e("No asset pack named '" + assetPackName + "'");
                    return;
                }
                LogUtils.e(String.format("Asset Pack(%s) status = %d\n%s\n%s\n%s", assetPackState.name()
                        ,assetPackState.status()
                        ," - totalBytesToDownload = " + assetPackState.totalBytesToDownload()
                        ," - bytesDownloaded = " + assetPackState.bytesDownloaded()
                        ," - transferProgressPercentage = " + assetPackState.transferProgressPercentage()));

                if(AssetPackStatus.COMPLETED == assetPackState.status()) {
                    // 资源包已经下载并且转移,应用可以使用
                    LogUtils.e("Asset pack downloaded and transfer");
                } else if(AssetPackStatus.NOT_INSTALLED == assetPackState.status()) {
                    // 资源包未安装
                    LogUtils.e("Asset pack not installed");                }
            } catch (Exception e) {
                LogUtils.e("Get asset pack onException -- " + e.getMessage());
            }
        }
    });

程序安装后运行,执行以上代码进行检查,会得到以下输出:
程序启动检查资源包状态

    从上面的示例可以看出,程序安装运行之后,资源包的状态是(NOT_INSTALLED = 8),从需要下载文件大小以及已经下载文件大小可以得出,这个状态是还未下载安装的。于是,接下来,就需要下载安装资源包数据。

5.2.3 下载安装资源包

    下载和安装资源包,调用 fetch() 方法(Kotlin语言调用 requestFetch() 方法),该方法返回一个 Task<AssetPackStates> 类型的异步方法,如下示例:

mAssetPackManager.fetch(Arrays.asList(assetPackName));

注意:虽然 fetch() 方法返回的是一个 Task<AssetPackStates> 类型的异步方法,但是该方法执行完成并不意味着资源包下载完成,可以使用 addOnCompleteListener() 添加完成监听,方法执行完成之后, 资源包的状态是 AssetPackStatus.PENDING = 1 ,这个状态的含义是:资源包下载任务处于待办并将要处理(The asset pack download is pending and will be processed soon.)。

5.2.4 添加资源包状态监听

    既然 fetch() 方法执行完成并不是资源包下载安装完成,那么我们需要有一个可以监测资源包状态变化的监听,这样才能掌握资源包的下载安装的过程以及何时完成,提供更加友好的UI交互,以及资源包现在安装完成之后进行下一步操作。添加资源包状态监听,在 AssetPackManager 实例对象中添加。如下示例:

final AssetPackStateUpdateListener mAssetPackStateUpdateListener = new AssetPackStateUpdateListener() {
    @Override
    public void onStateUpdate(@NonNull AssetPackState assetPackState) {
        switch (assetPackState.status()) {
            case AssetPackStatus.CANCELED:
                LogUtils.e(String.format("Asset Pack(%s) status CANCELED", assetPackState.name()));
                break;
            case AssetPackStatus.COMPLETED:
                LogUtils.e(String.format("Asset Pack(%s) status COMPLETED\n%s\n%s\n%s", assetPackState.name()
                        ," - totalBytesToDownload = " + assetPackState.totalBytesToDownload()
                        ," - bytesDownloaded = " + assetPackState.bytesDownloaded()
                        ," - transferProgressPercentage = " + assetPackState.transferProgressPercentage()));
                checkAssetPackStatus();
                break;
            case AssetPackStatus.DOWNLOADING:
                LogUtils.e(String.format("Asset Pack(%s) status DOWNLOADING\n%s\n%s", assetPackState.name()
                        ," - totalBytesToDownload = " + assetPackState.totalBytesToDownload()
                        ," - bytesDownloaded = " + assetPackState.bytesDownloaded()));
                break;
            case AssetPackStatus.FAILED:
                LogUtils.e(String.format("Asset Pack(%s) status FAILED", assetPackState.name()));
                break;
            case AssetPackStatus.NOT_INSTALLED:
                LogUtils.e(String.format("Asset Pack(%s) status NOT_INSTALLED", assetPackState.name()));
                break;
            case AssetPackStatus.PENDING:
                LogUtils.e(String.format("Asset Pack(%s) status PENDING", assetPackState.name()));
                break;
            case AssetPackStatus.TRANSFERRING:
                LogUtils.e(String.format("Asset Pack(%s) status TRANSFERRING\n%s", assetPackState.name()
                        ," - transferProgressPercentage = " + assetPackState.transferProgressPercentage()));
                break;
            case AssetPackStatus.UNKNOWN:
                LogUtils.e(String.format("Asset Pack(%s) status UNKNOWN", assetPackState.name()));
                break;
            case AssetPackStatus.WAITING_FOR_WIFI:
                LogUtils.e(String.format("Asset Pack(%s) status WAITING_FOR_WIFI", assetPackState.name()));
                break;
            default:
                break;
        }
    }
};

// 注册监听
mAssetPackManager.registerListener(mAssetPackStateUpdateListener); 

// 反注册监听,不在需要时,及时反注册监听,防止内存溢出
mAssetPackManager.unregisterListener(mAssetPackStateUpdateListener); 

    添加资源包状态监听之后,调用 fetch() 方法,将会得的如下的日志信息,可看到资源包下载安装成功的状态依次是 PENDING -> DOWNLOADING -> TRANSFERRING -> COMPLETED。其中 DOWNLOADINGTRANSFERRING 会根据资源文件的大小,可能会存在多次回调。下载安装资源包完成之后,再次调用获取资源包位置的方法,就可以成功获取到资源包的位置,获取资源包的资源并使用。

监听资源包状态

注意:在用户安装或更新应用后,Play 商店会自动触发下载所有快速跟进分发(fast-follow)的资源包。但是,当用户打开程序时,这些资源包可能还无法供用户使用(为完成下载安装)。因此,开发者必须在每次应用启动时检查快速跟进分发(fast-follow)的资源包的状态。如果正在下载,请使用监听器对其状态进行监控。如果下载已取消或暂停,可以使用 fetch() 方法恢复下载.。

5.2.5 内容较大的资源文件处理

    Play Asset Delivery 库在下载内容较大(超过150MB)的资源文件时,如果用户没有明确同意使用移动数据下载,且没有连接Wi-Fi或者Wi-Fi连接断开,下载都将会暂停,资源包状态会变为 AssetPackStatus.WAITING_FOR_WIFI。在此状态下,开发者可以使用 showCellularDataConfirmation() 方法(Kotlin语言环境使用 requestCellularDataConfirmation() 方法)弹出提示框,询问用户是否同意使用移动数据下载所有资源包,统一才会在一栋数据继续下载。 showCellularDataConfirmation() 方法返回一个 Task<Integer> 异步方法,可以通过 addOnSuccessListener() 添加监听获取用户操作结果,其中 Integer 类型数据为结果吗(ResultCcode),Activity.RESULT_OK 为用户同意,Activity.RESULT_CANCEL 为用户拒绝。如下示例:

boolean waitForWifiConfirmationShown = false;
final AssetPackStateUpdateListener mAssetPackStateUpdateListener = new AssetPackStateUpdateListener() {
    @Override
    public void onStateUpdate(@NonNull AssetPackState assetPackState) {
        switch (assetPackState.status()) {
            case AssetPackStatus.CANCELED:
                LogUtils.e(String.format("Asset Pack(%s) status CANCELED", assetPackState.name()));
                break;
            case AssetPackStatus.COMPLETED:
                LogUtils.e(String.format("Asset Pack(%s) status COMPLETED\n%s\n%s\n%s", assetPackState.name()
                        ," - totalBytesToDownload = " + assetPackState.totalBytesToDownload()
                        ," - bytesDownloaded = " + assetPackState.bytesDownloaded()
                        ," - transferProgressPercentage = " + assetPackState.transferProgressPercentage()));
                checkAssetPackStatus();
                break;
            case AssetPackStatus.DOWNLOADING:
                LogUtils.e(String.format("Asset Pack(%s) status DOWNLOADING\n%s\n%s", assetPackState.name()
                        ," - totalBytesToDownload = " + assetPackState.totalBytesToDownload()
                        ," - bytesDownloaded = " + assetPackState.bytesDownloaded()));
                break;
            case AssetPackStatus.FAILED:
                LogUtils.e(String.format("Asset Pack(%s) status FAILED", assetPackState.name()));
                break;
            case AssetPackStatus.NOT_INSTALLED:
                LogUtils.e(String.format("Asset Pack(%s) status NOT_INSTALLED", assetPackState.name()));
                break;
            case AssetPackStatus.PENDING:
                LogUtils.e(String.format("Asset Pack(%s) status PENDING", assetPackState.name()));
                break;
            case AssetPackStatus.TRANSFERRING:
                LogUtils.e(String.format("Asset Pack(%s) status TRANSFERRING\n%s", assetPackState.name()
                        ," - transferProgressPercentage = " + assetPackState.transferProgressPercentage()));
                break;
            case AssetPackStatus.UNKNOWN:
                LogUtils.e(String.format("Asset Pack(%s) status UNKNOWN", assetPackState.name()));
                break;
            case AssetPackStatus.WAITING_FOR_WIFI:
                LogUtils.e(String.format("Asset Pack(%s) status WAITING_FOR_WIFI", assetPackState.name()));
                if(!waitForWifiConfirmationShown) {
                    mAssetPackManager.showCellularDataConfirmation(RSDemoActivity.this)
                            .addOnSuccessListener(new OnSuccessListener<Integer>() {
                                @Override
                                public void onSuccess(Integer resultCode) {
                                    if(RESULT_OK == resultCode) {
                                        LogUtils.e("showCellularDataConfirmation --- User agree");
                                    } else if(RESULT_CANCELED == resultCode) {
                                        LogUtils.e("showCellularDataConfirmation --- User denied");
                                    }
                                }
                            });
                    waitForWifiConfirmationShown = true;
                }
                break;
            default:
                break;
        }
    }
};

注意:如果应用不在 AssetPackStatus.WAITING_FOR_WIFI状态下调用此方法询问用户,默认只能在 Wi-Fi 网络下载,因此资源包下载将会暂停,仅当用户连接到 Wi-Fi 网络环境时才会自动恢复下载。

5.2.6 资源包资源的获取

    当某个资源包的状态变为 AssetPackStatus.COMPLETED,即已经完成下载并安装,此时就可以使用 getPackLocation() 方法获取资源包的位置(参考:5.2.2 获取资源包的状态信息),assetsPath() 方法获取的路径及资源包安装的 assets 路径(示例:/data/data/com.rastargame.sdk.oversea.demo/files/assetpacks/asset_res_pack/210/210/assets),该目录下的文件结构,跟创建资源包时的目录结构一致。接下来,程序就可以通过文件读写的 API 访问资源包内容。

说明:资源包下载安装至应用内部数据目录,因此访问文件不需要存储权限。

5.3 Play Asset Delivery 库的其他方法

5.3.1 取消请求资源包

    使用 cancel() 方法可以取消资源包的请求操作。

注意:取消请求操作,是尽力而为的操作,并不一定会达到预期的效果。

5.3.2 移除资源包

    使用 removePack()(Kotlin语言环境为 requestRemovePack())方法,可以移除资源包。

5.3.3 获取多个资源包的位置信息

    使用 getPackLocations() 可以批量查询多个资源包的状态,此方法将返回资源包与其位置的映射。getPackLocations() 返回的映射包含当前已下载且为最新状态的每个资源包的条目。

5.4 本地测试

    快速根据分发本地测试跟安装时分发步骤基本一致,可以参考:4.5 本地测试

注意:由于快速跟进分发和按需分发的资源包,需要在应用安装后进行下载,本地测试只能模拟下载过程,并不是真实的从网络环境下载,在应用安装时就将资源包存储在设备存储中,因此对于快速跟进分发和按需分发的本地测试,无法测试网络 WAITING_FOR_WIFI 的场景。另外,本地测试也不支持更新,需要手动卸载旧版本。

六、编后语

    对于 Google Play 的资产分发,可以有效解决 apk 大小限制,巧用快速跟进分发和按需分发,也可以提高用户体验。对 Google Play 的资产分发就介绍到这,喜欢的朋友就点个赞吧。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/792754.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

虚拟机网络桥接,详细操作步骤,本地连接虚拟机

虚拟机网络桥接 文章目录 虚拟机网络桥接一&#xff0c;首先查看主机连接网络的网关二、打开虚拟机的worksation服务三、修改主机的VMnet8的IPV4属性四、修改虚拟机的workstation的虚拟网络五、修改VMnet8的IP,网关六、把虚拟机设置为桥接模式七、修改虚拟机的静态ip和对应的网…

JAVA面试总结-Redis篇章(三)——缓存雪崩

JAVA面试总结-Redis篇章&#xff08;三&#xff09;——缓存雪崩

测试开发之系统篇 - 常用系统命令

目录 前言&#xff1a; 查看占用端口的进程 Linux Windows 杀死进程 Linux Windows 按名称查看进程 命令行管道 后台运行服务 修改文件 Linux Mac 复制目录到远程 实时查看文件内容 查看正在运行的服务 查看服务状态 重启服务 查看内存状况 查看磁盘状况 …

基于MAXENT模型的生物多样性生境模拟与保护优先区甄选、自然保护区布局优化及未来气候变化下评估及论文写作教程

详情点击链接&#xff1a;基于MAXENT模型的生物多样性生境模拟与保护优先区甄选、自然保护区布局优化及未来气候变化下评估中的应用及论文写作 一&#xff1a;生物多样性保护格局与自然保护区格局优化 1.我国生物多样性格局与分布&#xff1b; 2.我国自然保护区格局与分布&…

前端年度工作述职报告优秀

前端年度工作述职报告优秀篇1 尊敬的各位领导、各位同仁&#xff1a; 大家好!按照20__年度我公司就职人员工作评估的安排和要求&#xff0c;我认真剖析、总结了自己的工作情况&#xff0c;现将本人工作开展情况向各位领导、同仁做以汇报&#xff0c;有不妥之处&#xff0c;希…

注册外部的AudioPolicy,监听焦点变化

开始 AudioFocus分发流程 在上面分发流程分析中&#xff0c;最终是在MediaFocusControl中进行处理的&#xff0c;在requestAudioFocus​方法中可以看到在执行焦点栈逻辑时会先执行AudioPolicy相关逻辑&#xff0c; 因此我们试一试可不可以利用这一点&#xff0c;来注册外部的…

用于开漏模式和推拉模式的 4bit 双向电平转换器MS4554N/MS4554N1

MS4554N/MS4554N1 是一款双向电平转换器&#xff0c;可以应用在 混合电压的数字信号系统。其使用两个独立构架的电源供电&#xff0c; A 端供电电压范围是 1.65V 到 5.5V &#xff0c; B 端供电电压范围是 2.3V 到 5.5V 。可用在电源电压为 1.8V 、 2.5V 、 3.3V 和…

FPGA学习——按键消抖的两种实现方法

文章目录 一、按键消抖简介1.1、为什么要按键消抖 二、C4开发板原理图三、按键消抖源码3.1、方案一&#xff08;每当检测到下降沿便开始重新计数&#xff09;3.2、方案二&#xff08;检测到第一次下降沿后便开始计数&#xff09; 四、仿真代码及仿真波形图五、拓展&#xff1a;…

【金万维】使用天联高级版登录用友U8,文件输出操作。

【操作步骤】 1、通过天高客户端登录U8后&#xff0c;打开对应的报表或者列表&#xff08;以余额表为例&#xff09;。 2、点击“输出”按钮后&#xff0c;弹出“另存为”窗口&#xff0c; 注意&#xff1a;当前“保存在&#xff1a;桌面”这个路径为服务器的桌面&#xff0c;…

matlab 读取fig中的数据

1.使用命令打开fig open(文件名.fig) 2.查看图像包含的数据和属性 Location是原始的&#xff0c;Normal 可能是调整了顺序后的。

echarts制作多个纵轴的折线图

代码 <script type"text/javascript"> $(function (){var myChart echarts.init(document.getElementById(main));option {color: ["#9bbb59","#0B438B","#4141F1","#F81945","#4bacc6","#F89E19&q…

怎么把知识问答做成二维码?分享简单的二维码制作工具

现在无论是工厂的职工还是学校的学生&#xff0c;都会有各种各样的知识问答。传统的纸质知识问答&#xff0c;不仅浪费人力物力&#xff0c;还需要占用上班或是上学的时间来完成。为了方便大家使用&#xff0c;我们可以把知识问答做成二维码图片&#xff0c;随时随地手机扫一扫…

【代码随想录day20】二叉搜索树中的搜索

题目 给定二叉搜索树&#xff08;BST&#xff09;的根节点 root 和一个整数值 val。 你需要在 BST 中找到节点值等于 val 的节点。 返回以该节点为根的子树。 如果节点不存在&#xff0c;则返回 null 。 示例 1: 思路 因为这里是二叉排序树&#xff0c;所以不用爆搜所有节点…

RTOS 低功耗设计原理及实现

RTOS 低功耗设计原理及实现 文章目录 RTOS 低功耗设计原理及实现&#x1f468;‍&#x1f3eb;前言&#x1f468;‍&#x1f52c;Tickless Idle Mode 的原理及实现&#x1f468;‍&#x1f680;Tickless Idle Mode 的软件设计原理&#x1f468;‍&#x1f4bb;Tickless Idle Mo…

超过443万人次观看|央媒聚焦全球吉商大会,实在智能携国产大模型TARS出席

据吉林新闻联播近期报道&#xff0c;第八届全球吉商大会、第二届吉林省校友人才促进吉林振兴发展大会在长春开幕。省委书记景俊海出席开幕式并讲话。省委副书记、省长胡玉亭主持开幕式。省政协主席朱国贤为“吉商突出贡献人物”颁奖。全国工商联副主席汪鸿雁致辞。开幕式全程进…

3.2.20:DTP与Datepicker实现日期的输入

【分享成果&#xff0c;随喜正能量】人生艰难自不必去回避&#xff0c;人生艰难说多了也是白说&#xff0c;为什么&#xff0c;解决不了问题&#xff0c;说了也还是那么难。。 我给VBA的定义&#xff1a;VBA是个人小型自动化处理的有效工具。利用好了&#xff0c;可以大大提高…

PostgreSQL——sql文件导入

Windows方式&#xff1a; 进入PostgreSQL安装目录的bin&#xff0c;进入cmd 执行命令&#xff1a; psql -d 数据库名 -h localhost -p 5432 -U 用户名 -f 文件目录 SQL Shell: 执行命令&#xff1a; \i 文件目录(Windows下要加引号和双斜线)

第五章 聚合函数与内置函数

第五章 聚合函数与内置函数 一、聚合函数1、常用聚合函数2、示例&#xff08;1&#xff09;count&#xff08;2&#xff09;sum&#xff08;3&#xff09;avg&#xff08;4&#xff09;max&#xff08;5&#xff09;min 二、内置函数1、日期函数&#xff08;1&#xff09;总览&…

【Java基础学习打卡18】运算符(上)

目录 前言一、运算符和表达式1.运算符2.表达式 二、算术运算符1.加法运算符2.减法运算符3.乘法运算符4.除法运算符5.取余运算符6.表达式类型自动提升 总结 前言 本文主要介绍运算符和表达式&#xff0c;及运算符中的算术运算符。在 Java 编程中&#xff0c;运算符起着非常重要…

Java获取调用当前方法的方法名和行数(亲测可行)

有时候一个方法被很多方法调用了&#xff0c;但是在调试应用程序的时候&#xff0c;需要知道是哪个方法调用它的&#xff0c;方便定位bug问题。否者&#xff0c;比较难以理清和解决一些bug问题。 适用&#xff1a;任何适用java语言编程的地方&#xff0c;java后端和android端。…