Как работать с API обновления Android в приложении?

Недавно я столкнулся с новым видом потока обновлений приложений, который может быть предоставлен Google Play. Мне понравился плавный процесс обновления приложения для Android. Я заметил нижеуказанные шаги в приложении Hotstar.

  1. Открыта карта снизу, показывающая, что доступно обновление
  2. Когда я нажал кнопку "Обновить Hotstar", всплыло одно диалоговое окно (похоже, оно предоставлено Google Play)

  1. Загрузка была запущена в фоновом режиме, пока приложение работало
  2. После завершения загрузки выскочил один SnackBar, показывая приложение, готовое к установке.
  3. Приложение перезапустилось после установки

Как мне этого добиться? Должен быть способ общения с Google Play. Я просмотрел много блогов. Но, не нашел никакого решения. Это может быть отличной возможностью для разработчика, если автоматическое обновление приложения отключено пользователем.

5 ответов

Решение

Официальная документация: https://developer.android.com/guide/app-bundle/in-app-updates

Ограничение: обновление в приложении работает только на устройствах под управлением Android 5.0 (уровень API 21) или выше

Шаг 1: Добавить зависимость:

dependencies {

    implementation 'com.google.android.play:core:1.5.0'
    ...
}

Шаг 2. Проверьте наличие обновлений и запустите, если оно доступно

mAppUpdateManager = AppUpdateManagerFactory.create(this);

mAppUpdateManager.registerListener(installStateUpdatedListener);

mAppUpdateManager.getAppUpdateInfo().addOnSuccessListener(appUpdateInfo -> {

        if (appUpdateInfo.updateAvailability() == UpdateAvailability.UPDATE_AVAILABLE
                && appUpdateInfo.isUpdateTypeAllowed(AppUpdateType.IMMEDIATE)){

            try {    
                    mAppUpdateManager.startUpdateFlowForResult(
                            appUpdateInfo, AppUpdateType.FLEXIBLE, MainActivity.this, RC_APP_UPDATE);
                }

            } catch (IntentSender.SendIntentException e) {
                e.printStackTrace();
            }

        } else if (appUpdateInfo.installStatus() == InstallStatus.DOWNLOADED){
            popupSnackbarForCompleteUpdate();
        } else {
            Log.e(TAG, "checkForAppUpdateAvailability: something else");
        }
    });

Шаг 3: Слушайте, чтобы обновить состояние

InstallStateUpdatedListener installStateUpdatedListener = new 
  InstallStateUpdatedListener() {
    @Override
    public void onStateUpdate(InstallState state) {
        if (state.installStatus() == InstallStatus.DOWNLOADED){
            popupSnackbarForCompleteUpdate();
        } else if (state.installStatus() == InstallStatus.INSTALLED){
            if (mAppUpdateManager != null){
          mAppUpdateManager.unregisterListener(installStateUpdatedListener);
            }

        } else {
            Log.i(TAG, "InstallStateUpdatedListener: state: " + state.installStatus());
        }
    }
};

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

@Override
protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
    super.onActivityResult(requestCode, resultCode, data);

    if (requestCode == RC_APP_UPDATE) {
        if (resultCode != RESULT_OK) {
            Log.e(TAG, "onActivityResult: app download failed");
        }
    }
}

Шаг 5: Гибкое обновление

private void popupSnackbarForCompleteUpdate() {

    Snackbar snackbar =
            Snackbar.make(
                    findViewById(R.id.coordinatorLayout_main),
                    "New app is ready!",
                    Snackbar.LENGTH_INDEFINITE);

    snackbar.setAction("Install", view -> {
        if (mAppUpdateManager != null){
            mAppUpdateManager.completeUpdate();
        }
    });


snackbar.setActionTextColor(getResources().getColor(R.color.install_color));
    snackbar.show();
}

Android официально объявил об обновлениях в приложении для всех сегодня. https://developer.android.com/guide/app-bundle/in-app-updates

Пытаясь реализовать это, официальная Документация Google, приведенная в принятом ответе, синтаксически неверна. Потребовалось некоторое исследование, но я наконец нашел правильный синтаксис:

Вместо:

// Creates an instance of the manager.
AppUpdateManager appUpdateManager = AppUpdateManagerFactory.create(context);

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

// Checks that the platform will allow the specified type of update.
if (appUpdateInfo.updateAvailability() == UpdateAvailability.UPDATE_AVAILABLE
  // For a flexible update, use AppUpdateType.FLEXIBLE
  && appUpdateInfo.isUpdateTypeAllowed(AppUpdateType.IMMEDIATE)) {
              // Request the update.

    appUpdateManager.startUpdateFlowForResult(
        // Pass the intent that is returned by 'getAppUpdateInfo()'.
        appUpdateInfo,
        // Or 'AppUpdateType.FLEXIBLE' for flexible updates.
        AppUpdateType.IMMEDIATE,
        // The current activity making the update request.
        this,
        // Include a request code to later monitor this update request.
        MY_REQUEST_CODE);
}

