In App Updates API by Google for Android — why and when it’s going to be useful, how to properly implement.

Siva Ganesh Kantamani
Programming Geeks

--

In app updates feature is one of the most awaited feature for android developers. Auto update feature in play store is enabled still most of the users aren’t updating to latest versions(they are but it was taking weeks, in some cases months), by then damage might be done for example, for all of us there is a time when we release a version and find out there is an issue which need to be fixed immediately or when you try a new feature in your app but most of the users haven’t updated to that version in weeks, which results in wrong metrics to track weather the feature is success.

Now that we know why to use in app updates API, developers let’s focus on how to do it.

Things to know before starting

Google being Google in 2019 IO released stable version of most useful and very easy to implement API called In App Updates API which is a part of play core library(≥ 1.5.0) and as always it’s backward compatible till Android 5.0 ( API level 21).

Overview

With In App Updates API integrated in your code google provide two types of update requests with materialistic design which seamlessly connect to your app UI.

Flexible: A user experience that provides background download of the update and at the same time no interruption on seamless use of the app. Flexible update is most useful when you integrated a new feature which is not core to your app.

Immediate: This is the flow where a blocking UI is prompted by google until the update is download and installed. Immediate update is most useful when there is bug in the production version.

However Android allows you to handle any of the two update types for every version of your app, so it’s up to you how to handle.

Let’s begin the coding part

Checking for updates:

Before getting started with either of these methods we should first check for an update is available. For that we use AppUpdateManager as show below

val updateManager = AppUpdateManagerFactory.create(this)
updateManager.appUpdateInfo.addOnSuccessListener {
if (it.updateAvailability() == UpdateAvailability.UPDATE_AVAILABLE) {
}
}

As shown in the above peace of code we start by creating an instance for AppUpdateManager class which contains AppUpdateInfo that returns an intent object that we use to check for an update. As we see in the code the object which is returned by AppUpdateInfo contains a method updateAvailability() which returns one the following

  • UNKNOWN
  • UPDATE_AVAILABLE
  • UPDATE_NOT_AVAILABLE
  • DEVELOPER_TRIGGERED_UPDATE_IN_PROGRESS

UPDATE_AVAILABLE: If this is the result then we can proced with step update request.

UPDATE_NOT_AVAILABLE: Your app is update to date, none of the following steps is required.

DEVELOPER_TRIGGERED_UPDATE_IN_PROGRESS: The update is already in progress which means you have requested for the update at-least once.

After checking that you are able to update the app, you can request an update using AppUpdateManager.startUpdateFlowForResult(), as shown below. However, you should be mindful how often you request updates to avoid annoying your users.

Requirements for startUpdateFlowForResult():

We need to pass AppUpdateInfo which we previously retrieved from AppUpdateManager , AppUpdateType (IMMEDIATE OR FLEXIBLE), Context of the present component, MY_REQUEST_CODE to monitor result from update request.

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

Calling this startUpdateFlowForResult() method will trigger a startActivityForResult() call and start the app update flow. In some cases, the request may be cancelled by the user (ActivityResult.RESULT_CANCELLED), or even fail (ActivityResult.RESULT_IN_APP_UPDATE_FAILED). In these cases we can catch the result in the onActivityResult() of our activity / fragment and handle the result accordingly.

Immediate Update:

The purpose of it is explained above and now we deal with logic & coding part

Immediate In-app Updates can be trigged by AppUpdateType.IMMEDIATE as mentioned previously, this will show a materialistic blocking UI by Google during the entire update time means downloading and installing the update.

Immediate Update

For this, we begin by launching the app update flow for a IMMEDIATE update:

updateManager.startUpdateFlowForResult(
appUpdateInfo,
AppUpdateType.IMMEDIATE,
this,
REQUEST_CODE_UPDATE)

