Compatibilidade com 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 de Play Core Native SDK

Antes de fazer o download, é necessário concordar com os Termos e Condições a seguir.

Termos e Condições

Última modificação: 24 de setembro de 2020
  1. Ao usar o kit de desenvolvimento de software da Play Core, você concorda com estes termos, além dos Termos de Serviço das APIs do Google ("TOS da API"). Se eles entrarem em conflito, estes termos terão prioridade em relação aos TOS da API. Leia estes termos e os TOS da API com atenção.
  2. Para os fins destes termos, "APIs" significa as APIs do Google, outros serviços para desenvolvedores e os softwares associados, incluindo códigos redistribuíveis.
  3. Um "código redistribuível" é um arquivo principal ou código de objeto fornecido pelo Google que chama as APIs.
  4. Sujeito a estes termos e aos TOS da API, você pode copiar e distribuir códigos redistribuíveis exclusivamente para inclusão como parte do seu cliente da API. O Google e nossos licenciantes são proprietários de todos os direitos, títulos e participações, incluindo toda e qualquer propriedade intelectual e outros direitos de propriedade relacionados aos códigos redistribuíveis. Não é permitido modificar, traduzir ou criar obras derivadas de códigos redistribuíveis.
  5. O Google pode fazer alterações nestes termos a qualquer momento, mediante aviso prévio, incluindo a possibilidade de se recusar a continuar usando o kit de desenvolvimento de software da Play Core. O Google publicará avisos de modificações nos termos em https://developer.android.com/guide/playcore/license. As mudanças não são retroativas.

Fazer o download de Play Core Native SDK

Fazer o download de Play Core Native SDK

play-core-native-sdk-1.11.0.zip

  1. Realize uma das ações abaixo:

  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 Primeiros passos com o NDK.

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

    Link de download Tamanho Soma de verificação SHA-256
    55,6 MB 058b4069f09714da938656d43b6dc28d3bc6f821c9d406e9c96a1c3af014dc45
  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/per-feature-proguard-files"
                    ...
                }
                debug {
                    ...
                }
            }
            externalNativeBuild {
                cmake {
                    path 'src/main/CMakeLists.txt'
                }
            }
        }
    
        dependencies {
            // Use the Play Core AAR included with the SDK.
            implementation files("$playcoreDir/playcore.aar")
    
            // Use the following dependency for the Play Integrity API.
            implementation("com.google.android.play:integrity:1.0.0")
            ...
        }
        

    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/per-feature-proguard-files")
                ...
            }
            debug {
                ...
            }
        }
        externalNativeBuild {
            cmake {
                path = "src/main/CMakeLists.txt"
            }
        }
    }
    
    dependencies {
        // Use the Play Core AAR included with the SDK.
        implementation(files("$playcoreDir/playcore.aar"))
        ...
    }
    
  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
            ...)
    

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 desempenho: atualização de prioridade média. Exigem uma atualização flexível.
  • Atualização crítica de segurança: atualização de alta prioridade. Exige uma atualização 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.

A seguir

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