支持应用内更新(原生代码)

本指南将介绍如何使用原生代码(C 或 C++)在您的应用中支持应用内更新。我们针对实现使用 Kotlin 编程语言或 Java 编程语言以及 Unity 的情况提供了单独的指南。

原生 SDK 概览

Play Core 原生 SDK 是 Play Core SDK 系列的一部分。该原生 SDK 包含一个 C 头文件 app_update.h,用于封装来自 Java Play 应用内更新库的 AppUpdateManager。此头文件允许应用直接从原生代码中调用适用于应用内更新的 API。

设置您的开发环境

下载 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.14.0.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 校验和
    36 MiB 782a8522d937848c83a715c9a258b95a3ff2879a7cd71855d137b41c00786a5e
  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
            ...)
    

数据收集

为便于 Google 改进产品,Play Core 原生 SDK 可能会收集版本相关数据,包括:

  • 应用的软件包名称
  • 应用的软件包版本
  • Play Core 原生 SDK 的版本

您将应用软件包上传到 Play 管理中心时,系统会收集这些数据。如需停用此数据收集流程,请移除 build.gradle 文件中的 $playcoreDir/playcore-native-metadata.jar 导入项。

请注意,这种与使用 Play Core 原生 SDK 相关的数据收集行为,以及 Google 使用所收集数据的行为,与您将应用软件包上传至 Play 管理中心时 Google 收集在 Gradle 中声明的库依赖项无关且相互独立。

将 Play Core 原生 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 返回非 null 结果:

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 Developer API,您可以设置每项更新的优先级。这样,应用就可以决定向用户推荐更新的强烈程度。例如,您可以考虑使用以下策略来设置更新优先级:

  • 细微的界面改进:低优先级更新;既不请求灵活更新,也不请求立即更新。仅在用户没有与您的应用互动时进行更新。
  • 性能提升:中优先级更新;请求灵活更新。
  • 关键安全更新:高优先级更新;请求立即更新。

Google Play 使用 0 到 5 之间的整数值来确定优先级,其中 0 是默认值,5 代表最高优先级。如需设置更新的优先级,请使用 Google Play Developer API 中 Edits.tracks.releases 下的 inAppUpdatePriority 字段。发布版本中的所有新增版本都被视为与发布版本具有相同的优先级。只有在发布新版本时才能设置优先级,且以后无法更改。

您可以按照 Play Developer API 文档中所述的方法,使用 Google Play Developer API 来设置优先级。请在 Edit.tracks: update 方法中传递的 Edit.tracks 资源中指定应用内更新的优先级。以下示例演示了如何发布一个版本代码为 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() 以释放不再需要的 AppUpdateInfoAppUpdateOptions 的实例。

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,这表示更新流程请求的某些参数的格式不正确。请进行检查以确保 AppUpdateInfoAppUpdateOptions 对象不为 null 且格式正确。
  • 错误代码为 -5, APP_UPDATE_UNAVAILABLE,这表示没有可用的适用更新。请确保目标版本具有相同的软件包名称应用 ID签名密钥。如果有可用的更新,请清除应用的缓存并再次调用 AppUpdateManager_requestAppUpdateInfo() 以刷新 AppUpdateInfo
  • 错误代码为 -6, APP_UPDATE_NOT_ALLOWED,这表示 AppUpdateOption 对象指明的更新类型是不允许的。在启动更新流程之前,请检查 AppUpdateInfo 对象是否指明允许相应更新类型。

后续步骤

测试应用的应用内更新,以验证您的集成是否正常运行。