If the user leaves the app before completion still update will be downloaded and installed in the background meanwhile if the user entered the app then developer should ensure that update is in progress. For this we need to check whether or not the updateAvailability() returns the DEVELOPER_TRIGGERED_UPDATE_IN_PROGRESS state. If so then we need to trigger the update flow so that the update process can be resumed. If you do not implement this part of the flow then the user will be able to continue using your application without the immediate update in-effect.

override fun onResume() {
super.onResume()
val updateManager = AppUpdateManagerFactory.create(this)
updateManager.appUpdateInfo
.addOnSuccessListener {
if (it.updateAvailability() ==
UpdateAvailability.DEVELOPER_TRIGGERED_UPDATE_IN_PROGRESS) {
updateManager.startUpdateFlowForResult(
it,
IMMEDIATE,
this,
REQUEST_CODE_UPDATE)
}
}
}
}

This is all we need to do for immediate update, system will take care of the things from here.

Flexible Update:

Flexible In-app Updates can be trigged by AppUpdateType.FLEXIBLE as mentioned previously, this will trigger a flow that displays an upgrade pop-up to the user and perform a download / install of the update in the background while the user can continue to use the application.

For this, we begin by launching the app update flow for a FLEXIBLE update:

updateManager.startUpdateFlowForResult(
appUpdateInfo,
AppUpdateType.FLEXIBLE,
this,
REQUEST_CODE_UPDATE)

As mentioned above this is all happening in the background, a little more monitoring is required during the update.For this we’re going to make use of the InstallStateUpdatedListener which will receive callbacks when the state of the flexible install changes. This contains a single callback, onStateUpdated(), which will pass us an instance of the InstallState class. From this we can make use of:

val listener = InstallStateUpdatedListener {
// Handle install state
}
val updateManager = AppUpdateManagerFactory.create(this)
updateManager.registerListener(listener)
...updateManager.unregisterListener(listener)

installStatus() — returns us a InstallStatus value that represents the current state of the update. This can be one of:

  • UNKNOWN
  • REQUIRES_UI_INTENT
  • PENDING
  • DOWNLOADING
  • DOWNLOADED
  • INSTALLING
  • INSTALLED
  • FAILED
  • CANCELLED

installErrorCode() — returns us an InstallErrorCode that represents the error state of the install

  • NO_ERROR
  • NO_ERROR_PARTIALLY_ALLOWED
  • ERROR_UNKOWN
  • ERROR_API_NOT_AVAILABLE
  • ERROR_INVALID_REQUEST
  • ERROR_INSTALL_UNAVAILABLE
  • ERROR_INSTALL_NOT_ALLOWED
  • ERROR_DOWNLOAD_NOT_PRESENT
  • ERROR_INTERNAL_ERROR

With these many options means, more flexibility during the update for example you can allow user to continue interact with the app throught the process and ask user to restart the app to complete the update on receiving DOWNLOADED state or developer can show appropriate UI based on the states in the listener.

To complete the update, when user allow to restart the app we can use the following method, which will complete the update and restart the app.

appUpdateManager.completeUpdate()

If the update is carried out when the app is in the foreground then there is a chance that the user will leave and return to our app before the update has been downloaded and installed. In this case, when our activity hit onResume() we’re going to want to check the status from our AppUpdateManager so that we can determine if we need to complete the update process. We can determine this by checking if the InstallStatus is in a DOWNLOADED state. If so, we can go ahead and call the completeUpdate() method to finish the update process.

updateManager.appUpdateInfo
.addOnSuccessListener {appUpdateInfo ->
if (appUpdateInfo.installStatus() == InstallStatus.DOWNLOADED) {
updateManager.completeUpdate()
}
}

From this article we learn about the new approach for in app updates through which we don’t need to put much effort.

If you have any difficulty in implementing or anything I have missed in the process, please let me know in the comments below.

If you like my article, please don’t forget to click 👏👏👏 & to recommend it to others 👏👏👏.

Also, to be notified about my next articles and stories, follow me on Medium.

--

--