fbpx

Blog

Android In-App Updates. Обновляем приложение на лету.

Чтобы эффективно удерживать пользователей и взаимодействовать с ними необходимо предоставлять им все новые функции и исправления. У одних пользователей Google Play автоматическое обновление включено, у других – нет. Некоторые из них редко подключаются к качественному Wi-Fi, из-за чего их приложения подолгу не обновляются. Google создали API, который позволяет определять наличие новых версий и функций и интегрировать настраиваемый встроенный процесс обновления, который выглядит как часть приложения. При обнаружении обновления можно уведомить о нем пользователя, предложив обновить приложение сразу или в удобное время. Теперь вы прямо в приложении можете проверить доступность обновления и спросить у пользователя хочет ли он получить новую версию прямо сейчас или позже. Звучит круто, давайте разбираться.

Android In-App Updates работает на девайсах с Android 5.0 (API level 21) и выше, и требуется использовать Play Core library версии 1.5.0 или выше. Если ваше приложение соблюдает данные требование, то вам доступно 2 сценария использования In-App Updates:

Сценарий Flexible.

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

Flexible (Гибкий) сценарий. Пользователь может пользоваться приложением, пока обновление скачивается – после загрузки можно запустить процесс обновления.

Сценарий Immediate (Немедленный)

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

Immediate (Немедленный) Процесс обновления запускается сразу и блокирует использование приложения до окончания установки обновления.

Имейте ввиду. Когда публикуете ваше приложение в виде Android App Bundle, максимальный размер сжатого приложения, поддерживаемого in-app updates не должен превышать 150MB. In-app updates не совместимы с приложениями использующими APK expansion files (.obb files).

Для того, чтобы использовать in app updates, необходимо добавить следующую зависимость

dependencies {
     implementation 'com.google.android.play:core:1.6.0'
}

Внедрение in-app updates достаточно просто. Например, на первой странице вашего приложения вам необходимо оповестить пользователя о том, что доступно обновление для приложения. Для этого в методе onCreate() вашей Activity необходимо добавить следующую проверку:

class MainActivity : AppCompatActivity() {

    private lateinit var appUpdateManager: AppUpdateManager

