Obsługa aktualizacji w aplikacji (Kotlin lub Java)

Z tego przewodnika dowiesz się, jak obsługiwać aktualizacje w aplikacji w aplikacji w Kotlinie lub Javie. Osobne przewodniki są dostępne w przypadkach, gdy implementacja korzysta z kodu natywnego (C/C++) oraz gdy implementacja korzysta z Unity lub Unreal Engine.

Konfigurowanie środowiska programistycznego

Biblioteka aktualizacji w aplikacji w Google Play jest częścią podstawowych bibliotek Google Play. Aby zintegrować bibliotekę aktualizacji w aplikacji w Google Play, dodaj te zależności Gradle.

Dynamiczny

// In your app's build.gradle file:
...
dependencies {
    // This dependency is downloaded from the Google's Maven repository.
    // So, make sure you also include that repository in your project's build.gradle file.
    implementation 'com.google.android.play:app-update:2.1.0'

    // For Kotlin users also add the Kotlin extensions library for Play In-App Update:
    implementation 'com.google.android.play:app-update-ktx:2.1.0'
    ...
}

Kotlin

// In your app's build.gradle.kts file:
...
dependencies {
    // This dependency is downloaded from the Google's Maven repository.
    // So, make sure you also include that repository in your project's build.gradle file.
    implementation("com.google.android.play:app-update:2.1.0")

    // For Kotlin users also import the Kotlin extensions library for Play In-App Update:
    implementation("com.google.android.play:app-update-ktx:2.1.0")
    ...
}

Sprawdzanie dostępności aktualizacji

Zanim poprosisz o aktualizację, sprawdź, czy jest ona dostępna dla Twojej aplikacji. Aby to zrobić, użyj AppUpdateManager:

Kotlin

val appUpdateManager = AppUpdateManagerFactory.create(context)

// Returns an intent object that you use to check for an update.
val appUpdateInfoTask = appUpdateManager.appUpdateInfo

// Checks that the platform will allow the specified type of update.
appUpdateInfoTask.addOnSuccessListener { appUpdateInfo ->
    if (appUpdateInfo.updateAvailability() == UpdateAvailability.UPDATE_AVAILABLE
        // This example applies an immediate update. To apply a flexible update
        // instead, pass in AppUpdateType.FLEXIBLE
        && appUpdateInfo.isUpdateTypeAllowed(AppUpdateType.IMMEDIATE)
    ) {
        // Request the update.
    }
}

Java

AppUpdateManager appUpdateManager = AppUpdateManagerFactory.create(context);

// Returns an intent object that you use to check for an update.
Task<AppUpdateInfo> appUpdateInfoTask = appUpdateManager.getAppUpdateInfo();

// Checks that the platform will allow the specified type of update.
appUpdateInfoTask.addOnSuccessListener(appUpdateInfo -> {
    if (appUpdateInfo.updateAvailability() == UpdateAvailability.UPDATE_AVAILABLE
          // This example applies an immediate update. To apply a flexible update
          // instead, pass in AppUpdateType.FLEXIBLE
          && appUpdateInfo.isUpdateTypeAllowed(AppUpdateType.IMMEDIATE)) {
              // Request the update.
    }
});

Zwrócona AppUpdateInfo instancja zawiera stan dostępności aktualizacji. W zależności od stanu aktualizacji instancja zawiera też te informacje:

  • Jeśli aktualizacja jest dostępna i dozwolona, instancja zawiera też intencję rozpoczęcia aktualizacji.
  • Jeśli aktualizacja w aplikacji jest już w toku, instancja zgłasza też jej stan.

Sprawdzanie, czy aktualizacja jest przestarzała

Oprócz sprawdzania, czy aktualizacja jest dostępna, możesz też sprawdzić, ile czasu minęło od ostatniego powiadomienia użytkownika o aktualizacji w Sklepie Play. Pomoże Ci to zdecydować, czy rozpocząć aktualizację elastyczną czy natychmiastową. Możesz na przykład poczekać kilka dni, zanim powiadomisz użytkownika o aktualizacji elastycznej, a potem kilka dni, zanim poprosisz o aktualizację natychmiastową.

