Поддержка обновлений внутри приложения (Kotlin или Java), Поддержка обновлений внутри приложения (Kotlin или Java)

В этом руководстве описывается, как поддерживать обновления внутри вашего приложения с помощью Kotlin или Java. Существуют отдельные руководства для случаев, когда ваша реализация использует собственный код (C/C++), и случаев, когда ваша реализация использует Unity .

Настройте среду разработки

Библиотека обновлений внутри приложения Play является частью библиотек Google Play Core . Пожалуйста, включите следующую зависимость Gradle для интеграции библиотеки обновлений Play в приложении.

классный

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

Котлин

// 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")
    ...
}

Проверьте наличие обновлений

Прежде чем запрашивать обновление, проверьте, доступно ли обновление для вашего приложения. Используйте AppUpdateManager для проверки наличия обновлений:

Котлин

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

Ява

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

Возвращенный экземпляр AppUpdateInfo содержит состояние доступности обновлений. В зависимости от статуса обновления экземпляр также содержит следующее:

  • Если обновление доступно и обновление разрешено, экземпляр также содержит намерение запустить обновление.
  • Если обновление в приложении уже выполняется, экземпляр также сообщает о состоянии текущего обновления.

Проверка устаревания обновлений

Помимо проверки доступности обновления, вы также можете проверить, сколько времени прошло с тех пор, как пользователь в последний раз был уведомлен об обновлении через Play Store. Это может помочь вам решить, следует ли вам инициировать гибкое обновление или немедленное обновление. Например, вы можете подождать несколько дней, прежде чем уведомить пользователя о гибком обновлении, и еще несколько дней после этого, прежде чем требовать немедленного обновления.

Используйте clientVersionStalenessDays() чтобы проверить количество дней с момента появления обновления в Play Store:

Котлин

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

Ява

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

Проверьте приоритет обновления

API разработчика Google Play позволяет вам устанавливать приоритет каждого обновления. Это позволяет вашему приложению решать, насколько настоятельно рекомендовать обновление пользователю. Например, рассмотрим следующую стратегию установки приоритета обновления:

  • Незначительные улучшения пользовательского интерфейса: Обновление с низким приоритетом ; не запрашивайте ни гибкого обновления, ни немедленного обновления. Обновляйте только тогда, когда пользователь не взаимодействует с вашим приложением.
  • Улучшения производительности: обновление среднего приоритета ; запросите гибкое обновление.
  • Критическое обновление безопасности: Высокоприоритетное обновление; запросите немедленное обновление.

Для определения приоритета Google Play использует целочисленное значение от 0 до 5, где 0 — значение по умолчанию, а 5 — наивысший приоритет. Чтобы установить приоритет обновления, используйте поле inAppUpdatePriority в разделе Edits.tracks.releases в API разработчика Google Play. Все новые версии, добавленные в выпуск, имеют тот же приоритет, что и сам выпуск. Приоритет можно установить только при выпуске новой версии и нельзя изменить позже.

Установите приоритет с помощью Google Play Developer API, как описано в документации Play Developer API . Приоритет обновления внутри приложения должен быть указан в ресурсе Edit.tracks , передаваемом в методе Edit.tracks: update . В следующем примере демонстрируется выпуск приложения с кодом версии 88 и inAppUpdatePriority 5:

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

В коде вашего приложения вы можете проверить уровень приоритета для данного обновления с помощью updatePriority() . Возвращаемый приоритет учитывает inAppUpdatePriority для всех кодов версий приложения между установленной версией и последней доступной версией, независимо от версии выпуска. Например, рассмотрим следующий сценарий:

  • Вы выпускаете версию 1 в производственную версию без приоритета.
  • Вы выпускаете версию 2 на внутреннюю тестовую версию с приоритетом 5.
  • Вы выпускаете версию 3 в производственную версию без приоритета.

Когда рабочие пользователи обновятся с версии 1 до версии 3, они получат приоритет 5, даже если версия 2 была опубликована на другой дорожке.

Котлин

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

Ява

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

Начать обновление

После того, как вы подтвердите, что обновление доступно, вы можете запросить обновление с помощью AppUpdateManager.startUpdateFlowForResult() :

Котлин

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

Ява

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());

Каждый экземпляр AppUpdateInfo можно использовать для запуска обновления только один раз. Чтобы повторить попытку обновления в случае сбоя, запросите новый AppUpdateInfo и еще раз проверьте, что обновление доступно и разрешено.

Вы можете зарегистрировать средство запуска результатов активности с помощью встроенного контракта ActivityResultContracts.StartIntentSenderForResult . Проверьте раздел об обратном вызове для получения статуса обновления .

Следующие шаги зависят от того, запрашиваете ли вы гибкое обновление или немедленное обновление .

Настройте обновление с помощью AppUpdateOptions.

AppUpdateOptions содержит поле AllowAssetPackDeletion , которое определяет, разрешено ли обновлению очищать пакеты ресурсов в случае ограниченного объема памяти устройства. По умолчанию для этого поля установлено значение false , но вместо этого вы можете использовать метод setAllowAssetPackDeletion() чтобы установить для него значение true :

