Obsługa aktualizacji w aplikacji (Kotlin lub Java)

Z tego przewodnika dowiesz się, jak obsługiwać aktualizacje w aplikacji w aplikacji za pomocą Kotlina lub Javy. Istnieją oddzielne przewodniki na wypadek, gdy implementacja używa kodu natywnego (C/C++), oraz na wypadek, gdy używa Unity lub Unreal Engine.

Konfigurowanie środowiska programistycznego

Biblioteka aktualizacji w aplikacji w Google Play należy do podstawowych bibliotek Google Play. Aby zintegrować Bibliotekę aktualizacji w aplikacji Google Play, dodaj tę zależność Gradle.

Groovy

// 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 dostępna aktualizacja aplikacji. Aby sprawdzić, czy jest dostępna aktualizacja, kliknij 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 instancja AppUpdateInfo zawiera stan dostępności aktualizacji. W zależności od stanu aktualizacji instancja zawiera też:

  • 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 podaje też stan tej aktualizacji.

Sprawdzanie aktualności aktualizacji

Oprócz sprawdzenia, czy jest dostępna nowa wersja, warto też sprawdzić, ile czasu minęło od ostatniego powiadomienia o aktualizacji w Sklepie Play. Pomoże Ci to zdecydować, czy zainicjować elastyczne wprowadzanie zmian, czy natychmiastowe wprowadzanie zmian. Możesz na przykład poczekać kilka dni, zanim powiadomisz użytkownika o możliwej aktualizacji, a potem jeszcze kilka dni, zanim poprosisz o natychmiastową aktualizację.

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

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. Pozwala to aplikacji określić, jak mocno zalecić użytkownikowi zaktualizowanie. Rozważ na przykład tę strategię ustawiania priorytetu aktualizacji:

  • Niewielkie ulepszenia interfejsu: aktualizacja o niskim priorytecie; nie żądaj aktualizacji elastycznej ani natychmiastowej aktualizacji. Aktualizuj tylko wtedy, gdy użytkownik nie wchodzi w interakcję z aplikacją.
  • Ulepszenia dotyczące wydajności: aktualizacja o średnim priorytecie; poproś o elastyczne aktualizacje.
  • Niezbędna aktualizacja zabezpieczeń: wysoki priorytet; zalecana natychmiastowa aktualizacja.

Aby określić priorytet, Google Play używa liczby całkowitej z zakresu od 0 do 5, przy czym 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 ramach danej wersji mają ten sam priorytet. 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 powinien być określony w zasobie Edit.tracks przekazanym w ramach metody Edit.tracks: update. Ten przykład pokazuje, jak wydać 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ą funkcji updatePriority(). Zwracana priorytet bierze pod uwagę wartość inAppUpdatePriority dla wszystkich kodów wersji aplikacji między zainstalowaną wersją a najnowszą dostępną wersją niezależnie od ścieżki wersji. Rozważmy na przykład taki scenariusz:

  • Wersja 1 jest publikowana na ścieżce produkcyjnej bez priorytetu.
  • Wersję 2 publikujesz na ścieżce testów wewnętrznych z priorytetem 5.
  • Wersja 3 jest publikowana na ścieżce produkcyjnej bez priorytetu.

Gdy użytkownicy w wersji produkcyjnej zaktualizują wersję 1 na wersję 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

Po potwierdzeniu dostępności aktualizacji możesz poprosić o jej zainstalowanie: 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żdej instancji AppUpdateInfo można użyć tylko raz do uruchomienia aktualizacji. Aby ponownie spróbować przeprowadzić aktualizację w przypadku niepowodzenia, poproś o nową AppUpdateInfo i ponownie sprawdź, czy aktualizacja jest dostępna i dozwolona.

Możesz zarejestrować funkcję uruchamiania wyników aktywności, korzystając z wbudowanego kontraktu ActivityResultContracts.StartIntentSenderForResult. Sprawdź sekcję o rozmowie zwrotnej z uaktualnieniem stanu.

Dalsze czynności zależą od tego, czy prośba dotyczy elastycznej aktualizacji, czy natychmiastowej aktualizacji.

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 pamięci urządzenia. Domyślnie to pole jest 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());