Aby sprawdzić, ile dni minęło od udostępnienia aktualizacji w Sklepie Play, użyj clientVersionStalenessDays() do sprawdzenia liczby dni od udostępnienia aktualizacji w Sklepie Play:

Kotlin

val appUpdateManager = AppUpdateManagerFactory.create(context)

// Returns an intent object that you use to check for an update.
val appUpdateInfoTask = appUpdateManager.appUpdateInfo

// Checks whether the platform allows the specified type of update,
// and current version staleness.
appUpdateInfoTask.addOnSuccessListener { appUpdateInfo ->
    if (appUpdateInfo.updateAvailability() == UpdateAvailability.UPDATE_AVAILABLE
          && (appUpdateInfo.clientVersionStalenessDays() ?: -1) >= DAYS_FOR_FLEXIBLE_UPDATE
          && appUpdateInfo.isUpdateTypeAllowed(AppUpdateType.FLEXIBLE)) {
              // Request the update.
    }
}

Java

AppUpdateManager appUpdateManager = AppUpdateManagerFactory.create(context);

// Returns an intent object that you use to check for an update.
Task<AppUpdateInfo> appUpdateInfoTask = appUpdateManager.getAppUpdateInfo();

// Checks whether the platform allows the specified type of update,
// and current version staleness.
appUpdateInfoTask.addOnSuccessListener(appUpdateInfo -> {
    if (appUpdateInfo.updateAvailability() == UpdateAvailability.UPDATE_AVAILABLE
          && appUpdateInfo.clientVersionStalenessDays() != null
          && appUpdateInfo.clientVersionStalenessDays() >= DAYS_FOR_FLEXIBLE_UPDATE
          && appUpdateInfo.isUpdateTypeAllowed(AppUpdateType.FLEXIBLE)) {
              // Request the update.
    }
});

Sprawdzanie priorytetu aktualizacji

Interfejs Google Play Developer API umożliwia ustawienie priorytetu każdej aktualizacji. Dzięki temu Twoja aplikacja może zdecydować, jak mocno zalecać użytkownikowi aktualizację. Rozważ na przykład tę strategię ustawiania priorytetu aktualizacji:

  • Drobne ulepszenia interfejsu: aktualizacja o niskim priorytecie ; nie proś o aktualizację elastyczną ani natychmiastową. Aktualizuj tylko wtedy, gdy użytkownik nie korzysta z Twojej aplikacji.
  • Ulepszenia wydajności: aktualizacja o średnim priorytecie ; poproś o aktualizację elastyczną.
  • Krytyczna aktualizacja zabezpieczeń: aktualizacja o wysokim priorytecie ; poproś o aktualizację natychmiastową.

Aby określić priorytet, Google Play używa liczby całkowitej od 0 do 5, gdzie 0 to wartość domyślna, a 5 to najwyższy priorytet. Aby ustawić priorytet aktualizacji, użyj pola inAppUpdatePriority w sekcji Edits.tracks.releases w interfejsie Google Play Developer API. Wszystkie nowo dodane wersje w wersji są traktowane jako mające taki sam priorytet jak wersja. Priorytet można ustawić tylko podczas wdrażania nowej wersji i nie można go później zmienić.

Ustaw priorytet za pomocą interfejsu Google Play Developer API zgodnie z opisem w dokumentacji interfejsu Play Developer API. Priorytet aktualizacji w aplikacji należy określić w zasobie Edit.tracks przekazanym w metodzie Edit.tracks: update. Ten przykład pokazuje, jak opublikować aplikację z kodem wersji 88 i inAppUpdatePriority 5:

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

W kodzie aplikacji możesz sprawdzić poziom priorytetu danej aktualizacji za pomocą updatePriority(). Zwrócony priorytet uwzględnia inAppUpdatePriority dla wszystkich kodów wersji aplikacji między zainstalowaną wersją a najnowszą dostępną wersją, niezależnie od ścieżki wersji. Rozważ na przykład ten scenariusz:

  • Publikujesz wersję do publikacji 1 na ścieżce produkcyjnej bez priorytetu.
  • Publikujesz wersję do publikacji 2 na ścieżce testu wewnętrznego z priorytetem 5.
  • Publikujesz wersję do publikacji 3 na ścieżce produkcyjnej bez priorytetu.