Котлин

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

Ява

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());

Получите обратный звонок для получения статуса обновления

После запуска обновления обратный вызов средства запуска зарегистрированных результатов активности получает результат диалогового окна подтверждения:

Котлин

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

Ява

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

Существует несколько значений, которые вы можете получить от обратного вызова onActivityResult() :

  • RESULT_OK : пользователь принял обновление. Для немедленных обновлений вы можете не получить этот обратный вызов, поскольку обновление уже должно быть завершено к тому моменту, когда управление временем будет возвращено вашему приложению.
  • RESULT_CANCELED : пользователь отклонил или отменил обновление.
  • ActivityResult.RESULT_IN_APP_UPDATE_FAILED : какая-то другая ошибка не позволила пользователю предоставить согласие или продолжить обновление.

Обеспечьте гибкое обновление

При запуске гибкого обновления пользователю сначала открывается диалоговое окно для запроса согласия. Если пользователь дает согласие, загрузка начинается в фоновом режиме, и пользователь может продолжать взаимодействовать с вашим приложением. В этом разделе описывается, как отслеживать и выполнять гибкое обновление внутри приложения.

Отслеживайте состояние гибкого обновления

После начала загрузки гибкого обновления вашему приложению необходимо отслеживать состояние обновления, чтобы знать, когда обновление можно будет установить, и отображать ход выполнения в пользовательском интерфейсе вашего приложения.

Вы можете отслеживать состояние выполняемого обновления, зарегистрировав прослушиватель обновлений состояния установки. Вы также можете разместить индикатор выполнения в пользовательском интерфейсе приложения, чтобы информировать пользователей о ходе загрузки.

Котлин

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

Ява

// 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);

Установите гибкое обновление

Когда вы обнаружите состояние InstallStatus.DOWNLOADED , вам необходимо перезапустить приложение, чтобы установить обновление.

В отличие от немедленных обновлений, Google Play не запускает автоматический перезапуск приложения для гибкого обновления. Это связано с тем, что во время гибкого обновления пользователь ожидает, что продолжит взаимодействовать с приложением, пока не решит, что хочет установить обновление.

Рекомендуется предоставить уведомление (или какое-либо другое указание пользовательского интерфейса), чтобы проинформировать пользователя о том, что обновление готово к установке, и запросить подтверждение перед перезапуском приложения.

В следующем примере демонстрируется реализация панели снэк-бара Material Design , которая запрашивает у пользователя подтверждение перезапуска приложения:

Котлин

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()
    }
}

Ява

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();
}

Когда вы вызываете appUpdateManager.completeUpdate() на переднем плане, платформа отображает полноэкранный пользовательский интерфейс, который перезапускает приложение в фоновом режиме. После того как платформа установит обновление, ваше приложение перезапустится и начнет свою основную деятельность.

Если вместо этого вы вызываете completeUpdate() когда ваше приложение находится в фоновом режиме , обновление устанавливается автоматически, не закрывая пользовательский интерфейс устройства.

Всякий раз, когда пользователь выводит ваше приложение на передний план, проверьте, есть ли в вашем приложении обновление, ожидающее установки. Если в вашем приложении есть обновление в состоянии DOWNLOADED , предложите пользователю установить это обновление. В противном случае данные обновления продолжают занимать память устройства пользователя.

Котлин

// 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()
            }
        }
}

Ява

// 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();
              }
          });
}

Обработка немедленного обновления

Когда вы запускаете немедленное обновление и пользователь соглашается начать обновление, Google Play отображает ход обновления поверх пользовательского интерфейса вашего приложения на протяжении всего времени обновления. Если пользователь закроет или завершит работу вашего приложения во время обновления, обновление продолжит загрузку и установку в фоновом режиме без дополнительного подтверждения пользователя.

Однако когда ваше приложение вернется на передний план, вам следует убедиться, что обновление не остановлено в состоянии UpdateAvailability.DEVELOPER_TRIGGERED_UPDATE_IN_PROGRESS . Если обновление зависло в этом состоянии, возобновите обновление:

Котлин

// 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())
            }
        }
}

Ява

// 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());
            }
          });
}

Поток обновления возвращает результат, как описано в справочной документации для startUpdateFlowForResult() . В частности, ваше приложение должно иметь возможность обрабатывать случаи, когда пользователь отклоняет обновление или отменяет загрузку. Когда пользователь выполняет любое из этих действий, пользовательский интерфейс Google Play закрывается. Ваше приложение должно определить лучший способ действий.

Если возможно, позвольте пользователю продолжить работу без обновления и запросите его снова позже. Если ваше приложение не может работать без обновления, рассмотрите возможность отображения информационного сообщения перед перезапуском процесса обновления или предложением пользователю закрыть приложение. Таким образом, пользователь понимает, что он может перезапустить ваше приложение, когда будет готов установить необходимое обновление.

