Compatibilidad con actualizaciones de la app (nativas)

En esta guía, se describe cómo admitir actualizaciones integradas en tu app usando código nativo (C o C++). Existen guías separadas para casos en los que tu implementación usa los lenguajes de programación Kotlin o Java, y casos en los que esta usa Unity.

Descripción general del SDK nativo

El SDK nativo de Play Core forma parte de la familia del SDK de Play Core. El SDK nativo incluye un archivo de encabezado C, app_update.h, que une AppUpdateManager de la biblioteca de actualizaciones integradas en la app de Java. Este archivo de encabezado permite que tu app llame a la API para realizar actualizaciones integradas en la app directamente desde tu código nativo.

Cómo configurar tu entorno de desarrollo

Descargar Play Core Native SDK

Antes de la descarga, debes aceptar los Términos y Condiciones.

Términos y Condiciones

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.
Descargar Play Core Native SDK

play-core-native-sdk-1.14.0.zip

  1. Elige una de las siguientes opciones:

  2. Prepara Android Studio para el desarrollo nativo con SDK Manager para instalar la versión más reciente del kit de desarrollo nativo (NDK) de CMake y Android. Obtén más información para crear o importar proyectos nativos en la sección Cómo comenzar a usar el NDK.

  3. Descarga el archivo ZIP y extráelo junto con tu proyecto.

    Vínculo de descarga Tamaño Suma de comprobación SHA-256
    36 MiB 782a8522d937848c83a715c9a258b95a3ff2879a7cd71855d137b41c00786a5e
  4. Actualiza el archivo build.gradle de tu app como se muestra a continuación:

    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.1'
    
            // 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.1")
    
        // Import these common dependencies.
        implementation("com.google.android.gms:play-services-tasks:18.0.2")
        implementation(files("$playcoreDir/playcore-native-metadata.jar"))
        ...
    }
    
  5. Actualiza los archivos CMakeLists.txt de tu app como se muestra a continuación:

    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
            ...)
    

Recopilación de datos

Para permitir que Google mejore el producto, el SDK nativo de Play Core puede recopilar datos relacionados con la versión como los siguientes:

  • Nombre del paquete de la app
  • Versión del paquete de la app
  • Versión del SDK nativo de Play Core

Estos datos se recopilarán cuando subas el paquete de tu app a Play Console. Para inhabilitar este proceso de recopilación de datos, quita la importación de $playcoreDir/playcore-native-metadata.jar en el archivo build.gradle.

Ten en cuenta que la recopilación de datos relacionada con tu uso del SDK nativo de Play Core y el uso que Google hace de los datos recopilados es independiente de la colección de dependencias de bibliotecas declarada por Google en Gradle cuando subes tu paquete de app a Play Console.

Después de integrar el SDK nativo de Play Core en tu proyecto, incluye la siguiente línea en los archivos que contengan llamadas a la APIs:

#include "play/app_update.h"

Cómo inicializar la API de actualizaciones integradas en la app

Siempre que uses la API de actualizaciones en la app, debes llamar a la función AppUpdateManager_init() para inicializarla, como se muestra en el siguiente ejemplo compilado con 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.
  }
}

Cómo comprobar la disponibilidad de actualizaciones

Antes de solicitar una actualización, verifica si hay alguna disponible para tu app. AppUpdateManager_requestInfo() inicia una solicitud asíncrona que recopila la información necesaria para iniciar el flujo de actualización integrada en la app más adelante. Esta función muestra APP_UPDATE_NO_ERROR si la solicitud se inicia correctamente.

AppUpdateErrorCode error_code = AppUpdateManager_requestInfo()

if (error_code == APP_UPDATE_NO_ERROR) {
    // The request has successfully started, check the result using
    // AppUpdateManager_getInfo.
}

Puedes realizar un seguimiento del proceso en curso y el resultado de la solicitud mediante AppUpdateManager_getInfo(). Además del código de error, esta función muestra una estructura opaca AppUpdateInfo, que puedes usar para recuperar información sobre la solicitud de actualización. Por ejemplo, es posible que desees llamar a esta función en cada bucle de juego hasta que muestre un resultado de info no nulo:

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
   }
...
}

Cómo verificar la obsolescencia de las actualizaciones

Además de comprobar si hay actualizaciones disponibles, te recomendamos que verifiques cuánto tiempo pasó desde que el usuario recibió una notificación sobre una actualización a través de Google Play Store. Esto puede ayudarte a decidir si debes iniciar una actualización flexible o una actualización inmediata. Por ejemplo, podrías esperar unos días antes de notificar al usuario con una actualización flexible y, luego, solicitar una actualización inmediata.

Usa AppUpdateInfo_getClientVersionStalenessDays() para verificar cuántos días pasaron desde que apareció la actualización en Play Store:

int32_t staleness_days = AppUpdateInfo_getClientVersionStalenessDays(info);

Cómo verificar la prioridad de las actualizaciones

La API de Google Play Developer te permite establecer la prioridad de cada actualización. De esta manera, tu app puede determinar la insistencia con la que recomendará una actualización al usuario. Por ejemplo, considera la siguiente estrategia para establecer la prioridad de las actualizaciones:

  • Mejoras menores en la IU: Actualización de baja prioridad. No solicites una actualización flexible ni una actualización inmediata. Actualiza la app solo cuando el usuario no esté interactuando con ella.
  • Mejoras en el rendimiento: Actualización de prioridad media. Solicita una actualización flexible.
  • Actualización crítica de seguridad: actualización de prioridad alta. Requiere una actualización inmediata.

