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

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

enter image description here

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

enter image description here

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

41
pratiked 1 Май 2019 в 20:04

5 ответов

Лучший ответ

Официальная документация: https://developer.android .com / руководство / приложение -расслоение / в приложении - обновлениях

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

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

dependencies {

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

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

mAppUpdateManager = AppUpdateManagerFactory.create(this);

mAppUpdateManager.registerListener(installStateUpdatedListener);

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

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

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

Для тестирования вы можете использовать FakeAppUpdateManager

https://developer.android.com/reference/com/google/android/play/core/appupdate/testing/FakeAppUpdateManager.html

24
Pratik ED 4 Фев 2020 в 10:23

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

< Сильный > Update : Обработка НЕМЕДЛЕННЫХ и ГИБКИХ обновлений в одном действии; Котлин кстати.

import android.app.Activity
import android.content.Intent
import android.content.IntentSender
import android.os.Bundle
import android.widget.Toast
import androidx.appcompat.app.AppCompatActivity
import androidx.core.content.ContextCompat
import com.google.android.material.snackbar.Snackbar
import com.google.android.play.core.appupdate.AppUpdateManager
import com.google.android.play.core.appupdate.AppUpdateManagerFactory
import com.google.android.play.core.install.InstallState
import com.google.android.play.core.install.InstallStateUpdatedListener
import com.google.android.play.core.install.model.AppUpdateType
import com.google.android.play.core.install.model.InstallStatus
import com.google.android.play.core.install.model.UpdateAvailability
import timber.log.Timber

class BaseUpdateCheckActivity : AppCompatActivity() {

    private val appUpdateManager: AppUpdateManager by lazy { AppUpdateManagerFactory.create(this) }
    private val appUpdatedListener: InstallStateUpdatedListener by lazy {
        object : InstallStateUpdatedListener {
            override fun onStateUpdate(installState: InstallState) {
                when {
                    installState.installStatus() == InstallStatus.DOWNLOADED -> popupSnackbarForCompleteUpdate()
                    installState.installStatus() == InstallStatus.INSTALLED -> appUpdateManager.unregisterListener(this)
                    else -> Timber.d("InstallStateUpdatedListener: state: %s", installState.installStatus())
                }
            }
        }
    }

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.main_ad_view)
        checkForAppUpdate()
    }

    private fun checkForAppUpdate() {
        // 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) {
                // Request the update.
                try {
                    val installType = when {
                        appUpdateInfo.isUpdateTypeAllowed(AppUpdateType.FLEXIBLE) -> AppUpdateType.FLEXIBLE
                        appUpdateInfo.isUpdateTypeAllowed(AppUpdateType.IMMEDIATE) -> AppUpdateType.IMMEDIATE
                        else -> null
                    }
                    if (installType == AppUpdateType.FLEXIBLE) appUpdateManager.registerListener(appUpdatedListener)

                    appUpdateManager.startUpdateFlowForResult(
                            appUpdateInfo,
                            installType!!,
                            this,
                            APP_UPDATE_REQUEST_CODE)
                } catch (e: IntentSender.SendIntentException) {
                    e.printStackTrace()
                }
            }
        }
    }


    override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
        super.onActivityResult(requestCode, resultCode, data)
        if (requestCode == APP_UPDATE_REQUEST_CODE) {
            if (resultCode != Activity.RESULT_OK) {
                Toast.makeText(this,
                        "App Update failed, please try again on the next app launch.",
                        Toast.LENGTH_SHORT)
                        .show()
            }
        }
    }

    private fun popupSnackbarForCompleteUpdate() {
        val snackbar = Snackbar.make(
                findViewById(R.id.drawer_layout),
                "An update has just been downloaded.",
                Snackbar.LENGTH_INDEFINITE)
        snackbar.setAction("RESTART") { appUpdateManager.completeUpdate() }
        snackbar.setActionTextColor(ContextCompat.getColor(this, R.color.accent))
        snackbar.show()
    }


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

                    //Check if Immediate update is required
                    try {
                        if (appUpdateInfo.updateAvailability() == UpdateAvailability.DEVELOPER_TRIGGERED_UPDATE_IN_PROGRESS) {
                            // If an in-app update is already running, resume the update.
                            appUpdateManager.startUpdateFlowForResult(
                                    appUpdateInfo,
                                    AppUpdateType.IMMEDIATE,
                                    this,
                                    APP_UPDATE_REQUEST_CODE)
                        }
                    } catch (e: IntentSender.SendIntentException) {
                        e.printStackTrace()
                    }
                }
    }

    companion object {
        private const val APP_UPDATE_REQUEST_CODE = 1991
    }
}

Источник: https://gist.github.com/saikiran91/6788ad4d00edca30dad3f51aa47a4c5

14
Sai 24 Авг 2019 в 12:22

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

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

0
Michael Dougan 1 Май 2019 в 17:19

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

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

0
Nick Fortescue 3 Май 2019 в 06:13

Пытаясь реализовать это, официальная Документация 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();
                            }
                        }
                    });
}
8
Michael Dougan 15 Май 2019 в 17:29