Следующие шаги

Проверьте обновления внутри приложения, чтобы убедиться, что ваша интеграция работает правильно.

,

В этом руководстве описывается, как поддерживать обновления внутри вашего приложения с помощью Kotlin или Java. Существуют отдельные руководства для случаев, когда ваша реализация использует собственный код (C/C++), и случаев, когда ваша реализация использует Unity .

Настройте среду разработки

Библиотека обновлений внутри приложения Play является частью библиотек Google Play Core . Пожалуйста, включите следующую зависимость Gradle для интеграции библиотеки обновлений Play в приложении.

классный

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

Котлин

// 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")
    ...
}

Проверьте наличие обновлений

Прежде чем запрашивать обновление, проверьте, доступно ли обновление для вашего приложения. Используйте AppUpdateManager для проверки наличия обновлений:

Котлин

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

Ява

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

Возвращенный экземпляр AppUpdateInfo содержит состояние доступности обновлений. В зависимости от статуса обновления экземпляр также содержит следующее:

  • Если обновление доступно и обновление разрешено, экземпляр также содержит намерение запустить обновление.
  • Если обновление в приложении уже выполняется, экземпляр также сообщает о состоянии текущего обновления.

Проверка устаревания обновлений

Помимо проверки доступности обновления, вы также можете проверить, сколько времени прошло с тех пор, как пользователь в последний раз был уведомлен об обновлении через Play Store. Это может помочь вам решить, следует ли вам инициировать гибкое обновление или немедленное обновление. Например, вы можете подождать несколько дней, прежде чем уведомить пользователя о гибком обновлении, и еще несколько дней после этого, прежде чем требовать немедленного обновления.

Используйте clientVersionStalenessDays() чтобы проверить количество дней с момента появления обновления в Play Store:

Котлин

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

Ява

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

Проверьте приоритет обновления

API разработчика Google Play позволяет вам устанавливать приоритет каждого обновления. Это позволяет вашему приложению решать, насколько настоятельно рекомендовать обновление пользователю. Например, рассмотрим следующую стратегию установки приоритета обновления:

  • Незначительные улучшения пользовательского интерфейса: Обновление с низким приоритетом ; не запрашивайте ни гибкого обновления, ни немедленного обновления. Обновляйте только тогда, когда пользователь не взаимодействует с вашим приложением.
  • Улучшения производительности: обновление среднего приоритета ; запросите гибкое обновление.
  • Критическое обновление безопасности: Высокоприоритетное обновление; запросите немедленное обновление.

Для определения приоритета Google Play использует целочисленное значение от 0 до 5, где 0 — значение по умолчанию, а 5 — наивысший приоритет. Чтобы установить приоритет обновления, используйте поле inAppUpdatePriority в разделе Edits.tracks.releases в API разработчика Google Play. Все новые версии, добавленные в выпуск, имеют тот же приоритет, что и сам выпуск. Приоритет можно установить только при выпуске новой версии и нельзя изменить позже.

Установите приоритет с помощью Google Play Developer API, как описано в документации Play Developer API . Приоритет обновления внутри приложения должен быть указан в ресурсе Edit.tracks , передаваемом в методе Edit.tracks: update . В следующем примере демонстрируется выпуск приложения с кодом версии 88 и inAppUpdatePriority 5:

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

В коде вашего приложения вы можете проверить уровень приоритета для данного обновления с помощью updatePriority() . Возвращаемый приоритет учитывает inAppUpdatePriority для всех кодов версий приложения между установленной версией и последней доступной версией, независимо от версии выпуска. Например, рассмотрим следующий сценарий:

  • Вы выпускаете версию 1 в производственную версию без приоритета.
  • Вы выпускаете версию 2 на внутреннюю тестовую версию с приоритетом 5.
  • Вы выпускаете версию 3 в производственную версию без приоритета.

Когда рабочие пользователи обновятся с версии 1 до версии 3, они получат приоритет 5, даже если версия 2 была опубликована на другой дорожке.

Котлин

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

Ява

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

Начать обновление

После того, как вы подтвердите, что обновление доступно, вы можете запросить обновление с помощью AppUpdateManager.startUpdateFlowForResult() :

Котлин

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

Ява

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());

Каждый экземпляр AppUpdateInfo можно использовать для запуска обновления только один раз. Чтобы повторить попытку обновления в случае сбоя, запросите новый AppUpdateInfo и еще раз проверьте, что обновление доступно и разрешено.

Вы можете зарегистрировать средство запуска результатов активности, используя встроенный контракт ActivityResultContracts.StartIntentSenderForResult . Проверьте раздел об обратном вызове для получения статуса обновления .

Следующие шаги зависят от того, запрашиваете ли вы гибкое обновление или немедленное обновление .

Настройте обновление с помощью AppUpdateOptions.

AppUpdateOptions содержит поле AllowAssetPackDeletion , которое определяет, разрешено ли обновлению очищать пакеты ресурсов в случае ограниченного объема памяти устройства. По умолчанию для этого поля установлено значение false , но вместо этого вы можете использовать метод setAllowAssetPackDeletion() чтобы установить для него значение true :

