This guide describes how to support in-app updates in your app using native code (C or C++). There are separate guides for cases where your implementation uses the Kotlin programming language or the Java programming language, and cases where your implementation uses Unity.
Native SDK overview
The Play Core Native SDK is part of the Play Core
SDK family. The Native
SDK includes a C header file, app_update.h
, that wraps
AppUpdateManager
from the Java Play In-App Update Library. This header file allows your app to
call the API for in-app updates directly from your native code.
Set up your development environment
Download Play Core Native SDK
Before downloading, you must agree to the following terms and conditions.
Terms and Conditions
Last modified: September 24, 2020- 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.
- For purposes of these terms, "APIs" means Google's APIs, other developer services, and associated software, including any Redistributable Code.
- “Redistributable Code” means Google-provided object code or header files that call the APIs.
- 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.
- 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.
Do either of the following:
- Install Android Studio version 4.0 or higher. Use the SDK Manager UI to install Android SDK Platform version 10.0 (API level 29).
- Install the Android SDK command-line tools
and use
sdkmanager
to install Android SDK Platform version 10.0 (API level 29).
Prepare Android Studio for native development by using the SDK Manager to install the latest CMake and Android Native Development Kit (NDK). For more information on creating or importing native projects, see Getting Started with the NDK.
Download the zip file and extract it alongside your project.
Download Link Size SHA-256 Checksum 37.8 MiB 9db60185185342f28d2c278b60222333608c67bc022e458a25224eaea8c4c4b7 Update your app’s
build.gradle
file as shown below: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.3.0' 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.3.0") 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")) ... }
Update your app’s
CMakeLists.txt
files as shown below: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 ...)
Data Collection
The Play Core Native SDK may collect version related data to allow Google to improve the product, including:
- App’s package name
- App’s package version
- Play Core Native SDK's version
This data will be collected when you upload your app package
to the Play Console. To opt-out of this data collection process, remove the
$playcoreDir/playcore-native-metadata.jar
import in the build.gradle file.
Note, this data collection related to your use of the Play Core Native SDK and Google’s use of the collected data is separate and independent of Google’s collection of library dependencies declared in Gradle when you upload your app package to the Play Console.
After you integrate the Play Core Native SDK into your project, include the following line in files that contain API calls:
#include "play/app_update.h"
Initialize the in-app update API
Whenever you use the in-app update API, initialize it first by calling the
AppUpdateManager_init()
function, as shown in the following example built with
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.
}
}
Check for update availability
Before you request an update, check if there is an update available for your
app.
AppUpdateManager_requestInfo()
starts an asynchronous request that gathers the required information to launch
the in-app update flow later. The function returns APP_UPDATE_NO_ERROR
if the
request starts successfully.
AppUpdateErrorCode error_code = AppUpdateManager_requestInfo()
if (error_code == APP_UPDATE_NO_ERROR) {
// The request has successfully started, check the result using
// AppUpdateManager_getInfo.
}
You can track the ongoing process and result of the request using
AppUpdateManager_getInfo()
.
In addition to the error code, this function returns an
AppUpdateInfo
opaque struct, which you can use to retrieve information about the update
request. For example, you might want to call this function in every game loop
until it returns a non-null result for 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
}
...
}
Check update staleness
In addition to checking whether an update is available, you might also want to check how much time has passed since the user was last notified of an update through the Play Store. This can help you decide whether you should initiate a flexible update or an immediate update. For example, you might wait a few days before notifying the user with a flexible update, and a few days after that before requiring an immediate update.
Use
AppUpdateInfo_getClientVersionStalenessDays()
to check the number of days since the update became available through the Play
Store:
int32_t staleness_days = AppUpdateInfo_getClientVersionStalenessDays(info);
Check update priority
The Google Play Developer API allows you to set the priority of each update. This allows your app to decide how strongly to recommend an update to the user. For example, consider the following strategy for setting update priority:
- Minor UI improvements: Low-priority update; request neither a flexible update nor an immediate update. Update only when the user isn't interacting with your app.
- Performance improvements: Medium-priority update; request a flexible update.
- Critical security update: High-priority update; request an immediate update.
To determine priority, Google Play uses an integer value between 0 and 5, with 0
being the default and 5 being the highest priority. To set the priority for an
update, use the inAppUpdatePriority
field under
Edits.tracks.releases
in the Google Play Developer API. All newly-added versions in the release are
considered to be the same priority as the release. Priority can only be set when
rolling out a new release and cannot be changed later.
Set the priority using the Google Play Developer API, as described in the Play
Developer API
documentation.
Specify in-app update priority in the
Edit.tracks
resource passed in the
Edit.tracks: update
method. The following example demonstrates releasing an app with version code
88 and inAppUpdatePriority
5:
{ "releases": [{ "versionCodes": ["88"], "inAppUpdatePriority": 5, "status": "completed" }] }
In your app's code, you can check the priority level for a given update using
AppUpdateInfo_getPriority()
:
int32_t priority = AppUpdateInfo_getPriority(info);
Start an update
After you confirm that an update is available, you can request an update using
AppUpdateManager_requestStartUpdate()
.
Before you request an update, get an up-to-date AppUpdateInfo
object and
create an
AppUpdateOptions
object to configure the update flow. An AppUpdateOptions
object defines
options for an in-app update flow, including whether the update should be
flexible or immediate.
The following example creates an AppUpdateOptions
object for a flexible update
flow:
// Creates an AppUpdateOptions configuring a flexible in-app update flow.
AppUpdateOptions* options;
AppUpdateErrorCode error_code = AppUpdateOptions_createOptions(APP_UPDATE_TYPE_FLEXIBLE, &options);
The following example creates an AppUpdateOptions
object for an immediate
update flow:
// Creates an AppUpdateOptions configuring an immediate in-app update flow.
AppUpdateOptions* options;
AppUpdateErrorCode error_code = AppUpdateOptions_createOptions(APP_UPDATE_TYPE_IMMEDIATE, &options);
The AppUpdateOptions
object also contains an AllowAssetPackDeletion
field
that defines whether the update is allowed to clear asset
packs in case of limited device storage. This
field is set to false
by default, but you can use the
AppUpdateOptions_setAssetPackDeletionAllowed()
method to set it to true
instead:
bool allow = true;
AppUpdateErrorCode error_code = AppUpdateOptions_setAssetPackDeletionAllowed(options, allow);
After you have an up-to-date AppUpdateInfo
object and a properly-configured
AppUpdateOptions
object, call AppUpdateManager_requestStartUpdate()
to
asynchronously request an update flow, passing in an Android Activity jobject
for the final parameter.
AppUpdateErrorCode request_error_code =
AppUpdateManager_requestStartUpdate(info, options, app->activity->clazz);
To free up resources, release instances of AppUpdateInfo
and
AppUpdateOptions
that you no longer need by calling
AppUpdateInfo_destroy()
and
AppUpdateOptions_destroy()
,
respectively.
AppUpdateInfo_destroy(info);
AppUpdateOptions_destroy(options);
For an immediate update flow, Google Play displays a user confirmation page. When the user accepts the request, Google Play automatically downloads and installs the update in the foreground, then restarts the app to the updated version if installation is successful.
For a flexible update flow, you can keep requesting up-to-date AppUpdateInfo
objects to keep track of the current update status while the user continues to
interact with the app. After the download finishes successfully, you must
trigger the completion of the update by calling
AppUpdateManager_requestCompleteUpdate()
,
as shown in the following example:
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.
}
}
Free up resources by calling the
AppUpdateManager_destroy()
function after your app has finished using the API.
Error handling
This section describes solutions for common errors indicated by specific
AppUpdateErrorCode
values:
- An error code of
-110, APP_UPDATE_INITIALIZATION_NEEDED
indicates that the API has not been initialized successfully. CallAppUpdateManager_init()
to initialize the API. - An error code of
-4, APP_UPDATE_INVALID_REQUEST
indicates that some parameters of the update flow request are malformed. Check to make sure that theAppUpdateInfo
andAppUpdateOptions
objects are not null and are correctly formatted. - An error code of
-5, APP_UPDATE_UNAVAILABLE
indicates that there is no applicable update available. Make sure that the target version has the same package name, application ID, and signing key. If there is an update available, clear the app's cache and callAppUpdateManager_requestAppUpdateInfo()
again to refreshAppUpdateInfo
. - An error code of
-6, APP_UPDATE_NOT_ALLOWED
indicates that the update type indicated by theAppUpdateOption
object is not allowed. Check whether theAppUpdateInfo
object indicates that the update type is allowed before starting the update flow.
Next steps
Test your app's in-app updates to verify that your integration is working correctly.