Keeping your app up-to-date on your users’ devices enables them to try new
features, as well as benefit from performance improvements and bug fixes.
Although some users enable background updates when their device is connected to
an unmetered connection, other users may need to be reminded to update. In-app
updates is a Play Core library feature that introduces a new request flow to
prompt active users to update your app.
Add this in
In-app updates work only with devices running Android 5.0 (API level 21) or
higher, and requires you to use Play Core
library 1.5.0 or higher. After
meeting these requirements, your app can support the following UX for in-app
updates:
You can monitor the state of an update in progress by registering a listener to install status updates.
The result contains the updated availability status. If an update is available and the update is allowed, the returned
If an in-app update is already in progress, the result will also report the status of the in-progress update.
Each
The type of update you request determines the next steps you need to take. To learn more, read the section about how to either Handle an immediate update or Handle a flexible update.
Unlike an immediate update, Google Play does not trigger an app restart for you. That’s because, during a flexible update, the user has an expectation to keep using the app until they decide that they want to install the update.
So, it’s recommended that you provide a notification (or some other UI indication) that informs the user that installation is ready and requests user confirmation to restart the app.
For example, you can implement a snackbar with Material Design requests confirmation from the user to restart the app, as shown in figure 1.
The following code sample shows a snackbar notification to the user after a flexible update is downloaded.
When you call
If you instead call
When the user brings your app to the foreground, it’s recommended that you check that your app doesn’t have an update waiting to be installed. If your app has an update in the
However, when your app returns to the foreground, you should confirm that the an update is not stalled in the
The following describes the different values you may receive from the
Add this in
dependencies{ implementation 'com.google.android.play:core:1.6.4' }
- Flexible
- Immediate
Flexible
A user experience that provides background download and
installation with graceful state monitoring. This UX is appropriate when
it’s acceptable for the user to use the app while downloading the update. For
example, you want to urge users to try a new feature that’s not critical to
the core functionality of your app.
Immediate
A full-screen user experience that requires the user to
update and restart the app in order to continue using the app. This UX is
best for cases where an update is critical for continued use of the app.
After a user accepts an immediate update, Google Play handles the update
installation and app restart.
First, create an instance of AppUpdateManager and listener for update state:
Check for update availability
Before requesting an update, you need to first check if one is available for your app. To check for an update, useAppUpdateManager
,
as shown below:// Creates instance of the manager. private val appUpdateManager: AppUpdateManager = AppUpdateManagerFactory.create(this) // Returns an intent object that you use to check for an update. private val appUpdateInfoTask = appUpdateManager.appUpdateInfo private val MY_REQUEST_CODE = 1000 private lateinit var listener: InstallStateUpdatedListener
override fun onResume() { super.onResume() checkAppUpdate() }
Start an update
After checking that you are able to update the app, you can request an update usingAppUpdateManager.startUpdateFlowForResult()
,
as shown below. However, you should be mindful how often you request updates to
avoid annoying or tiring your users. That is, you should limit requesting
in-app updates to only the changes that are important to the functionality of
your app.You can monitor the state of an update in progress by registering a listener to install status updates.
private fun checkAppUpdate() { // Create a listener to track request state updates. listener = InstallStateUpdatedListener { if (it.installStatus() == InstallStatus.DOWNLOADED) { // After the update is downloaded, show a notification // and request user confirmation to restart the app. popupSnackBarForCompleteUpdate() } } // Before starting an update, register a listener for updates. appUpdateManager.registerListener(listener) // Start an update. // Checks that the platform will allow the specified type of update. appUpdateInfoTask.addOnSuccessListener { appUpdateInfo -> if ((appUpdateInfo.updateAvailability() == UpdateAvailability.UPDATE_AVAILABLE || appUpdateInfo.updateAvailability() == UpdateAvailability.DEVELOPER_TRIGGERED_UPDATE_IN_PROGRESS) // For a flexible update, use AppUpdateType.FLEXIBLE && appUpdateInfo.isUpdateTypeAllowed(IMMEDIATE)) { // Request the update. requestUpdate(appUpdateInfo) } } }
The result contains the updated availability status. If an update is available and the update is allowed, the returned
AppUpdateInfo
also contains an intent to start the update. See the next section for how to
start the update.If an in-app update is already in progress, the result will also report the status of the in-progress update.
Each
AppUpdateInfo
an instance can be used to start an update only once.
To retry the update in case of failure, you need to request a new
AppUpdateInfo
and check again that the update is available and allowed.The type of update you request determines the next steps you need to take. To learn more, read the section about how to either Handle an immediate update or Handle a flexible update.
Install a flexible update
If you monitor the flexible update state and you detect theInstallStatus.DOWNLOADED
state, you need to restart the app to install the
update.Unlike an immediate update, Google Play does not trigger an app restart for you. That’s because, during a flexible update, the user has an expectation to keep using the app until they decide that they want to install the update.
So, it’s recommended that you provide a notification (or some other UI indication) that informs the user that installation is ready and requests user confirmation to restart the app.
For example, you can implement a snackbar with Material Design requests confirmation from the user to restart the app, as shown in figure 1.
The following code sample shows a snackbar notification to the user after a flexible update is downloaded.
When you call
appUpdateManager.completeUpdate()
in the foreground, the platform displays a full-screen UI that restarts the app
in the background. After the platform installs the update, the app restarts into
its main activity.If you instead call
appUpdateManager.completeUpdate()
in the background, the
the update is installed silently without obscuring the device UI.When the user brings your app to the foreground, it’s recommended that you check that your app doesn’t have an update waiting to be installed. If your app has an update in the
DOWNLOADED
state, show the notification to request that the user
install the update, as shown below. Otherwise, the update data continues to
occupy the user’s device storage.// 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() } } }
Handle an immediate update
If you are performing an immediate update, and the user consents to install the update, Google Play displays the update progress on top of your app's UI across the entire duration of the update. During the update, if the user closes or terminates your app, the update should continue to download and install in the background without additional user confirmation.However, when your app returns to the foreground, you should confirm that the an update is not stalled in the
UpdateAvailability.DEVELOPER_TRIGGERED_UPDATE_IN_PROGRESS
state. If the update is stalled in this state, resume the update, as shown below:// 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. manager.startUpdateFlowForResult( appUpdateInfo, IMMEDIATE, this, MY_REQUEST_CODE ); } } }
Request for App Update
private fun requestUpdate(appUpdateInfo: AppUpdateInfo) { appUpdateManager.startUpdateFlowForResult( // Pass the intent that is returned by 'getAppUpdateInfo()'. appUpdateInfo, // Or 'AppUpdateType.FLEXIBLE' for flexible updates. IMMEDIATE, // The current activity making the update request. this, // Include a request code to later monitor this update request. MY_REQUEST_CODE) }
SnackBar for showing the completion of update download and restart the app action
/* Displays the snackBar notification and call to action. */ private fun popupSnackBarForCompleteUpdate() { Snackbar.make(findViewById(R.id.frmFragmentContainer), "An update has just been downloaded.", Snackbar.LENGTH_INDEFINITE ).apply { setAction("RESTART") { appUpdateManager.completeUpdate() } setActionTextColor(ContextCompat.getColor(this@LockerActivity, R.color.orange_bg)) show() } }
Get a callback for update status
After starting an update, you can use anonActivityResult()
callback to handle an update failure or cancellation, as shown below.override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { super.onActivityResult(requestCode, resultCode, data) if (requestCode == MY_REQUEST_CODE) { if (resultCode != RESULT_OK) { log("Update flow failed! Result code: $resultCode") // If the update is cancelled or fails, // you can request to start the update again. } } }
The following describes the different values you may receive from the
onActivityResult()
callback:RESULT_OK
: The user has accepted the update. For immediate updates, you might not receive this callback because the update should already be completed by Google Play by the time the control is given back to your app.RESULT_CANCELED
: The user has denied or canceled the update.ActivityResult.RESULT_IN_APP_UPDATE_FAILED
: Some other error prevented either the user from providing consent or the update to proceed.
onDestroy() unregister the app update progress listener
override fun onDestroy() { super.onDestroy() // When status updates are no longer needed, unregister the listener. appUpdateManager.unregisterListener(listener) }
Troubleshoot
This section describes some possible solutions to situations where in-app updates might not work as expected during testing.- In-app updates are available only to user accounts that own the app. So, make sure the account you’re using has downloaded your app from Google Play at least once before using the account to test in-app updates.
- Make sure that the app that you are testing in-app updates with has the same application ID and is signed with the same signing key as the one available from Google Play.
- Because Google Play can only update an app to a higher version code, make sure the app you are testing as a lower version code than the update version code.
- Make sure the account is eligible and the Google Play cache is
up to date. To do so, while logged into the Google Play Store account on the
test device, proceed as follows:
- Make sure you completely close the Google Play Store App.
- Open the Google Play Store app and go to the My Apps & Games tab.
- If the app you are testing doesn’t appear with an available update, check that you’ve properly set up your testing tracks.
Comments
Post a Comment