本指南介绍如何使用原生代码(C 或 C++)在您的应用中支持应用内更新。对于使用Kotlin 编程语言或 Java 编程语言进行实现的情况,以及使用Unity进行实现的情况,都有单独的指南。
原生 SDK 概述
Play Core Native SDK 是Play Core SDK系列的一部分。Native SDK 包含一个 C 头文件,app_update.h
,它包装了来自 Java Play 应用内更新库的AppUpdateManager
。此头文件允许您的应用直接从您的原生代码调用应用内更新的 API。
设置您的开发环境
下载 Play Core Native SDK
在下载之前,您必须同意以下条款和条件。
条款和条件
上次修改时间:2020 年 9 月 24 日- 通过使用 Play Core 软件开发工具包,您同意这些条款以及Google API 服务条款(“API 服务条款”)。如果这些条款之间存在冲突,则这些条款将优先于 API 服务条款。请仔细阅读这些条款和 API 服务条款。
- 出于这些条款的目的,“API”是指 Google 的 API、其他开发者服务和相关软件,包括任何可再发行代码。
- “可再发行代码”是指 Google 提供的调用 API 的对象代码或头文件。
- 根据这些条款和 API 服务条款,您可能仅出于将其作为 API 客户端的一部分包含在内的目的而复制和分发可再发行代码。Google 及其许可方拥有可再发行代码中所有权利、所有权和权益,包括任何和所有知识产权和其他专有权利。您不得修改、翻译或创建可再发行代码的衍生作品。
- Google 可能会随时更改这些条款,并提供通知以及拒绝进一步使用 Play Core 软件开发工具包的机会。Google 将在https://developer.android.com/guide/playcore/license上发布有关条款修改的通知。更改不具有追溯效力。
执行以下任一操作
- 安装Android Studio版本 4.0 或更高版本。使用 SDK 管理器 UI 安装 Android SDK 平台版本 10.0(API 级别 29)。
- 安装Android SDK 命令行工具并使用
sdkmanager
安装 Android SDK 平台版本 10.0(API 级别 29)。
通过使用SDK 管理器安装最新的 CMake 和 Android Native Development Kit (NDK) 来为原生开发准备 Android Studio。有关创建或导入原生项目的更多信息,请参阅NDK 入门。
下载 zip 文件并将其解压缩到您的项目旁边。
下载链接 大小 SHA-256 校验和 37.8 MiB 9db60185185342f28d2c278b60222333608c67bc022e458a25224eaea8c4c4b7 更新您的应用的
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")) ... }
更新您的应用的
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 Native SDK 可能会收集与版本相关的数据,以允许 Google 改善产品,包括
- 应用的包名
- 应用的包版本
- Play Core Native SDK 的版本
当您将应用包上传到 Play Console 时,将收集这些数据。要选择退出此数据收集过程,请删除 build.gradle 文件中$playcoreDir/playcore-native-metadata.jar
的导入。
请注意,与您使用 Play Core Native SDK 以及 Google 使用收集的数据相关的数据收集与 Google 在您将应用包上传到 Play Console 时在 Gradle 中声明的库依赖项的收集是分开且独立的。
将 Play Core Native SDK 集成到您的项目后,在包含 API 调用的文件中包含以下行
#include "play/app_update.h"
初始化应用内更新 API
在每次使用应用内更新 API 时,请先通过调用 AppUpdateManager_init()
函数进行初始化,如下例所示,该示例使用 android_native_app_glue.h
构建。
void android_main(android_app* app) {
app->onInputEvent = HandleInputEvent;
AppUpdateErrorCode error_code =
AppUpdateManager_init(app->activity->vm, app->activity->clazz);
if (error_code == APP_UPDATE_NO_ERROR) {
// You can use the API.
}
}
检查更新可用性
在请求更新之前,请检查您的应用是否有可用的更新。 AppUpdateManager_requestInfo()
会启动一个异步请求,收集启动应用内更新流程所需的信息。如果请求成功启动,则该函数返回 APP_UPDATE_NO_ERROR
。
AppUpdateErrorCode error_code = AppUpdateManager_requestInfo()
if (error_code == APP_UPDATE_NO_ERROR) {
// The request has successfully started, check the result using
// AppUpdateManager_getInfo.
}
您可以使用 AppUpdateManager_getInfo()
跟踪请求的进行过程和结果。除了错误代码外,此函数还会返回一个 AppUpdateInfo
不透明结构体,您可以使用它来检索有关更新请求的信息。例如,您可能希望在每个游戏循环中调用此函数,直到它为 info
返回非空结果。
AppUpdateInfo* info;
GameUpdate() {
// Keep calling this in every game loop until info != nullptr
AppUpdateErrorCode error_code = AppUpdateManager_getInfo(&info);
if (error_code == APP_UPDATE_NO_ERROR && info != nullptr) {
// Successfully started, check the result in the following functions
}
...
}
检查更新陈旧性
除了检查更新是否可用之外,您可能还需要检查自用户上次通过 Play 商店收到更新通知以来经过了多长时间。这可以帮助您确定是否应启动灵活更新或立即更新。例如,您可能需要等待几天才能通过灵活更新通知用户,然后再等待几天才能要求立即更新。
使用 AppUpdateInfo_getClientVersionStalenessDays()
检查自更新在 Play 商店中可用以来经过的天数。
int32_t staleness_days = AppUpdateInfo_getClientVersionStalenessDays(info);
检查更新优先级
Google Play 开发者 API 允许您设置每个更新的优先级。这使您的应用能够决定向用户推荐更新的力度。例如,请考虑以下设置更新优先级的策略:
- 次要 UI 改进:**低优先级**更新;既不请求灵活更新也不请求立即更新。仅在用户未与您的应用交互时更新。
- 性能改进:**中优先级**更新;请求灵活更新。
- 关键安全更新:**高优先级**更新;请求立即更新。
为了确定优先级,Google Play 使用 0 到 5 之间的整数值,其中 0 为默认值,5 为最高优先级。要设置更新的优先级,请使用 Google Play 开发者 API 中 Edits.tracks.releases
下的 inAppUpdatePriority
字段。发布中的所有新增版本都视为与该发布具有相同的优先级。优先级只能在推出新版本时设置,以后无法更改。
按照 Play 开发者 API 文档 中的说明,使用 Google Play 开发者 API 设置优先级。在 Edit.tracks
资源中指定应用内更新优先级,该资源传递到 Edit.tracks: update
方法中。以下示例演示了如何发布版本代码为 88 且 inAppUpdatePriority
为 5 的应用。
{ "releases": [{ "versionCodes": ["88"], "inAppUpdatePriority": 5, "status": "completed" }] }
在您的应用代码中,您可以使用 AppUpdateInfo_getPriority()
检查给定更新的优先级。
int32_t priority = AppUpdateInfo_getPriority(info);
启动更新
确认更新可用后,您可以使用 AppUpdateManager_requestStartUpdate()
请求更新。在请求更新之前,请获取最新的 AppUpdateInfo
对象并创建一个 AppUpdateOptions
对象来配置更新流程。AppUpdateOptions
对象定义了应用内更新流程的选项,包括更新应该是灵活更新还是立即更新。
以下示例创建了一个用于灵活更新流程的 AppUpdateOptions
对象。
// Creates an AppUpdateOptions configuring a flexible in-app update flow.
AppUpdateOptions* options;
AppUpdateErrorCode error_code = AppUpdateOptions_createOptions(APP_UPDATE_TYPE_FLEXIBLE, &options);
以下示例创建了一个用于立即更新流程的 AppUpdateOptions
对象。
// Creates an AppUpdateOptions configuring an immediate in-app update flow.
AppUpdateOptions* options;
AppUpdateErrorCode error_code = AppUpdateOptions_createOptions(APP_UPDATE_TYPE_IMMEDIATE, &options);
AppUpdateOptions
对象还包含一个 AllowAssetPackDeletion
字段,该字段定义了在设备存储空间有限的情况下,是否允许更新清除 资源包。默认情况下,此字段设置为 false
,但您可以使用 AppUpdateOptions_setAssetPackDeletionAllowed()
方法将其设置为 true
。
bool allow = true;
AppUpdateErrorCode error_code = AppUpdateOptions_setAssetPackDeletionAllowed(options, allow);
获取最新的 AppUpdateInfo
对象和正确配置的 AppUpdateOptions
对象后,请调用 AppUpdateManager_requestStartUpdate()
以异步请求更新流程,并将 Android Activity jobject
作为最后一个参数传递。
AppUpdateErrorCode request_error_code =
AppUpdateManager_requestStartUpdate(info, options, app->activity->clazz);
要释放资源,请分别调用 AppUpdateInfo_destroy()
和 AppUpdateOptions_destroy()
释放不再需要的 AppUpdateInfo
和 AppUpdateOptions
实例。
AppUpdateInfo_destroy(info);
AppUpdateOptions_destroy(options);
对于立即更新流程,Google Play 会显示用户确认页面。当用户接受请求时,Google Play 会自动在前台下载并安装更新,然后如果安装成功,则将应用重新启动到更新的版本。
对于灵活更新流程,您可以继续请求最新的 AppUpdateInfo
对象以跟踪当前更新状态,同时用户继续与应用交互。下载成功完成后,您必须通过调用 AppUpdateManager_requestCompleteUpdate()
触发更新的完成,如下例所示。
AppUpdateStatus status = AppUpdateInfo_getStatus(info);
if (status == APP_UPDATE_DOWNLOADED) {
AppUpdateErrorCode error_code = AppUpdateManager_requestCompleteUpdate();
if (error_code != APP_UPDATE_NO_ERROR)
{
// There was an error while completing the update flow.
}
}
在您的应用完成使用 API 后,请调用 AppUpdateManager_destroy()
函数释放资源。
错误处理
本部分介绍特定 AppUpdateErrorCode
值指示的常见错误的解决方案。
- 错误代码
-110, APP_UPDATE_INITIALIZATION_NEEDED
表示 API 未成功初始化。请调用AppUpdateManager_init()
初始化 API。 - 错误代码
-4, APP_UPDATE_INVALID_REQUEST
表示更新流程请求的一些参数格式错误。请确保AppUpdateInfo
和AppUpdateOptions
对象不为空且格式正确。 - 错误代码
-5, APP_UPDATE_UNAVAILABLE
表示没有可用的适用更新。请确保目标版本具有相同的 包名、应用 ID 和 签名密钥。如果存在可用的更新,请清除应用的缓存并再次调用AppUpdateManager_requestAppUpdateInfo()
以刷新AppUpdateInfo
。 - 错误代码
-6, APP_UPDATE_NOT_ALLOWED
表示AppUpdateOption
对象指示的更新类型不允许。在启动更新流程之前,请检查AppUpdateInfo
对象是否指示允许更新类型。
后续步骤
测试应用的应用内更新 以验证您的集成是否正常工作。