      override fun onCreate(savedInstanceState: Bundle?) {
         super.onCreate(savedInstanceState)
         setContentView(R.layout.activity_main)
         appUpdateManager = AppUpdateManagerFactory.create(this)
         appUpdateManager.appUpdateInfo.addOnSuccessListener {
         if (it.updateAvailability() == UpdateAvailability.UPDATE_AVAILABLE &&
             it.isUpdateTypeAllowed(AppUpdateType.FLEXIBLE)) {   //  check for the type of update flow you want
             requestUpdate()
         }
     }
 }

Класс AppUpdateManager из библиотеки Play Core library используется для получения объекта AppUpdateInfo, который содержит информацию о наличии обновления, доступной версии и статусе обновления. Если обновление доступно, то здесь же содержится Intent для старта обновления. Если in-app update уже запущено то, то вернется статус обновления. Так как процесс получения информации о доступном обновлении происходит асинхронно – то используя callback addOnSuccessListener мы можем получить добавить метод, который после получения информации запустить intent для обновления

private fun requestUpdate(appUpdateInfo: AppUpdateInfo?) {
     appUpdateManager.startUpdateFlowForResult(
         appUpdateInfo,
         AppUpdateType.FLEXIBLE, //  HERE specify the type of update flow you want
         this,   //  the instance of an activity
         REQUEST_CODE_FLEXIBLE_UPDATE
     )
 }

Ответ пользователя мы получим в методе onActivityResult(). Код ответа может быть одним из: Activity.RESULT_OKActivity.RESULT_CANCELEDActivityResult.RESULT_IN_APP_UPDATE_FAILED. Последний статус вернется из Play core библиотеки и возвращается, если обновление прервано по каким-то причинам.

companion object {
     private const val REQUEST_CODE_FLEXI_UPDATE = 17362
 }
 override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
     super.onActivityResult(requestCode, resultCode, data)
     if (requestCode == REQUEST_CODE_FLEXI_UPDATE) {
         when (resultCode) {
             Activity.RESULT_OK -> { //  handle user's approval }
             Activity.RESULT_CANCELED -> { //  handle user's rejection }
             ActivityResult.RESULT_IN_APP_UPDATE_FAILED -> { //  handle update failure }
         }
     }
 }

Дальше все зависит от того, какой тип обновления вы выбрали.

Сценарий Immediate (Немедленный)

В этом случае после вызова метода appUpdateManager.startUpdateFlowForResult() Google Play запустить процесс обновления и пользователь не сможет использовать приложение до окончания установки приложения. Однако, следует обработать кейс, если приложение в фоне. Для этого

override fun onResume() {
     super.onResume()
     appUpdateManager.appUpdateInfo.addOnSuccessListener {
         if (it.updateAvailability() == UpdateAvailability.DEVELOPER_TRIGGERED_UPDATE_IN_PROGRESS) {
             appUpdateManager.startUpdateFlowForResult(
                 it,
                 AppUpdateType.IMMEDIATE,
                 this,
                 REQUEST_CODE_FLEXI_UPDATE
             )
         }
     }
 }

Флаг UpdateAvailability.DEVELOPER_TRIGGERED_UPDATE_IN_PROGRESS значит, что обновление уже было запущено и процесс установки продолжается. Запуск startUpdateFlowForResult запусит блокирующий экран Google Play с обновлением. После окончания установки обновления Google Play перезапустит приложение.

Сценарий Flexible

В случае, если вы запустили in-app update в режиме Flexible, то как только пользователь согласился обновить приложение, он может продолжить использовать приложение: листать ленту с котятами, лайкать фото друзей и т.д. О том, что обновление скачивается, пользователь заметит по progress bar который находится на панели уведомлений. Вы же, как разработчик можете подписаться на InstallStateUpdatedListener и получать статус обновления (или ошибку)

class MainActivity : AppCompatActivity(), InstallStateUpdatedListener {
    override fun onStateUpdate(installState: InstallState) {
        if (installState.installStatus() == InstallStatus.DOWNLOADED) {
            notifyUser()
        }
    }
    
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        appUpdateManager = AppUpdateManagerFactory.create(this)
        appUpdateManager.registerListener(this)
        
        appUpdateManager.appUpdateInfo.addOnSuccessListener {
        if (it.updateAvailability() == UpdateAvailability.UPDATE_AVAILABLE &&
            it.isUpdateTypeAllowed(AppUpdateType.FLEXIBLE)) {   //  check for the type of update flow you want
            requestUpdate()
        }
    }
    
    override fun onDestroy() {
        super.onDestroy()
        appUpdateManager.unregisterListener(this)
    }
    
    private fun notifyUser() {
        Snackbar
            .make(appVersionTextView, R.string.restart_to_update, Snackbar.LENGTH_INDEFINITE)
            .setAction(R.string.action_restart) {
                appUpdateManager.completeUpdate()
                appUpdateManager.unregisterListener(this)
            }
            .show()
    }
}

Флаг InstallStatus.DOWNLOADED значит, что загрузка завершена и можно уведомить об этом полльзователя (показать snackbar/toast/dialog). Чтобы закончить обновление, необходимо вызвать метод appUpdateManager.completeUpdate() и отписаться от Listener’a. Как и в случае с режимом Immediate, при закрытии приложения, процесс установки приложения не закончится, поэтому пользователя необходимо уведомить о статусе, когда он вернется. Опять же, метод onResume() неплохое место для этого.

override fun onResume() {
        super.onResume()
        appUpdateManager.appUpdateInfo.addOnSuccessListener {
            if (it.installStatus() == InstallStatus.DOWNLOADED) {
                notifyUser()
            }
        }
    }

Вот собственно и все! Резюмируя еще раз подчеркну.
Немедленное обновление необходимо в критических случаях, например для исправлений, касающихся безопасности или конфиденциальности. Когда пользователь соглашается на такое обновление, оно скачивается и активируется при перезапуске приложения. Гибкие обновления после согласия пользователя скачиваются в фоновом режиме. После скачивания вы можете предложить пользователю перезапустить приложение, или же обновление может примениться при переходе приложения в фоновый режим. Если планируете интегрировать данную фичу в ваше приложение, рекомендую ознакомиться с документацией и дополнительными источниками. А здесь как приложение Pandao внедряло данную фичу у себя.