Gdy użytkownicy wersji produkcyjnej zaktualizują aplikację z wersji 1 do wersji 3, otrzymają priorytet 5, mimo że wersja 2 została opublikowana na innej ścieżce.

Kotlin

val appUpdateManager = AppUpdateManagerFactory.create(context)

// Returns an intent object that you use to check for an update.
val appUpdateInfoTask = appUpdateManager.appUpdateInfo

// Checks whether the platform allows the specified type of update,
// and checks the update priority.
appUpdateInfoTask.addOnSuccessListener { appUpdateInfo ->
    if (appUpdateInfo.updateAvailability() == UpdateAvailability.UPDATE_AVAILABLE
          && appUpdateInfo.updatePriority() >= 4 /* high priority */
          && appUpdateInfo.isUpdateTypeAllowed(AppUpdateType.IMMEDIATE)) {
              // Request an immediate update.
    }
}

Java

AppUpdateManager appUpdateManager = AppUpdateManagerFactory.create(context);

// Returns an intent object that you use to check for an update.
Task<AppUpdateInfo> appUpdateInfoTask = appUpdateManager.getAppUpdateInfo();

// Checks whether the platform allows the specified type of update,
// and checks the update priority.
appUpdateInfoTask.addOnSuccessListener(appUpdateInfo -> {
    if (appUpdateInfo.updateAvailability() == UpdateAvailability.UPDATE_AVAILABLE
          && appUpdateInfo.updatePriority() >= 4 /* high priority */
          && appUpdateInfo.isUpdateTypeAllowed(AppUpdateType.IMMEDIATE)) {
              // Request an immediate update.
    }
});

Rozpoczynanie aktualizacji

Gdy potwierdzisz, że aktualizacja jest dostępna, możesz poprosić o nią za pomocą AppUpdateManager.startUpdateFlowForResult():

Kotlin

appUpdateManager.startUpdateFlowForResult(
    // Pass the intent that is returned by 'getAppUpdateInfo()'.
    appUpdateInfo,
    // an activity result launcher registered via registerForActivityResult
    activityResultLauncher,
    // Or pass 'AppUpdateType.FLEXIBLE' to newBuilder() for
    // flexible updates.
    AppUpdateOptions.newBuilder(AppUpdateType.IMMEDIATE).build())

Java

appUpdateManager.startUpdateFlowForResult(
    // Pass the intent that is returned by 'getAppUpdateInfo()'.
    appUpdateInfo,
    // an activity result launcher registered via registerForActivityResult
    activityResultLauncher,
    // Or pass 'AppUpdateType.FLEXIBLE' to newBuilder() for
    // flexible updates.
    AppUpdateOptions.newBuilder(AppUpdateType.IMMEDIATE).build());

Każda instancja AppUpdateInfo może być użyta do rozpoczęcia aktualizacji tylko raz. Aby ponowić próbę aktualizacji w przypadku niepowodzenia, poproś o nową instancję AppUpdateInfo i ponownie sprawdź, czy aktualizacja jest dostępna i dozwolona.

Możesz zarejestrować moduł uruchamiający wynik aktywności za pomocą wbudowanego ActivityResultContracts.StartIntentSenderForResult kontraktu. Zapoznaj się z sekcją dotyczącą otrzymywania wywołania zwrotnego stanu aktualizacji.

Dalsze kroki zależą od tego, czy prosisz o aktualizację elastyczną czy o aktualizację natychmiastową.

Konfigurowanie aktualizacji za pomocą AppUpdateOptions

AppUpdateOptions zawiera pole AllowAssetPackDeletion, które określa czy aktualizacja może wyczyścić pakiety zasobów w przypadku ograniczonej ilości pamięci urządzenia. To pole jest domyślnie ustawione na false, ale możesz użyć metody setAllowAssetPackDeletion(), aby ustawić je na true:

Kotlin

appUpdateManager.startUpdateFlowForResult(
    // Pass the intent that is returned by 'getAppUpdateInfo()'.
    appUpdateInfo,
    // an activity result launcher registered via registerForActivityResult
    activityResultLauncher,
    // Or pass 'AppUpdateType.FLEXIBLE' to newBuilder() for
    // flexible updates.
    AppUpdateOptions.newBuilder(AppUpdateType.IMMEDIATE)
        .setAllowAssetPackDeletion(true)
        .build())