Prośba o połączenie zwrotne w celu uzyskania informacji o stanie zgłoszenia

Po rozpoczęciu aktualizacji zarejestrowany wywołanie funkcji wywołania z wynikiem działania 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.
            }
        }
    });

Z funkcji onActivityResult()callback możesz otrzymać kilka wartości:

  • RESULT_OK: użytkownik zaakceptował aktualizację. W przypadku natychmiastowych aktualizacji możesz nie otrzymać tego połączenia zwrotnego, ponieważ aktualizacja powinna być już zakończona, zanim kontrola zostanie przywrócona 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 przeprowadzenie aktualizacji.

Obsługa elastycznej aktualizacji

Gdy rozpoczniesz elastyczne aktualizowanie, użytkownikowi najpierw wyświetli się okno z prośbą o zgodę. Jeśli użytkownik wyrazi zgodę, pobieranie rozpocznie się w tle, a użytkownik będzie mógł nadal korzystać z aplikacji. W tej sekcji opisano, jak monitorować i przeprowadzać elastyczne aktualizacje w aplikacji.

Monitorowanie stanu elastycznej aktualizacji

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

Stan trwającej aktualizacji możesz monitorować, rejestrując listenera na potrzeby aktualizacji stanu instalacji. Możesz też umieścić w interfejsie aplikacji pasek postępu, 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 elastycznej aktualizacji

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

W przypadku aktualizacji elastycznych Google Play nie uruchamia automatycznie ponownego uruchamiania aplikacji, tak jak w przypadku aktualizacji natychmiastowych. Dzieje się tak, ponieważ podczas aktualizacji elastycznej użytkownik może nadal korzystać z aplikacji, dopóki nie zdecyduje się na zainstalowanie aktualizacji.

Zalecamy wyświetlenie powiadomienia (lub innego wskazania w interfejsie) z informacją o tym, że aktualizacja jest gotowa do zainstalowania, oraz poproszenie o potwierdzenie przed ponownym uruchomieniem aplikacji.

Ten przykład pokazuje implementację paska informacji w stylu 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świetla interfejs pełnoekranowy, który uruchamia aplikację w tle. Gdy platforma zainstaluje aktualizację, aplikacja uruchomi się ponownie i przejdzie do głównej czynności.

Jeśli zamiast tego wywołasz completeUpdate(), gdy aplikacja jest w tle, aktualizacja zostanie zainstalowana bez uruchamiania interfejsu użytkownika.

Za każdym razem, gdy użytkownik przeniesie aplikację na pierwszy plan, sprawdź, czy czeka na nią aktualizacja. Jeśli aplikacja ma aktualizację w stanie DOWNLOADED, poproś użytkownika o zainstalowanie aktualizacji. W przeciwnym razie dane aktualizacji nadal będą zajmować miejsce na urządzeniu 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();
              }
          });
}

Przeprowadzanie natychmiastowej aktualizacji

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

Gdy jednak aplikacja wróci na pierwszy plan, sprawdź, czy aktualizacja nie została wstrzymana w stanie UpdateAvailability.DEVELOPER_TRIGGERED_UPDATE_IN_PROGRESS. Jeśli aktualizacja wstrzyma się w tym stanie, wznów 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 startUpdateFlowForResult(). W szczególności aplikacja powinna obsługiwać przypadki, gdy użytkownik odrzuci aktualizację lub anuluje pobieranie. Gdy użytkownik wykona któreś z tych działań, interfejs Google Play zostanie zamknięty. Aplikacja powinna określić najlepszy sposób działania.

Jeśli to możliwe, pozwól użytkownikowi kontynuować bez aktualizacji i poproś o to ponownie później. Jeśli aplikacja nie może działać bez aktualizacji, przed ponownym uruchomieniem procesu aktualizacji warto wyświetlić użytkownikowi wiadomość informacyjną lub poprosić go o zamknięcie aplikacji. W ten sposób użytkownik będzie wiedział, że może ponownie uruchomić aplikację, gdy będzie gotowy do zainstalowania wymaganej aktualizacji.

Dalsze kroki

Sprawdź aktualizacje w aplikacji, aby upewnić się, że integracja działa prawidłowo.