Котлин

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

Ява

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());

Получите обратный звонок для получения статуса обновления

После запуска обновления обратный вызов средства запуска зарегистрированных результатов активности получает результат диалогового окна подтверждения:

Котлин

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

Ява

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

Существует несколько значений, которые вы можете получить от обратного вызова onActivityResult() :

  • RESULT_OK : пользователь принял обновление. Для немедленных обновлений вы можете не получить этот обратный вызов, поскольку обновление уже должно быть завершено к тому моменту, когда управление временем будет возвращено вашему приложению.
  • RESULT_CANCELED : пользователь отклонил или отменил обновление.
  • ActivityResult.RESULT_IN_APP_UPDATE_FAILED : какая-то другая ошибка не позволила пользователю предоставить согласие или продолжить обновление.

Обеспечьте гибкое обновление

При запуске гибкого обновления пользователю сначала открывается диалоговое окно для запроса согласия. Если пользователь дает согласие, загрузка начинается в фоновом режиме, и пользователь может продолжать взаимодействовать с вашим приложением. В этом разделе описывается, как отслеживать и выполнять гибкое обновление внутри приложения.

Отслеживайте состояние гибкого обновления

После начала загрузки гибкого обновления вашему приложению необходимо отслеживать состояние обновления, чтобы знать, когда обновление можно будет установить, и отображать ход выполнения в пользовательском интерфейсе вашего приложения.

Вы можете отслеживать состояние выполняемого обновления, зарегистрировав прослушиватель обновлений состояния установки. Вы также можете разместить индикатор выполнения в пользовательском интерфейсе приложения, чтобы информировать пользователей о ходе загрузки.

Котлин

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

Ява

// 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);

Установите гибкое обновление

Когда вы обнаружите состояние InstallStatus.DOWNLOADED , вам необходимо перезапустить приложение, чтобы установить обновление.

В отличие от немедленных обновлений, Google Play не запускает автоматический перезапуск приложения для гибкого обновления. Это связано с тем, что во время гибкого обновления пользователь ожидает, что продолжит взаимодействовать с приложением, пока не решит, что хочет установить обновление.

Рекомендуется предоставить уведомление (или какое-либо другое указание пользовательского интерфейса), чтобы проинформировать пользователя о том, что обновление готово к установке, и запросить подтверждение перед перезапуском приложения.

В следующем примере демонстрируется реализация панели снэк-бара Material Design , которая запрашивает у пользователя подтверждение перезапуска приложения:

Котлин

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()
    }
}

Ява

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();
}

Когда вы вызываете appUpdateManager.completeUpdate() на переднем плане, платформа отображает полноэкранный пользовательский интерфейс, который перезапускает приложение в фоновом режиме. После того как платформа установит обновление, ваше приложение перезапустится и начнет свою основную деятельность.

Если вместо этого вы вызываете completeUpdate() когда ваше приложение находится в фоновом режиме , обновление устанавливается автоматически, не закрывая пользовательский интерфейс устройства.

Всякий раз, когда пользователь выводит ваше приложение на передний план, проверьте, есть ли в вашем приложении обновление, ожидающее установки. Если в вашем приложении есть обновление в состоянии DOWNLOADED , предложите пользователю установить это обновление. В противном случае данные обновления продолжают занимать память устройства пользователя.

Котлин

// 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()
            }
        }
}

Ява

// 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();
              }
          });
}

Обработка немедленного обновления

Когда вы запускаете немедленное обновление и пользователь соглашается начать обновление, Google Play отображает ход обновления поверх пользовательского интерфейса вашего приложения на протяжении всего времени обновления. Если пользователь закроет или завершит работу вашего приложения во время обновления, обновление продолжит загрузку и установку в фоновом режиме без дополнительного подтверждения пользователя.

Однако когда ваше приложение вернется на передний план, вам следует убедиться, что обновление не остановлено в состоянии UpdateAvailability.DEVELOPER_TRIGGERED_UPDATE_IN_PROGRESS . Если обновление зависло в этом состоянии, возобновите обновление:

Котлин

// 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())
            }
        }
}

Ява

// 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());
            }
          });
}

Поток обновления возвращает результат, как описано в справочной документации для startUpdateFlowForResult() . В частности, ваше приложение должно иметь возможность обрабатывать случаи, когда пользователь отклоняет обновление или отменяет загрузку. Когда пользователь выполняет любое из этих действий, пользовательский интерфейс Google Play закрывается. Ваше приложение должно определить лучший способ действий.

Если возможно, позвольте пользователю продолжить работу без обновления и запросите его снова позже. Если ваше приложение не может работать без обновления, рассмотрите возможность отображения информационного сообщения перед перезапуском процесса обновления или предложением пользователю закрыть приложение. Таким образом, пользователь понимает, что он может перезапустить ваше приложение, когда будет готов установить необходимое обновление.

