В этом руководстве рассказывается, как реализовать поддержку обновлений в приложении с помощью нативного кода (C или C++). Также есть отдельные руководства для случаев, когда вы используете язык программирования Kotlin или Java, Unity или Unreal Engine.
Обзор Native SDK
Play Core Native SDK входит в семейство Play Core SDK. Native SDK включает заголовочный файл C app_update.h, который упаковывает AppUpdateManager из библиотеки Java Play In-App Update. Этот заголовочный файл позволяет приложению вызывать API для обновлений непосредственно из нативного кода.
Как настроить среду разработки
Скачать Play Core Native SDK
Перед загрузкой вы должны согласиться со следующими условиями.
Условия и положения
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.
Выполните одно из следующих действий:
- Установите Android Studio версии 4.0 или более поздней. Установите Android SDK Platform версии 10.0 (уровень API 29) с помощью SDK Manager.
- Установите инструменты командной строки Android SDK и используйте
sdkmanager, чтобы установить Android SDK Platform версии 10.0 (уровень API 29).
Подготовьте Android Studio к нативной разработке. Для этого с помощью SDK Manager установите последние версии CMake и Android Native Development Kit (NDK). Подробную информацию о создании и импорте нативных проектов можно найти в статье Начало работы с NDK.
Скачайте ZIP-файл и распакуйте его в папку проекта.
Ссылка для скачивания Размер Контрольная сумма SHA-256 54,8 МиБ 008b8fedc6179a6dc6ccc21af75591afc7036f78f3d5559d844f1b923934fef0 Обновите файл
build.gradleприложения, как показано ниже.Яркий
// 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.6.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.6.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 может собирать данные о версии, чтобы мы могли улучшать продукт. К таким данным относятся:
- Название пакета приложения.
- Версия пакета приложения.
- Версия Play Core Native SDK
Эти данные будут собраны, когда вы загрузите пакет приложения в Play Console. Чтобы отказаться от сбора данных, удалите импорт $playcoreDir/playcore-native-metadata.jar в файле build.gradle.
Обратите внимание, что сбор данных, связанный с использованием Play Core Native SDK, и использование собранных данных компанией Google не зависят от сбора данных о зависимостях библиотек, объявленных в Gradle, который выполняется, когда вы загружаете пакет приложения в Play Console.
После интеграции нативного SDK Play Core в проект добавьте следующую строку в файлы, содержащие вызовы 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
}
...
}
Проверка актуальности обновления
Помимо проверки наличия обновления, вы также можете проверить, сколько времени прошло с момента последнего уведомления пользователя об обновлении через Google Play. Это поможет вам решить, какое обновление запустить: гибкое или немедленное. Например, вы можете подождать несколько дней, прежде чем уведомить пользователя о гибком обновлении, и ещё несколько дней, прежде чем потребовать немедленного обновления.
Используйте AppUpdateInfo_getClientVersionStalenessDays(), чтобы проверить, сколько дней прошло с момента, когда обновление стало доступно в Google Play.
int32_t staleness_days = AppUpdateInfo_getClientVersionStalenessDays(info);
Проверить приоритет обновления
Google Play Developer API позволяет задавать приоритет для каждого обновления. Это позволяет приложению решить, насколько настойчиво рекомендовать пользователю обновление. Например, можно использовать следующую стратегию:
- Незначительные улучшения интерфейса: низкий приоритет. Не запрашивайте ни гибкое, ни срочное обновление. Обновляйте виджет, только когда пользователь не взаимодействует с приложением.
- Улучшение производительности. Средний приоритет. Запросите гибкое обновление.
- Критическое обновление системы безопасности – высокий приоритет, требуется немедленное обновление.
Для определения приоритета Google Play использует целое число от 0 до 5, где 0 – значение по умолчанию, а 5 – наивысший приоритет. Чтобы задать приоритет обновления, используйте поле inAppUpdatePriority в разделе Edits.tracks.releases в Google Play Developer API. Все новые версии в выпуске имеют тот же приоритет, что и выпуск. Приоритет можно задать только при выпуске новой версии, и изменить его позже нельзя.
Задайте приоритет с помощью Google Play Developer API, как описано в документации по Play Developer 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(), чтобы асинхронно запросить процесс обновления. В качестве последнего параметра передайте объект jobject.
AppUpdateErrorCode request_error_code =
AppUpdateManager_requestStartUpdate(info, options, app->activity->clazz);
Чтобы освободить ресурсы, удалите ненужные экземпляры AppUpdateInfo и AppUpdateOptions, вызвав AppUpdateInfo_destroy() и AppUpdateOptions_destroy() соответственно.
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.
}
}
Освободите ресурсы, вызвав функцию AppUpdateManager_destroy() после того, как приложение перестанет использовать API.
Обработка ошибок
В этом разделе описаны решения распространенных ошибок, на которые указывают определенные значения AppUpdateErrorCode.
- Код ошибки
-110, APP_UPDATE_INITIALIZATION_NEEDEDозначает, что API не был инициализирован. ВызовитеAppUpdateManager_init(), чтобы инициализировать API. - Код ошибки
-4, APP_UPDATE_INVALID_REQUESTуказывает на то, что некоторые параметры запроса на обновление потока имеют неверный формат. Убедитесь, что объектыAppUpdateInfoиAppUpdateOptionsне имеют нулевого значения и правильно отформатированы. - Код ошибки
-5, APP_UPDATE_UNAVAILABLEозначает, что подходящее обновление недоступно. Убедитесь, что у целевой версии те же название пакета, идентификатор приложения и ключ подписи. Если обновление доступно, очистите кеш приложения и снова вызовитеAppUpdateManager_requestAppUpdateInfo(), чтобы обновитьAppUpdateInfo. - Код ошибки
-6, APP_UPDATE_NOT_ALLOWEDозначает, что тип обновления, указанный объектомAppUpdateOption, не разрешен. Прежде чем запускать обновление, проверьте, разрешен ли его тип в объектеAppUpdateInfo.
Дальнейшие действия
Протестируйте обновления приложения, чтобы убедиться, что интеграция работает правильно.