Java

appUpdateManager.startUpdateFlowForResult(
    // Pass the intent that is returned by 'getAppUpdateInfo()'.
    appUpdateInfo,
    // an activity result launcher registered via registerForActivityResult
    activityResultLauncher,
    // Or pass 'AppUpdateType.FLEXIBLE' to newBuilder() for
    // flexible updates.
    AppUpdateOptions.newBuilder(AppUpdateType.IMMEDIATE)
        .setAllowAssetPackDeletion(true)
        .build());

Otrzymywanie wywołania zwrotnego stanu aktualizacji

Po rozpoczęciu aktualizacji zarejestrowane wywołanie zwrotne modułu uruchamiającego wynik aktywności otrzymuje wynik okna potwierdzenia:

Kotlin

registerForActivityResult(StartIntentSenderForResult()) { result: ActivityResult ->
    // handle callback
    if (result.resultCode != RESULT_OK) {
        log("Update flow failed! Result code: " + result.resultCode);
        // If the update is canceled or fails,
        // you can request to start the update again.
    }
}

Java

registerForActivityResult(
    new ActivityResultContracts.StartIntentSenderForResult(),
    new ActivityResultCallback<ActivityResult>() {
        @Override
        public void onActivityResult(ActivityResult result) {
            // handle callback
            if (result.getResultCode() != RESULT_OK) {
                log("Update flow failed! Result code: " + result.getResultCode());
                // If the update is canceled or fails,
                // you can request to start the update again.
            }
        }
    });

Możesz otrzymać kilka wartości z wywołania zwrotnego onActivityResult():

  • RESULT_OK: użytkownik zaakceptował aktualizację. W przypadku aktualizacji natychmiastowych możesz nie otrzymać tego wywołania zwrotnego, ponieważ aktualizacja powinna być już zakończona, zanim kontrola zostanie przekazana z powrotem do Twojej aplikacji.
  • RESULT_CANCELED: użytkownik odrzucił lub anulował aktualizację.
  • ActivityResult.RESULT_IN_APP_UPDATE_FAILED: inny błąd uniemożliwił użytkownikowi wyrażenie zgody lub uniemożliwił przeprowadzenie aktualizacji.

Obsługa aktualizacji elastycznej

Gdy rozpoczniesz aktualizację elastyczną, najpierw pojawi się okno z prośbą o zgodę użytkownika. Jeśli użytkownik wyrazi zgodę, pobieranie rozpocznie się w tle, a użytkownik będzie mógł nadal korzystać z Twojej aplikacji. W tej sekcji opisujemy, jak monitorować i zakończyć elastyczną aktualizację w aplikacji.

Monitorowanie stanu aktualizacji elastycznej

Po rozpoczęciu pobierania aktualizacji elastycznej Twoja aplikacja musi monitorować jej stan, aby wiedzieć, kiedy można ją zainstalować, i wyświetlać postęp w interfejsie aplikacji.

Możesz monitorować stan aktualizacji w toku, rejestrując odbiornik aktualizacji stanu instalacji. Możesz też dodać pasek postępu w interfejsie aplikacji, aby informować użytkowników o postępie pobierania.

Kotlin

// Create a listener to track request state updates.
val listener = InstallStateUpdatedListener { state ->
    // (Optional) Provide a download progress bar.
    if (state.installStatus() == InstallStatus.DOWNLOADING) {
      val bytesDownloaded = state.bytesDownloaded()
      val totalBytesToDownload = state.totalBytesToDownload()
      // Show update progress bar.
    }
    // Log state or install the update.
}

// Before starting an update, register a listener for updates.
appUpdateManager.registerListener(listener)

// Start an update.

// When status updates are no longer needed, unregister the listener.
appUpdateManager.unregisterListener(listener)

Java