Para determinar la prioridad, Google Play usa un valor entero entre 0 y 5, en el que 0 es la prioridad predeterminada y 5 es la más alta. A fin de establecer la prioridad de una actualización, usa el campo inAppUpdatePriority en Edits.tracks.releases de la API de Google Play Developer. Se considerará que todas las versiones agregadas en la original tendrán establecida la misma prioridad que este última. Solo se puede establecer la prioridad al momento de lanzar una versión nueva; no se puede cambiar más tarde.

Establece la prioridad mediante la API de Google Play Developer como se describe en la documentación de la API de Play Developer. Especifica la prioridad de actualización de la app en el recurso Edit.tracks pasado en el método Edit.tracks: update. En el siguiente ejemplo, se muestra cómo lanzar una app con código de versión 88 y inAppUpdatePriority 5:

{
  "releases": [{
      "versionCodes": ["88"],
      "inAppUpdatePriority": 5,
      "status": "completed"
  }]
}

En el código de tu app, puedes verificar el nivel de prioridad de una actualización determinada con AppUpdateInfo_getPriority():

int32_t priority = AppUpdateInfo_getPriority(info);

Cómo iniciar una actualización

Después de confirmar que hay una actualización disponible, puedes solicitarla con AppUpdateManager_requestStartUpdate(): Antes de hacerlo, obtén un objeto AppUpdateInfo actualizado y crea un objeto AppUpdateOptions para configurar el flujo de la actualización. Un objeto AppUpdateOptions define opciones para un flujo de actualización integrada en la app, que incluye si la actualización debe ser flexible o inmediata.

En el siguiente ejemplo, se crea un objeto AppUpdateOptions para un flujo de actualización flexible:

// Creates an AppUpdateOptions configuring a flexible in-app update flow.
AppUpdateOptions* options;
AppUpdateErrorCode error_code = AppUpdateOptions_createOptions(APP_UPDATE_TYPE_FLEXIBLE, &options);

En el siguiente ejemplo, se crea un objeto AppUpdateOptions para un flujo de actualización inmediato:

// Creates an AppUpdateOptions configuring an immediate in-app update flow.
AppUpdateOptions* options;
AppUpdateErrorCode error_code = AppUpdateOptions_createOptions(APP_UPDATE_TYPE_IMMEDIATE, &options);

El objeto AppUpdateOptions también contiene un campo AllowAssetPackDeletion que define si la actualización puede borrar paquetes de recursos si el almacenamiento del dispositivo es limitado. Este campo se establece en false de forma predeterminada, pero puedes usar el método AppUpdateOptions_setAssetPackDeletionAllowed() para establecerlo en true en su lugar:

bool allow = true;
AppUpdateErrorCode error_code = AppUpdateOptions_setAssetPackDeletionAllowed(options, allow);

Una vez que tengas un objeto AppUpdateInfo actualizado y un objeto AppUpdateOptions configurado correctamente, llama a AppUpdateManager_requestStartUpdate() a fin de solicitar de forma asíncrona un flujo de actualización y pasa una actividad de Android jobject para el parámetro final.

AppUpdateErrorCode request_error_code =
AppUpdateManager_requestStartUpdate(info, options, app->activity->clazz);

Para despejar recursos, libera instancias de AppUpdateInfo y AppUpdateOptions que ya no necesites mediante llamadas a AppUpdateInfo_destroy() y AppUpdateOptions_destroy(), respectivamente.

AppUpdateInfo_destroy(info);
AppUpdateOptions_destroy(options);

Para obtener un flujo de actualización inmediata, Google Play muestra una página de confirmación del usuario. Cuando el usuario acepta la solicitud, Google Play descarga e instala automáticamente la actualización en primer plano y, luego, reinicia la app a la versión actualizada si la instalación se realizó correctamente.

Para obtener un flujo de actualización flexible, puedes seguir solicitando objetos AppUpdateInfo actualizados y hacer un seguimiento del estado actual de la actualización mientras el usuario interactúa con la app. Una vez que la descarga finalice correctamente, debes llamar a AppUpdateManager_requestCompleteUpdate() y activar la finalización, como se muestra en el siguiente ejemplo:

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.
    }
}

Llama a la función AppUpdateManager_destroy() una vez que tu app haya terminado de usar la API a fin de liberar recursos.

Administración de errores

En esta sección, se detallan soluciones de errores comunes indicados con valores específicos de AppUpdateErrorCode:

  • Un código de error de -110, APP_UPDATE_INITIALIZATION_NEEDED indica que la API no se inicializó correctamente. Llama a AppUpdateManager_init() para inicializarla.
  • Un código de error de -4, APP_UPDATE_INVALID_REQUEST indica que algunos parámetros de la solicitud de flujo de actualización presentan errores de formato. Comprueba que los objetos AppUpdateInfo y AppUpdateOptions no sean nulos y tengan el formato correcto.
  • Un código de error de -5, APP_UPDATE_UNAVAILABLE indica que no hay una actualización aplicable disponible. Asegúrate de que la versión de destino tenga el mismo nombre de paquete, ID de aplicación y clave de firma. Si hay una actualización disponible, borra la caché de la app y vuelve a llamar a AppUpdateManager_requestAppUpdateInfo() para actualizar AppUpdateInfo.
  • Un código de error de -6, APP_UPDATE_NOT_ALLOWED indica que no se permite el tipo de actualización indicado por el objeto AppUpdateOption. Comprueba que el objeto AppUpdateInfo indique que el tipo de actualización está permitido antes de iniciar el flujo correspondiente.

Próximos pasos

Prueba las actualizaciones integradas en la app a fin de verificar que la integración funcione correctamente.