Следующие шаги

Проверьте обновления внутри приложения, чтобы убедиться, что ваша интеграция работает правильно.

,

В этом руководстве описывается, как поддерживать обновления внутри вашего приложения с помощью Kotlin или Java. Существуют отдельные руководства для случаев, когда ваша реализация использует собственный код (C/C++), и случаев, когда ваша реализация использует Unity .

Настройте среду разработки

Библиотека обновлений внутри приложения Play является частью библиотек Google Play Core . Пожалуйста, включите следующую зависимость Gradle для интеграции библиотеки обновлений Play в приложении.

классный

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

Котлин

// 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")
    ...
}

Проверьте наличие обновлений

Прежде чем запрашивать обновление, проверьте, доступно ли обновление для вашего приложения. Используйте AppUpdateManager для проверки наличия обновлений:

Котлин

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

Ява

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

Возвращенный экземпляр AppUpdateInfo содержит состояние доступности обновления. В зависимости от статуса обновления экземпляр также содержит следующее:

  • Если обновление доступно и обновление разрешено, экземпляр также содержит намерение запустить обновление.
  • Если обновление в приложении уже выполняется, экземпляр также сообщает о состоянии текущего обновления.

Проверка устаревания обновлений

Помимо проверки доступности обновления, вы также можете проверить, сколько времени прошло с тех пор, как пользователь в последний раз был уведомлен об обновлении через Play Store. Это может помочь вам решить, следует ли вам инициировать гибкое обновление или немедленное обновление. Например, вы можете подождать несколько дней, прежде чем уведомить пользователя о гибком обновлении, и еще несколько дней после этого, прежде чем требовать немедленного обновления.

Используйте clientVersionStalenessDays() чтобы проверить количество дней с момента появления обновления в Play Store:

Котлин

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

Ява

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

Проверьте приоритет обновления

API разработчика Google Play позволяет вам устанавливать приоритет каждого обновления. Это позволяет вашему приложению решать, насколько настоятельно рекомендовать обновление пользователю. Например, рассмотрим следующую стратегию установки приоритета обновления:

  • Незначительные улучшения пользовательского интерфейса: Обновление с низким приоритетом ; не запрашивайте ни гибкого обновления, ни немедленного обновления. Обновляйте только тогда, когда пользователь не взаимодействует с вашим приложением.
  • Улучшения производительности: обновление среднего приоритета ; запросите гибкое обновление.
  • Критическое обновление безопасности: Высокоприоритетное обновление; запросите немедленное обновление.

Для определения приоритета Google Play использует целочисленное значение от 0 до 5, где 0 — значение по умолчанию, а 5 — наивысший приоритет. Чтобы установить приоритет обновления, используйте поле inAppUpdatePriority в разделе Edits.tracks.releases в API разработчика Google Play. Все новые версии, добавленные в выпуск, имеют тот же приоритет, что и сам выпуск. Приоритет можно установить только при выпуске новой версии и нельзя изменить позже.

Установите приоритет с помощью Google Play Developer API, как описано в документации Play Developer API . Приоритет обновления внутри приложения должен быть указан в ресурсе Edit.tracks , передаваемом в методе Edit.tracks: update . В следующем примере демонстрируется выпуск приложения с кодом версии 88 и inAppUpdatePriority 5:

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

В коде вашего приложения вы можете проверить уровень приоритета для данного обновления с помощью updatePriority() . Возвращаемый приоритет учитывает inAppUpdatePriority для всех кодов версий приложения между установленной версией и последней доступной версией, независимо от версии выпуска. Например, рассмотрим следующий сценарий:

  • Вы выпускаете версию 1 в производственную версию без приоритета.
  • Вы выпускаете версию 2 на внутреннюю тестовую версию с приоритетом 5.
  • Вы выпускаете версию 3 в производственную версию без приоритета.

Когда рабочие пользователи обновятся с версии 1 до версии 3, они получат приоритет 5, даже если версия 2 была опубликована на другой дорожке.

Котлин

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

Ява

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

Начать обновление

После того, как вы подтвердите, что обновление доступно, вы можете запросить его с помощью AppUpdateManager.startUpdateFlowForResult() :

Котлин

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

Ява

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());

Каждый экземпляр AppUpdateInfo можно использовать для запуска обновления только один раз. Чтобы повторить попытку обновления в случае сбоя, запросите новый AppUpdateInfo и еще раз проверьте, что обновление доступно и разрешено.

Вы можете зарегистрировать средство запуска результатов активности с помощью встроенного контракта ActivityResultContracts.StartIntentSenderForResult . Проверьте раздел об обратном вызове для получения статуса обновления .

Следующие шаги зависят от того, запрашиваете ли вы гибкое обновление или немедленное обновление .

Настройте обновление с помощью AppUpdateOptions.

AppUpdateOptions содержит поле AllowAssetPackDeletion , которое определяет, разрешено ли обновлению очищать пакеты ресурсов в случае ограниченного объема памяти устройства. По умолчанию для этого поля установлено значение false , но вместо этого вы можете использовать метод setAllowAssetPackDeletion() чтобы установить для него значение true :