Сделай это:

    private AppUpdateManager appUpdateManager;
    ...
    // onCreate(){ 
    // Creates instance of the manager.
    appUpdateManager = AppUpdateManagerFactory.create(mainContext);

    // Don't need to do this here anymore
    // Returns an intent object that you use to check for an update.
    //Task<AppUpdateInfo> appUpdateInfo = appUpdateManager.getAppUpdateInfo();

    appUpdateManager
            .getAppUpdateInfo()
            .addOnSuccessListener(
                    appUpdateInfo -> {

                        // Checks that the platform will allow the specified type of update.
                        if ((appUpdateInfo.updateAvailability() == UpdateAvailability.UPDATE_AVAILABLE)
                                && appUpdateInfo.isUpdateTypeAllowed(AppUpdateType.IMMEDIATE))
                        {
                            // Request the update.
                            try {
                                appUpdateManager.startUpdateFlowForResult(
                                        appUpdateInfo,
                                        AppUpdateType.IMMEDIATE,
                                        this,
                                        REQUEST_APP_UPDATE);
                            } catch (IntentSender.SendIntentException e) {
                                e.printStackTrace();
                            }
                        }
                    });

Затем закодируйте аналогичный фрагмент кода в переопределении onResume() на случай, если установка застрянет на этом пути:

//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.
                            try {
                                appUpdateManager.startUpdateFlowForResult(
                                        appUpdateInfo,
                                        AppUpdateType.IMMEDIATE,
                                        this,
                                        REQUEST_APP_UPDATE);
                            } catch (IntentSender.SendIntentException e) {
                                e.printStackTrace();
                            }
                        }
                    });
}

Please try this once.

Step 1: In build.gradle file add the below library

implementation 'com.google.android.play:core:1.6.4'

Step 2: Declare the following variables in class (Ex MainActivity.java)

    private AppUpdateManager mAppUpdateManager;
    private int RC_APP_UPDATE = 999;
    private int inAppUpdateType;
    private com.google.android.play.core.tasks.Task<AppUpdateInfo> appUpdateInfoTask;
    private InstallStateUpdatedListener installStateUpdatedListener;

Step 3: In onCreate() method add the below code (initializing variables)

        // Creates instance of the manager.
        mAppUpdateManager = AppUpdateManagerFactory.create(this);
        // Returns an intent object that you use to check for an update.
        appUpdateInfoTask = mAppUpdateManager.getAppUpdateInfo();
        //lambda operation used for below listener
        //For flexible update
        installStateUpdatedListener = installState -> {
            if (installState.installStatus() == InstallStatus.DOWNLOADED) {
                popupSnackbarForCompleteUpdate();
            }
        };
        mAppUpdateManager.registerListener(installStateUpdatedListener);

Step 4: In onDestroy() method of activity just unregister the listener

 @Override
    protected void onDestroy() {
        mAppUpdateManager.unregisterListener(installStateUpdatedListener);
        super.onDestroy();
    }

Step 5: In onResume() we need to listen to both Flexible and Immediate updates by the below code.

@Override
        protected void onResume() {
           try {   
  mAppUpdateManager.getAppUpdateInfo().addOnSuccessListener(appUpdateInfo -> {
        if (appUpdateInfo.updateAvailability() == 
           UpdateAvailability.DEVELOPER_TRIGGERED_UPDATE_IN_PROGRESS) {
           // If an in-app update is already running, resume the update.
                       try {
                            mAppUpdateManager.startUpdateFlowForResult(
                                    appUpdateInfo,
                                    inAppUpdateType,
                                    this,
                                    RC_APP_UPDATE);
                        } catch (IntentSender.SendIntentException e) {
                            e.printStackTrace();
                        }
                    }
                });


  mAppUpdateManager.getAppUpdateInfo().addOnSuccessListener(appUpdateInfo -> {
     //For flexible update            
       if (appUpdateInfo.installStatus() == InstallStatus.DOWNLOADED) {
                        popupSnackbarForCompleteUpdate();
                    }
                });
            } catch (Exception e) {
                e.printStackTrace();
            }

            super.onResume();
    }

Step 6: In onActivityResult() we need to handle user click actions(only for flexible update)

 @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        super.onActivityResult(requestCode, resultCode, data);
if (requestCode == RC_APP_UPDATE) {
            //when user clicks update button
            if (resultCode == RESULT_OK) {
                Toast.makeText(MainActivity.this, "App download starts...", Toast.LENGTH_LONG).show();
            } else if (resultCode != RESULT_CANCELED) {
                //if you want to request the update again just call checkUpdate()
                Toast.makeText(MainActivity.this, "App download canceled.", Toast.LENGTH_LONG).show();
            } else if (resultCode == RESULT_IN_APP_UPDATE_FAILED) {
                Toast.makeText(MainActivity.this, "App download failed.", Toast.LENGTH_LONG).show();
            }
        }
}