// Create a listener to track request state updates.
InstallStateUpdatedListener listener = state -> {
  // (Optional) Provide a download progress bar.
  if (state.installStatus() == InstallStatus.DOWNLOADING) {
      long bytesDownloaded = state.bytesDownloaded();
      long totalBytesToDownload = state.totalBytesToDownload();
      // Implement progress bar.
  }
  // Log state or install the update.
};

// Before starting an update, register a listener for updates.
appUpdateManager.registerListener(listener);

// Start an update.

// When status updates are no longer needed, unregister the listener.
appUpdateManager.unregisterListener(listener);

Instalowanie aktualizacji elastycznej

Gdy wykryjesz stan InstallStatus.DOWNLOADED, musisz ponownie uruchomić aplikację, aby zainstalować aktualizację.

W przeciwieństwie do aktualizacji natychmiastowych Google Play nie uruchamia automatycznie ponownego uruchomienia aplikacji w przypadku aktualizacji elastycznej. Dzieje się tak, ponieważ podczas aktualizacji elastycznej użytkownik oczekuje, że będzie mógł nadal korzystać z aplikacji, dopóki nie zdecyduje, że chce zainstalować aktualizację.

Zalecamy wyświetlenie powiadomienia (lub innego wskazania w interfejsie), aby poinformować użytkownika, że aktualizacja jest gotowa do zainstalowania, i poprosić o potwierdzenie przed ponownym uruchomieniem aplikacji.

Ten przykład pokazuje, jak zaimplementować pasek powiadomień Material Design który prosi użytkownika o potwierdzenie ponownego uruchomienia aplikacji:

Kotlin

val listener = { state ->
    if (state.installStatus() == InstallStatus.DOWNLOADED) {
        // After the update is downloaded, show a notification
        // and request user confirmation to restart the app.
        popupSnackbarForCompleteUpdate()
    }
    ...
}

// Displays the snackbar notification and call to action.
fun popupSnackbarForCompleteUpdate() {
    Snackbar.make(
        findViewById(R.id.activity_main_layout),
        "An update has just been downloaded.",
        Snackbar.LENGTH_INDEFINITE
    ).apply {
        setAction("RESTART") { appUpdateManager.completeUpdate() }
        setActionTextColor(resources.getColor(R.color.snackbar_action_text_color))
        show()
    }
}

Java

InstallStateUpdatedListener listener = state -> {
    if (state.installStatus() == InstallStatus.DOWNLOADED) {
        // After the update is downloaded, show a notification
        // and request user confirmation to restart the app.
        popupSnackbarForCompleteUpdate();
    }
    ...
};

// Displays the snackbar notification and call to action.
private void popupSnackbarForCompleteUpdate() {
  Snackbar snackbar =
      Snackbar.make(
          findViewById(R.id.activity_main_layout),
          "An update has just been downloaded.",
          Snackbar.LENGTH_INDEFINITE);
  snackbar.setAction("RESTART", view -> appUpdateManager.completeUpdate());
  snackbar.setActionTextColor(
      getResources().getColor(R.color.snackbar_action_text_color));
  snackbar.show();
}

Gdy wywołasz appUpdateManager.completeUpdate() na pierwszym planie, platforma wyświetli interfejs pełnoekranowy, który ponownie uruchomi aplikację w tle. Gdy platforma zainstaluje aktualizację, Twoja aplikacja zostanie ponownie uruchomiona w głównym działaniu.

Jeśli zamiast tego wywołasz completeUpdate() , gdy Twoja aplikacja działa w tle, aktualizacja zostanie zainstalowana w trybie cichym bez zasłaniania interfejsu urządzenia.

Za każdym razem, gdy użytkownik przenosi Twoją aplikację na pierwszy plan, sprawdź, czy aplikacja ma aktualizację oczekującą na zainstalowanie. Jeśli Twoja aplikacja ma aktualizację w stanie DOWNLOADED, poproś użytkownika o jej zainstalowanie. W przeciwnym razie dane aktualizacji będą nadal zajmować pamięć urządzenia użytkownika.

Kotlin

// Checks that the update is not stalled during 'onResume()'.
// However, you should execute this check at all app entry points.
override fun onResume() {
    super.onResume()

    appUpdateManager
        .appUpdateInfo
        .addOnSuccessListener { appUpdateInfo ->
            ...
            // If the update is downloaded but not installed,
            // notify the user to complete the update.
            if (appUpdateInfo.installStatus() == InstallStatus.DOWNLOADED) {
                popupSnackbarForCompleteUpdate()
            }
        }
}