Котлин

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

Ява

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());

Получите обратный звонок для получения статуса обновления

После запуска обновления обратный вызов средства запуска зарегистрированных результатов активности получает результат диалогового окна подтверждения:

Котлин

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

Ява

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

Существует несколько значений, которые вы можете получить от обратного вызова onActivityResult() :

  • RESULT_OK : пользователь принял обновление. Для немедленных обновлений вы можете не получить этот обратный вызов, поскольку обновление уже должно быть завершено к тому моменту, когда управление временем будет возвращено вашему приложению.
  • RESULT_CANCELED : пользователь отклонил или отменил обновление.
  • ActivityResult.RESULT_IN_APP_UPDATE_FAILED : какая-то другая ошибка не позволила пользователю предоставить согласие или продолжить обновление.

Обеспечьте гибкое обновление

При запуске гибкого обновления пользователю сначала открывается диалоговое окно для запроса согласия. Если пользователь дает согласие, загрузка начинается в фоновом режиме, и пользователь может продолжать взаимодействовать с вашим приложением. В этом разделе описывается, как отслеживать и выполнять гибкое обновление внутри приложения.

Отслеживайте состояние гибкого обновления

После начала загрузки гибкого обновления вашему приложению необходимо отслеживать состояние обновления, чтобы знать, когда обновление можно будет установить, и отображать ход выполнения в пользовательском интерфейсе вашего приложения.

Вы можете отслеживать состояние выполняемого обновления, зарегистрировав прослушиватель обновлений состояния установки. Вы также можете разместить индикатор выполнения в пользовательском интерфейсе приложения, чтобы информировать пользователей о ходе загрузки.

Котлин

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

Ява

// 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);

Установите гибкое обновление

Когда вы обнаружите состояние InstallStatus.DOWNLOADED , вам необходимо перезапустить приложение, чтобы установить обновление.

В отличие от немедленных обновлений, Google Play не запускает автоматический перезапуск приложения для гибкого обновления. Это связано с тем, что во время гибкого обновления пользователь ожидает, что продолжит взаимодействовать с приложением до тех пор, пока не решит, что хочет установить обновление.

Рекомендуется предоставить уведомление (или какое-либо другое указание пользовательского интерфейса), чтобы проинформировать пользователя о том, что обновление готово к установке, и запросить подтверждение перед перезапуском приложения.

В следующем примере демонстрируется реализация панели снэк-бара Material Design , которая запрашивает у пользователя подтверждение перезапуска приложения:

Котлин

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()
    }
}

Ява

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();
}

Когда вы вызываете appUpdateManager.completeUpdate() на переднем плане, платформа отображает полноэкранный пользовательский интерфейс, который перезапускает приложение в фоновом режиме. После того как платформа установит обновление, ваше приложение перезапустится и начнет свою основную деятельность.

Если вместо этого вы вызываете completeUpdate() когда ваше приложение находится в фоновом режиме , обновление устанавливается автоматически, не закрывая пользовательский интерфейс устройства.

Всякий раз, когда пользователь выводит ваше приложение на передний план, проверьте, есть ли в вашем приложении обновление, ожидающее установки. Если в вашем приложении есть обновление в состоянии DOWNLOADED , предложите пользователю установить это обновление. В противном случае данные обновления продолжают занимать память устройства пользователя.

Котлин

// 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()
            }
        }
}

Ява

// 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();
              }
          });
}

Обработка немедленного обновления

Когда вы запускаете немедленное обновление и пользователь соглашается начать обновление, Google Play отображает ход обновления поверх пользовательского интерфейса вашего приложения на протяжении всего времени обновления. Если пользователь закроет или завершит работу вашего приложения во время обновления, обновление продолжит загрузку и установку в фоновом режиме без дополнительного подтверждения пользователя.

Однако когда ваше приложение вернется на передний план, вам следует убедиться, что обновление не остановлено в состоянии UpdateAvailability.DEVELOPER_TRIGGERED_UPDATE_IN_PROGRESS . Если обновление зависло в этом состоянии, возобновите обновление:

Котлин

// 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())
            }
        }
}

Ява

// 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());
            }
          });
}

Поток обновления возвращает результат, как описано в справочной документации для startUpdateFlowForResult() . В частности, ваше приложение должно иметь возможность обрабатывать случаи, когда пользователь отклоняет обновление или отменяет загрузку. Когда пользователь выполняет любое из этих действий, пользовательский интерфейс Google Play закрывается. Ваше приложение должно определить лучший способ действий.

Если возможно, позвольте пользователю продолжить работу без обновления и запросите его снова позже. Если ваше приложение не может работать без обновления, рассмотрите возможность отображения информационного сообщения перед перезапуском процесса обновления или предложением пользователю закрыть приложение. Таким образом, пользователь понимает, что он может перезапустить ваше приложение, когда будет готов установить необходимое обновление.