Step 7: Create a method to check update available or not and start the update (Immediate update)

private void inAppUpdate() {

        try {
            // Checks that the platform will allow the specified type of update.
            appUpdateInfoTask.addOnSuccessListener(new OnSuccessListener<AppUpdateInfo>() {
                @Override
                public void onSuccess(AppUpdateInfo appUpdateInfo) {
                    if (appUpdateInfo.updateAvailability() == UpdateAvailability.UPDATE_AVAILABLE
                            // For a flexible update, use AppUpdateType.FLEXIBLE
                            && appUpdateInfo.isUpdateTypeAllowed(inAppUpdateType)) {
                        // Request the update.

                        try {
                            mAppUpdateManager.startUpdateFlowForResult(
                                    // Pass the intent that is returned by 'getAppUpdateInfo()'.
                                    appUpdateInfo,
                                    // Or 'AppUpdateType.FLEXIBLE' for flexible updates.
                                    inAppUpdateType,
                                    // The current activity making the update request.
                                    MainActivity.this,
                                    // Include a request code to later monitor this update request.
                                    RC_APP_UPDATE);
                        } catch (IntentSender.SendIntentException ignored) {

                        }
                    }
                }
            });
        } catch (Exception e) {
            e.printStackTrace();
        }

    }

Step 8: Finely create a snack dialog or any alert to show the user that flexible update is downloaded and ready to update(need used action to start update - only for flexible update)

private void popupSnackbarForCompleteUpdate() {
        try {
        Snackbar snackbar =
            Snackbar.make(
                    findViewById(R.id.id_of_root_loyout),
                    "An update has just been downloaded.\nRestart to update",
                    Snackbar.LENGTH_INDEFINITE);

    snackbar.setAction("INSTALL", view -> {
        if (mAppUpdateManager != null){
            mAppUpdateManager.completeUpdate();
        }
    });
snackbar.setActionTextColor(getResources().getColor(R.color.install_color));
    snackbar.show();

        } catch (Resources.NotFoundException e) {
            e.printStackTrace();
        }
    } 

Шаг 9: Теперь вызовите метод с типом обновления в приложении (гибкое или немедленное) в любом месте, где вы хотите начать проверку обновлений.

//For Immediate 
inAppUpdateType = AppUpdateType.IMMEDIATE; //1
inAppUpdate();

//For Flexible 
inAppUpdateType = AppUpdateType.FLEXIBLE; //0
inAppUpdate();

Что следует помнить:

  1. Гибкое обновление будет загружено первым, затем оно уведомит пользователя о завершении загрузки, после чего пользователь должен начать обновление (параметры, указанные выше в шаге 8).

  2. В консоли Google Play есть возможность протестировать совместное использование в приложении, просто мы можем загрузить обычный apk(без подписи apk) для тестирования. https://support.google.com/googleplay/android-developer/answer/9303479?hl=en

  3. Необходимо включить опцию совместного использования в приложении в приложении Play Store для тестового устройства. Как включить общий доступ к приложениям для Android?

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

Попробуйте эти библиотеки, которые можно реализовать в несколько строк кода.

https://github.com/SanojPunchihewa/InAppUpdater

https://github.com/dnKaratzas/android-inapp-update

Я предполагаю, что оно контролируется самим приложением, а не Google Play. Я разработал приложения, которые делают вызов API при запуске, чтобы прочитать "последний" номер версии и определить, является ли эта версия "обязательным" обновлением или нет, и сравнить его с версией работающего приложения. Если доступна новая версия, пользователю предоставляется диалоговое окно, подобное тому, которое вы отобразили (хотя у него гораздо приятнее), предупреждающее пользователя о наличии обновления. Если обновление является "обязательным", то в сообщении говорится, что они должны обновить приложение, прежде чем продолжить. Если они нажимают "Обновить", они перенаправляются на страницу App Store, где они обычно запускают загрузку обновления и приложение закрывается. Если они нажимают Закрыть, приложение просто закрывается. Если обновление не является обязательным, их спрашивают, хотели бы они обновить сейчас или продолжить. Если они нажимают "Обновить", они перенаправляются на страницу App Store, где они обычно запускают загрузку обновления и приложение закрывается. Если они нажимают "Продолжить", то они просто включаются в существующую версию приложения.

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

Google тестирует раннюю версию API обновления в приложениях, как описано в этом сообщении в блоге.

Он доступен только для некоторых партнеров по раннему тестированию прямо сейчас, но в конечном итоге он должен быть доступен для всех разработчиков. Следите за блогом разработчиков Android и за объявлениями в консоли Play.

Другие вопросы по тегам