Suporte para atualizações no app (nativo)

Este guia descreve como oferecer compatibilidade com atualizações no app usando código nativo (C ou C++). Há guias separados para casos em que sua implementação usa a linguagem de programação Kotlin ou Java e outros em que ela usa o Unity.

Visão geral do SDK nativo

O SDK nativo da Play Core faz parte da família do SDK da Play Core. O SDK nativo inclui um arquivo principal C, app_update.h, que envolve o AppUpdateManager da biblioteca Java Play In-App Update. Esse arquivo principal permite que o app chame a API para atualizações no app diretamente pelo código nativo.

Configurar seu ambiente de desenvolvimento

Fazer o download Play Core Native SDK

Antes de fazer o download, você precisa concordar com os seguintes Termos e Condições.

Termos e Condições

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

play-core-native-sdk-1.13.0.zip

  1. Realize uma das seguintes ações:

  2. Prepare o Android Studio para o desenvolvimento nativo usando o SDK Manager para instalar o CMake e o Android Native Development Kit (NDK) mais recentes. Para saber mais sobre como criar ou importar projetos nativos, consulte Começar a usar o NDK.

  3. Faça o download do arquivo ZIP e extraia ele junto com o projeto.

    Link de download Tamanho Checksum SHA-256
    35,6 MiB 4eee8aafbe0309c0b4ba377c7c7bc1986c73ae70dd7ce3a04f792e1a67d79d51
  4. Atualize o arquivo build.gradle do app como mostrado abaixo:

    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. Atualize os arquivos CMakeLists.txt do app como mostrado abaixo:

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

Coleta de dados

O SDK nativo da Play Core pode coletar dados relacionados à versão para que o Google melhore o produto, incluindo:

  • Nome do pacote do app
  • Versão do pacote do app
  • Versão do SDK nativo da Play Core

Esses dados vão ser coletados quando você fizer upload do seu pacote de apps para o Play Console. Para desativar esse processo de coleta de dados, remova a importação de $playcoreDir/playcore-native-metadata.jar no arquivo build.gradle.

Essa coleta de dados relacionada ao seu uso do SDK nativo da Play Core e o uso do Google dos dados coletados são independentes e não estão relacionados à coleta de dependências de biblioteca do Google declaradas no Gradle quando você faz upload do pacote do app para o Play Console.

Depois de integrar o SDK nativo da Play Core ao projeto, inclua a linha abaixo nos arquivos que contêm chamadas de API:

#include "play/app_update.h"

Inicializar a API de atualização no app

Sempre que você usar a API de atualização no app, inicialize-a primeiro chamando a função AppUpdateManager_init(), conforme mostrado no exemplo a seguir criado com 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.
  }
}

Conferir se há atualizações disponíveis

Antes de solicitar uma atualização, confira se há uma disponível para seu app. A função AppUpdateManager_requestInfo() inicia uma solicitação assíncrona que coleta as informações necessárias para iniciar o fluxo de atualização no app posteriormente. Ela retornará APP_UPDATE_NO_ERROR se a solicitação for iniciada com êxito.

AppUpdateErrorCode error_code = AppUpdateManager_requestInfo()

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

Você pode rastrear o processo contínuo e o resultado da solicitação usando a AppUpdateManager_getInfo(). Além do código de erro, essa função retorna uma estrutura AppUpdateInfo opaca, que pode ser usada para recuperar informações sobre a solicitação de atualização Por exemplo, você pode querer chamar essa função em cada loop de jogo até que ela retorne um resultado não nulo para 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
   }
...
}

Conferir inatividade de atualização

Além de conferir se uma atualização está disponível, também é possível saber quanto tempo se passou desde que o usuário foi notificado pela última vez sobre uma atualização pela Play Store. Isso pode ajudar você a decidir se deve iniciar uma atualização flexível ou uma imediata. Por exemplo, você pode esperar alguns dias antes de notificar o usuário sobre uma atualização flexível e mais alguns antes de exigir uma imediata.

Use AppUpdateInfo_getClientVersionStalenessDays() para verificar o número de dias desde que a atualização foi disponibilizada na Play Store:

int32_t staleness_days = AppUpdateInfo_getClientVersionStalenessDays(info);

Conferir prioridade de atualização

A API Google Play Developer permite que você defina a prioridade de cada atualização. Isso permite que o app decida como recomendar uma atualização para o usuário. Por exemplo, considere a seguinte estratégia para definir a prioridade de atualização:

  • Pequenas melhorias na IU: atualização de baixa prioridade. Não exigem a atualização flexível nem a imediata. Só atualize quando o usuário não estiver interagindo com o app.
  • Melhorias de performance: atualização de prioridade média. Exigem uma atualização flexível.
  • Atualização crítica de segurança: é de alta prioridade e precisa ser feita de forma imediata.