Следующие шаги

Проверьте обновления внутри приложения, чтобы убедиться, что ваша интеграция работает правильно.

,

В этом руководстве описывается, как поддерживать обновления внутри вашего приложения с помощью Kotlin или Java. Существуют отдельные руководства для случаев, когда ваша реализация использует собственный код (C/C++), и случаев, когда ваша реализация использует Unity .

Настройте среду разработки

Библиотека обновлений внутри приложения Play является частью библиотек Google Play Core . Пожалуйста, включите следующую зависимость Gradle для интеграции библиотеки обновлений Play в приложении.

классный

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

Котлин

// 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")
    ...
}

Проверьте наличие обновлений

Прежде чем запрашивать обновление, проверьте, доступно ли обновление для вашего приложения. Используйте AppUpdateManager для проверки наличия обновлений:

Котлин

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

Ява

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

Возвращенный экземпляр AppUpdateInfo содержит состояние доступности обновления. В зависимости от статуса обновления экземпляр также содержит следующее:

  • Если обновление доступно и обновление разрешено, экземпляр также содержит намерение запустить обновление.
  • Если обновление в приложении уже выполняется, экземпляр также сообщает о состоянии текущего обновления.

Проверка устаревания обновлений

Помимо проверки доступности обновления, вы также можете проверить, сколько времени прошло с тех пор, как пользователь в последний раз был уведомлен об обновлении через Play Store. Это может помочь вам решить, следует ли вам инициировать гибкое обновление или немедленное обновление. Например, вы можете подождать несколько дней, прежде чем уведомить пользователя о гибком обновлении, и еще несколько дней после этого, прежде чем требовать немедленного обновления.

Используйте clientVersionStalenessDays() чтобы проверить количество дней с момента появления обновления в Play Store:

Котлин

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

Ява

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

Проверьте приоритет обновления

API разработчика Google Play позволяет вам устанавливать приоритет каждого обновления. Это позволяет вашему приложению решать, насколько настоятельно рекомендовать обновление пользователю. Например, рассмотрим следующую стратегию установки приоритета обновления:

  • Незначительные улучшения пользовательского интерфейса: Обновление с низким приоритетом ; не запрашивайте ни гибкого обновления, ни немедленного обновления. Обновляйте только тогда, когда пользователь не взаимодействует с вашим приложением.
  • Улучшения производительности: обновление среднего приоритета ; запросите гибкое обновление.
  • Критическое обновление безопасности: Высокоприоритетное обновление; запросите немедленное обновление.

Для определения приоритета Google Play использует целое число от 0 до 5, где 0 — значение по умолчанию, а 5 — наивысший приоритет. Чтобы установить приоритет обновления, используйте поле inAppUpdatePriority в разделе Edits.tracks.releases в API разработчика Google Play. Все новые версии, добавленные в выпуск, имеют тот же приоритет, что и сам выпуск. Приоритет можно установить только при выпуске новой версии и нельзя изменить позже.

Установите приоритет с помощью Google Play Developer API, как описано в документации Play Developer API . Приоритет обновления внутри приложения должен быть указан в ресурсе Edit.tracks , передаваемом в методе Edit.tracks: update . В следующем примере демонстрируется выпуск приложения с кодом версии 88 и inAppUpdatePriority 5:

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

В коде вашего приложения вы можете проверить уровень приоритета для данного обновления с помощью updatePriority() . Возвращаемый приоритет учитывает inAppUpdatePriority для всех кодов версий приложения между установленной версией и последней доступной версией, независимо от версии выпуска. Например, рассмотрим следующий сценарий:

  • Вы выпускаете версию 1 в производственную версию без приоритета.
  • Вы выпускаете версию 2 на внутреннюю тестовую версию с приоритетом 5.
  • Вы выпускаете версию 3 в производственную версию без приоритета.

Когда рабочие пользователи обновят версию 1 до версии 3, они получат приоритет 5, даже если версия 2 была опубликована на другой дорожке.

Котлин

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

Ява

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

Начать обновление

После того, как вы подтвердите, что обновление доступно, вы можете запросить его с помощью AppUpdateManager.startUpdateFlowForResult() :

Котлин

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

Ява

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());

Каждый экземпляр AppUpdateInfo можно использовать для запуска обновления только один раз. Чтобы повторить попытку обновления в случае сбоя, запросите новый AppUpdateInfo и еще раз проверьте, что обновление доступно и разрешено.

Вы можете зарегистрировать запуск результата активности, используя строительный контракт с ActivityResultContracts.StartIntentSenderForResult . Проверьте раздел о получении обратного вызова для статуса обновления .

Следующие шаги зависят от того, запрашиваете ли вы гибкое обновление или немедленное обновление .

Настройте обновление с AppupDateOptions

AppUpdateOptions содержит поле AllowAssetPackDeletion , которое определяет, разрешено ли обновление очищать пакеты активов в случае ограниченного хранения устройств. Это поле по умолчанию установлено на false , но вы можете использовать метод setAllowAssetPackDeletion() чтобы вместо этого установить его на true :

