Supportare gli aggiornamenti in-app (nativi)

Questa guida descrive come supportare gli aggiornamenti in-app nella tua app utilizzando il codice nativo (C o C++). Sono disponibili guide separate per i casi in cui la tua implementazione utilizza il linguaggio di programmazione Kotlin o il linguaggio di programmazione Java e per quelli in cui l'implementazione usa Unity.

Panoramica dell'SDK nativo

L'SDK nativo Core di Play fa parte della famiglia di SDK Play Core. L'SDK nativo include un file di intestazione C, app_update.h, che esegue il wrapping di AppUpdateManager dalla libreria di aggiornamenti in-app di Java Play. Questo file di intestazione consente all'app di chiamare l'API per gli aggiornamenti in-app direttamente dal tuo codice nativo.

Configura l'ambiente di sviluppo

Scarica Play Core Native SDK

Prima di eseguire il download, devi accettare i seguenti termini e condizioni.

Termini e condizioni

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

play-core-native-sdk-1.14.0.zip

  1. Procedi in uno dei seguenti modi:

  2. Prepara Android Studio per lo sviluppo nativo usando SDK Manager per installare la versione più recente di CMake e del Native Development Kit (NDK) per Android. Per ulteriori informazioni sulla creazione o sull'importazione di progetti nativi, consulta la Guida introduttiva all'NDK.

  3. Scarica il file ZIP ed estrailo insieme al tuo progetto.

    Link di download Dimensioni Checksum SHA-256
    36 MiB 782a8522d937848c83a715c9a258b95a3ff2879a7cd71855d137b41c00786a5e
  4. Aggiorna il file build.gradle dell'app come mostrato di seguito:

    trendy

        // 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. Aggiorna i file CMakeLists.txt dell'app come mostrato di seguito:

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

Raccolta dei dati

L'SDK nativo della libreria di base Play potrebbe raccogliere dati relativi alla versione per consentire a Google di migliorare il prodotto, tra cui:

  • Nome del pacchetto dell'app
  • Versione del pacchetto dell'app
  • Versione Play Core Native SDK

Questi dati verranno raccolti quando caricherai il pacchetto dell'app in Play Console. Per disattivare questo processo di raccolta dei dati, rimuovi l'importazione $playcoreDir/playcore-native-metadata.jar nel file build.gradle.

Tieni presente che questa raccolta di dati relativa all'utilizzo dell'SDK nativo di Play Core e all'uso dei dati raccolti da parte di Google è separata e indipendente dalla raccolta di dipendenze di libreria dichiarate in Gradle quando carichi il pacchetto di app in Play Console.

Dopo aver integrato l'SDK nativo della libreria di base Play nel progetto, includi la seguente riga nei file che contengono chiamate API:

#include "play/app_update.h"

Inizializzare l'API di aggiornamento in-app

Ogni volta che utilizzi l'API di aggiornamento in-app, inizializzala chiamando la funzione AppUpdateManager_init(), come mostrato nell'esempio seguente, creato 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.
  }
}

Verifica la disponibilità di aggiornamenti

Prima di richiedere un aggiornamento, controlla se è disponibile un aggiornamento per la tua app. AppUpdateManager_requestInfo() avvia una richiesta asincrona che raccoglie le informazioni richieste per avviare il flusso di aggiornamento in-app in un secondo momento. La funzione restituisce APP_UPDATE_NO_ERROR se la richiesta viene avviata correttamente.

AppUpdateErrorCode error_code = AppUpdateManager_requestInfo()

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

Puoi monitorare il processo in corso e il risultato della richiesta utilizzando AppUpdateManager_getInfo(). Oltre al codice di errore, questa funzione restituisce uno struct opaco AppUpdateInfo, che puoi utilizzare per recuperare le informazioni sulla richiesta di aggiornamento. Ad esempio, potresti voler chiamare questa funzione in ogni ciclo di gioco finché non restituisce un risultato non null per 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
   }
...
}

Controlla il mancato aggiornamento degli aggiornamenti

Oltre a verificare se è disponibile un aggiornamento, potresti anche controllare quanto tempo è trascorso dall'ultima notifica all'utente tramite il Play Store. Questo può aiutarti a decidere se avviare un aggiornamento flessibile o un aggiornamento immediato. Ad esempio, potresti attendere alcuni giorni prima di inviare una notifica all'utente con un aggiornamento flessibile e alcuni giorni dopo prima di richiedere un aggiornamento immediato.

Utilizza AppUpdateInfo_getClientVersionStalenessDays() per controllare il numero di giorni da quando l'aggiornamento è stato reso disponibile tramite il Play Store:

int32_t staleness_days = AppUpdateInfo_getClientVersionStalenessDays(info);

Controlla la priorità degli aggiornamenti

L'API Google Play Developer ti consente di impostare la priorità di ogni aggiornamento. Ciò consente alla tua app di decidere in che misura consigliare un aggiornamento all'utente. Ad esempio, considera la seguente strategia per l'impostazione della priorità di aggiornamento:

  • Miglioramenti di minore entità all'interfaccia utente: aggiornamento a bassa priorità; non richiedere un aggiornamento flessibile né un aggiornamento immediato. Aggiornali solo quando l'utente non interagisce con l'app.
  • Miglioramenti delle prestazioni: aggiornamento con priorità media; richiedi un aggiornamento flessibile.
  • Aggiornamento per la sicurezza critico: aggiornamento con priorità elevata; richiedi un aggiornamento immediato.