Para determinar a prioridade, o Google Play usa um valor inteiro entre 0 e 5, sendo 0 o padrão e 5 a prioridade mais alta. Para definir a prioridade de uma atualização, use o campo inAppUpdatePriority em Edits.tracks.releases na API Google Play Developer. Todas as versões recém-adicionadas são consideradas como tendo a mesma prioridade da versão lançada. A prioridade só pode ser definida ao lançar uma nova versão e não pode ser mudada posteriormente.

Defina a prioridade usando a API Google Play Developer, conforme descrito na documentação da API Google Play Developer. Especifique a prioridade de atualização no app no recurso Edit.tracks transmitido no método Edit.tracks: update. O exemplo a seguir demonstra o lançamento de um app com o código de versão 88 e inAppUpdatePriority 5:

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

No código do app, é possível conferir o nível de prioridade de uma determinada atualização usando AppUpdateInfo_getPriority():

int32_t priority = AppUpdateInfo_getPriority(info);

Iniciar uma atualização

Depois de confirmar que há uma atualização disponível, solicite-a usando AppUpdateManager_requestStartUpdate(). Antes de solicitar, providencie um objeto AppUpdateInfo atualizado e crie um objeto AppUpdateOptions para configurar o fluxo de atualização. Um objeto AppUpdateOptions define opções para um fluxo de atualização no app, incluindo se a atualização precisa ser flexível ou imediata.

O exemplo a seguir cria um objeto AppUpdateOptions para um fluxo de atualização flexível:

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

O exemplo a seguir cria um objeto AppUpdateOptions para um fluxo de atualização imediata:

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

O objeto AppUpdateOptions também contém um campo AllowAssetPackDeletion que define se a atualização tem permissão para limpar pacotes de recursos no caso de espaço de armazenamento limitado no dispositivo. Esse campo é definido como false por padrão, mas é possível usar o método AppUpdateOptions_setAssetPackDeletionAllowed() para defini-lo como true:

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

Depois de ter um objeto AppUpdateInfo atualizado e um objeto AppUpdateOptions configurado corretamente, chame AppUpdateManager_requestStartUpdate() para solicitar um fluxo de atualização de forma assíncrona, transmitindo uma atividade jobject do Android como parâmetro final.

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

Para liberar recursos, deixe de lado instâncias de AppUpdateInfo e AppUpdateOptions que não sejam mais necessárias, chamando AppUpdateInfo_destroy() e AppUpdateOptions_destroy(), respectivamente.

AppUpdateInfo_destroy(info);
AppUpdateOptions_destroy(options);

Para um fluxo de atualização imediato, o Google Play exibe uma página de confirmação do usuário. Quando o usuário aceita a solicitação, o Google Play faz automaticamente o download e a instalação da atualização em primeiro plano e, em seguida, reinicia o app para a versão atualizada, caso a instalação seja bem-sucedida.

Para um fluxo de atualização flexível, você pode continuar solicitando objetos AppUpdateInfo atualizados para acompanhar o status de atualização atual enquanto o usuário continua a interagir com o app. Depois que o download terminar, acione a conclusão da atualização, chamando AppUpdateManager_requestCompleteUpdate(), conforme mostrado no exemplo a seguir:

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

Quando seu app terminar de usar a API, libere recursos chamando a função AppUpdateManager_destroy().

Gerenciamento de erros

Esta seção descreve soluções para erros comuns indicados por valores AppUpdateErrorCode específicos:

  • Um código de erro -110, APP_UPDATE_INITIALIZATION_NEEDED indica que a API não foi inicializada corretamente. Chame AppUpdateManager_init() para inicializar a API.
  • Um código de erro -4, APP_UPDATE_INVALID_REQUEST indica que alguns parâmetros da solicitação de fluxo de atualização estão incorretos. Confira se os objetos AppUpdateInfo e AppUpdateOptions não são nulos e se estão formatados corretamente.
  • Um código de erro -5, APP_UPDATE_UNAVAILABLE indica que não há uma atualização aplicável disponível. Confira se a versão de destino tem os mesmos nome de pacote, ID do aplicativo e chave de assinatura. Se houver uma atualização disponível, limpe o cache do app e chame AppUpdateManager_requestAppUpdateInfo() novamente para atualizar o objeto AppUpdateInfo.
  • Um código de erro -6, APP_UPDATE_NOT_ALLOWED aponta que o tipo de atualização indicado pelo objeto AppUpdateOption não é permitido. Confira se o objeto AppUpdateInfo indica que o tipo de atualização é permitido antes de iniciar o fluxo de atualização.

Próximas etapas

Testar as atualizações no app para verificar se a integração está funcionando corretamente.