Java

// Checks that the update is not stalled during 'onResume()'.
// However, you should execute this check at all app entry points.
@Override
protected void onResume() {
  super.onResume();

  appUpdateManager
      .getAppUpdateInfo()
      .addOnSuccessListener(appUpdateInfo -> {
              ...
              // If the update is downloaded but not installed,
              // notify the user to complete the update.
              if (appUpdateInfo.installStatus() == InstallStatus.DOWNLOADED) {
                  popupSnackbarForCompleteUpdate();
              }
          });
}

Obsługa aktualizacji natychmiastowej

Gdy rozpoczniesz aktualizację natychmiastową i użytkownik wyrazi zgodę na jej rozpoczęcie, Google Play będzie wyświetlać postęp aktualizacji na pierwszym planie interfejsu Twojej aplikacji przez cały czas jej trwania. Jeśli użytkownik zamknie lub zakończy Twoją aplikację podczas aktualizacji, aktualizacja powinna być nadal pobierana i instalowana w tle bez dodatkowego potwierdzenia użytkownika.

Gdy jednak Twoja aplikacja wróci na pierwszy plan, musisz potwierdzić, że aktualizacja nie jest wstrzymana w stanie UpdateAvailability.DEVELOPER_TRIGGERED_UPDATE_IN_PROGRESS. Jeśli aktualizacja jest wstrzymana w tym stanie, wznow ją:

Kotlin

// Checks that the update is not stalled during 'onResume()'.
// However, you should execute this check at all entry points into the app.
override fun onResume() {
    super.onResume()

    appUpdateManager
        .appUpdateInfo
        .addOnSuccessListener { appUpdateInfo ->
            ...
            if (appUpdateInfo.updateAvailability()
                == UpdateAvailability.DEVELOPER_TRIGGERED_UPDATE_IN_PROGRESS
            ) {
                // If an in-app update is already running, resume the update.
                appUpdateManager.startUpdateFlowForResult(
                  appUpdateInfo,
                  activityResultLauncher,
                  AppUpdateOptions.newBuilder(AppUpdateType.IMMEDIATE).build())
            }
        }
}

Java

// Checks that the update is not stalled during 'onResume()'.
// However, you should execute this check at all entry points into the app.
@Override
protected void onResume() {
  super.onResume();

  appUpdateManager
      .getAppUpdateInfo()
      .addOnSuccessListener(
          appUpdateInfo -> {
            ...
            if (appUpdateInfo.updateAvailability()
                == UpdateAvailability.DEVELOPER_TRIGGERED_UPDATE_IN_PROGRESS) {
                // If an in-app update is already running, resume the update.
                appUpdateManager.startUpdateFlowForResult(
                  appUpdateInfo,
                  activityResultLauncher,
                  AppUpdateOptions.newBuilder(AppUpdateType.IMMEDIATE).build());
            }
          });
}

Proces aktualizacji zwraca wynik zgodnie z opisem w dokumentacji referencyjnej dla startUpdateFlowForResult(). W szczególności Twoja aplikacja powinna być w stanie obsługiwać przypadki, w których użytkownik odrzuca aktualizację lub anuluje pobieranie. Gdy użytkownik wykona jedną z tych czynności, interfejs Google Play zostanie zamknięty. Twoja aplikacja powinna określić najlepszy sposób postępowania.

Jeśli to możliwe, pozwól użytkownikowi kontynuować bez aktualizacji i poproś go o nią później. Jeśli Twoja aplikacja nie może działać bez aktualizacji, rozważ wyświetlenie komunikatu informacyjnego przed ponownym uruchomieniem procesu aktualizacji lub poproszeniem użytkownika o zamknięcie aplikacji. Dzięki temu użytkownik będzie wiedział, że może ponownie uruchomić Twoją aplikację, gdy będzie gotowy do zainstalowania wymaganej aktualizacji.

Dalsze kroki

Przetestuj aktualizacje w aplikacji, aby sprawdzić, czy integracja działa prawidłowo.