Per determinare la priorità, Google Play utilizza un valore intero compreso tra 0 e 5, dove 0 è il valore predefinito e 5 è la priorità più alta. Per impostare la priorità di un aggiornamento, usa il campo inAppUpdatePriority in Edits.tracks.releases nell'API Google Play Developer. Tutte le nuove versioni aggiunte nella release vengono considerate con la stessa priorità della release. La priorità può essere impostata solo durante l'implementazione di una nuova release e non può essere modificata in un secondo momento.

Imposta la priorità utilizzando l'API Google Play Developer, come descritto nella documentazione relativa all'API Play Developer. Specifica la priorità di aggiornamento in-app nella risorsa Edit.tracks trasmessa nel metodo Edit.tracks: update. L'esempio seguente illustra il rilascio di un'app con il codice di versione 88 e inAppUpdatePriority 5:

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

Nel codice della tua app, puoi controllare il livello di priorità per un determinato aggiornamento utilizzando AppUpdateInfo_getPriority():

int32_t priority = AppUpdateInfo_getPriority(info);

Avvia un aggiornamento

Dopo aver confermato che è disponibile un aggiornamento, puoi richiederlo utilizzando AppUpdateManager_requestStartUpdate(). Prima di richiedere un aggiornamento, recupera un oggetto AppUpdateInfo aggiornato e crea un oggetto AppUpdateOptions per configurare il flusso di aggiornamento. Un oggetto AppUpdateOptions definisce le opzioni per un flusso di aggiornamento in-app, ad esempio se l'aggiornamento deve essere flessibile o immediato.

L'esempio seguente crea un oggetto AppUpdateOptions per un flusso di aggiornamento flessibile:

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

L'esempio seguente crea un oggetto AppUpdateOptions per un flusso di aggiornamento immediato:

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

L'oggetto AppUpdateOptions contiene anche un campo AllowAssetPackDeletion che definisce se l'aggiornamento può cancellare i pacchetti di asset in caso di spazio di archiviazione limitato del dispositivo. Questo campo è impostato su false per impostazione predefinita, ma puoi utilizzare il metodo AppUpdateOptions_setAssetPackDeletionAllowed() per impostarlo su true:

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

Quando hai un oggetto AppUpdateInfo aggiornato e un oggetto AppUpdateOptions configurato correttamente, chiama AppUpdateManager_requestStartUpdate() per richiedere in modo asincrono un flusso di aggiornamento, passando un'attività Android jobject per il parametro finale.

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

Per liberare risorse, rilascia le istanze di AppUpdateInfo e AppUpdateOptions che non ti servono più chiamando rispettivamente AppUpdateInfo_destroy() e AppUpdateOptions_destroy().

AppUpdateInfo_destroy(info);
AppUpdateOptions_destroy(options);

Per un flusso di aggiornamento immediato, Google Play mostra una pagina di conferma per l'utente. Quando l'utente accetta la richiesta, Google Play scarica e installa automaticamente l'aggiornamento in primo piano e, se l'installazione va a buon fine, riavvia l'app alla versione aggiornata.

Per un flusso di aggiornamento flessibile, puoi continuare a richiedere oggetti AppUpdateInfo aggiornati per tenere traccia dello stato di aggiornamento attuale mentre l'utente continua a interagire con l'app. Al termine del download, devi attivare il completamento dell'aggiornamento chiamando AppUpdateManager_requestCompleteUpdate(), come mostrato nell'esempio seguente:

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

Libera risorse chiamando la funzione AppUpdateManager_destroy() dopo che l'app ha finito di utilizzare l'API.

Gestione degli errori

Questa sezione descrive le soluzioni per gli errori comuni indicati da valori specifici di AppUpdateErrorCode:

  • Un codice di errore -110, APP_UPDATE_INITIALIZATION_NEEDED indica che l'API non è stata inizializzata correttamente. Chiama AppUpdateManager_init() per inizializzare l'API.
  • Un codice di errore -4, APP_UPDATE_INVALID_REQUEST indica che alcuni parametri della richiesta del flusso di aggiornamento non sono corretti. Verifica che gli oggetti AppUpdateInfo e AppUpdateOptions non siano nulli e che siano formattati correttamente.
  • Un codice di errore -5, APP_UPDATE_UNAVAILABLE indica che non è disponibile alcun aggiornamento applicabile. Assicurati che la versione di destinazione abbia lo stesso nome del pacchetto, lo stesso ID applicazione e la stessa chiave di firma. Se è disponibile un aggiornamento, svuota la cache dell'app e chiama di nuovo AppUpdateManager_requestAppUpdateInfo() per aggiornare AppUpdateInfo.
  • Un codice di errore -6, APP_UPDATE_NOT_ALLOWED indica che il tipo di aggiornamento indicato dall'oggetto AppUpdateOption non è consentito. Controlla se l'oggetto AppUpdateInfo indica che il tipo di aggiornamento è consentito prima di iniziare il flusso di aggiornamento.

Passaggi successivi

Testa gli aggiornamenti in-app dell'app per verificare che l'integrazione funzioni correttamente.