Котлин

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

Ява

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());

Получите обратный вызов для статуса обновления

После начала обновления зарегистрированный обратный вызов запуска запуска

Котлин

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

Ява

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

Есть несколько значений, которые вы могли бы получить от обратного вызова onActivityResult() :

  • RESULT_OK : пользователь принял обновление. Для немедленных обновлений вы можете не получить этот обратный вызов, потому что обновление уже должно быть закончено к мощности управления, возвращающимся вашему приложению.
  • RESULT_CANCELED : пользователь отрицал или отменил обновление.
  • ActivityResult.RESULT_IN_APP_UPDATE_FAILED : какая -то другая ошибка не позволила пользователю предоставить согласие, либо обновление не было выполнено.

Обрабатывать гибкое обновление

Когда вы запускаете гибкое обновление, пользователю сначала появляется диалог, чтобы запросить согласие. Если пользователь соглашается, то загрузка начинается в фоновом режиме, и пользователь может продолжать взаимодействовать с вашим приложением. В этом разделе описывается, как контролировать и заполнить гибкое обновление в приложении.

Следите за гибким состоянием обновления

После того, как загрузка начинается для гибкого обновления, вашему приложению необходимо отслеживать состояние обновления, чтобы узнать, когда может быть установлено обновление, и для отображения прогресса в пользовательском интерфейсе вашего приложения.

Вы можете отслеживать состояние обновления, зарегистрировав слушателя для обновлений статуса установки. Вы также можете предоставить панель прогресса в пользовательском интерфейсе приложения, чтобы информировать пользователей о прогрессе загрузки.

Котлин

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

Ява

// 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);

Установите гибкое обновление

Когда вы обнаружите состояние InstallStatus.DOWNLOADED , вам необходимо перезагрузить приложение для установки обновления.

В отличие от немедленных обновлений, Google Play автоматически не запускает перезапуск приложения для гибкого обновления. Это связано с тем, что во время гибкого обновления пользователь ожидает продолжать взаимодействовать с приложением, пока не решит, что они хотят установить обновление.

Рекомендуется предоставить уведомление (или какую -то другую индикацию пользовательского интерфейса), чтобы сообщить пользователю, что обновление готово к установке и запросу подтверждения перед перезапуском приложения.

Следующий пример демонстрирует реализацию закусочной дизайна материала , которая запрашивает подтверждение от пользователя для перезапуска приложения:

Котлин

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()
    }
}

Ява

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();
}

Когда вы называете appUpdateManager.completeUpdate() на переднем плане, на платформе отображается полноэкранный пользовательский интерфейс, который перезапускает приложение в фоновом режиме. После того, как платформа устанавливает обновление, ваше приложение перезагружается в свое основное действие.

Если вы вместо этого вызовите completeUpdate() когда ваше приложение находится в фоновом режиме , обновление установлено молча, не затмевая пользовательский интерфейс устройства.

Всякий раз, когда пользователь выводит ваше приложение на передний план, проверьте, есть ли в вашем приложении обновление, ожидающее установки. Если в вашем приложении есть обновление в DOWNLOADED состоянии, предложите пользователю установить обновление. В противном случае данные обновления продолжают занимать хранилище устройства пользователя.

Котлин

// 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()
            }
        }
}

Ява

// 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();
              }
          });
}

Обрабатывать немедленное обновление

Когда вы начинаете немедленное обновление, и пользователь соглашается начать обновление, Google Play отображает прогресс обновления в верхней части пользовательского интерфейса вашего приложения на протяжении всей продолжительности обновления. Если пользователь закрывает или завершает ваше приложение во время обновления, обновление должно продолжать загружать и устанавливать в фоновом режиме без дополнительного подтверждения пользователя.

Однако, когда ваше приложение возвращается на передний план, вы должны подтвердить, что обновление не задерживается в состоянии UpdateAvailability.DEVELOPER_TRIGGERED_UPDATE_IN_PROGRESS . Если обновление остановлено в этом состоянии, возобновите обновление:

Котлин

// 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())
            }
        }
}

Ява

// 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());
            }
          });
}

Поток обновления возвращает результат, как описано в справочной документации для StartupDateFlowForresult () . В частности, ваше приложение должно иметь возможность обрабатывать случаи, когда пользователь отказывается от обновления или отменяет загрузку. Когда пользователь выполняет любое из этих действий, интерфейс Google Play закрывается. Ваше приложение должно определить лучший способ продолжить.

Если возможно, пусть пользователь будет продолжаться без обновления и привнести его снова позже. Если ваше приложение не может функционировать без обновления, рассмотрите возможность отображения информативного сообщения перед перезапуском потока обновления или побуждения пользователя закрыть приложение. Таким образом, пользователь понимает, что он может перезапустить ваше приложение, когда они будут готовы установить необходимое обновление.

Следующие шаги

Проверьте обновления вашего приложения, чтобы убедиться, что ваша интеграция работает правильно.