Everything About Android’s In-App Update API

Being a developer, I would always want that the user should have latest/updated version of the app. After all, a happy and satisfied user is the measure of a good app. 🙂 But there are some barriers which come between the user and the latest version of the app.

For example, automatic updates may only validate if the device has a Wi-Fi connection and is not cellular network, or the user may have disabled the option. These conditions requires user to go to the Play Store and search for updates.

To ease this burden from the app user, Google has launched the feature called In-App Update API to update the app. Once this is implemented in the app, user will not need to go to Play Store to check whether the update is available or not, this will be handled by the app itself.

In-app update API is a Play Core library feature that introduces a new request flow to update the app. This feature allows user to get updated version of the app if updates are available.

Basically, there are 2 ways for implementing In-app update API:

  1. Flexible Update
  2. Immediate Update

1. Flexible Update:

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 app users to try a new feature that’s not critical to the core functionality of the app.

This way is majorly used when you don’t want to force user to update the app which means user can either update the app or simply ignore/cancel it and continue using current version ( app without update ).

2. Immediate Update (forced app update):

This UX is best when an update is critical for continued use of the app. This is full screen UX which means user cannot use the app when downloading an app update is in progress. After a user accepts an immediate update, Google Play handles the update installation and app restart.

Coding Implementation :

1. First of all, add Play Core library 1.5.0 or higher in build.gradle then we need to check if the update for app is available or not. For doing this, we have AppUpdateManager which manages operations that allow app to initiate its own updates.

// Creates instance of the manager.
val appUpdateManager = AppUpdateManagerFactory.create(context)

// 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 ->
            
            // For a flexible update, use AppUpdateType.FLEXIBLE
            if (appUpdateInfo.updateAvailability() == UpdateAvailability.UPDATE_AVAILABLE && appUpdateInfo.isUpdateTypeAllowed(AppUpdateType.IMMEDIATE)) {
                // Request the update.
            }
        }

The result contains the update availability status.

Update availablity status:

StatusConstant valueDescription
DEVELOPER_TRIGGERED_UPDATE_IN_PROGRESS3An update has been triggered and is in progress.
UNKNOWN0Update status is Unknown
UPDATE_AVAILABLE2Update available
UPDATE_NOT_AVAILABLE1Update not available

2. Next step is to request an update using AppUpdateManager.startUpdateFlowForResult().

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

3. Get callback for update status:

onActivityResult() can be used to handle the update cancellation or failure.

override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent) {
        if (requestCode == MY_REQUEST_CODE) {
            if (resultCode != RESULT_OK) {
                log("Update flow failed! Result code: $resultCode")
            }
        }
    }

Different values you may receive from the onAcivityResult() callback:

  • RESULT_OK : The user has accepted the update.
  • RESULT_CANCELED : The user has denied or cancelled the update.
  • RESULT_IN_APP_UPDATE_FAILED : Some other error prevented either the user from providing consent or the update to proceed.

4. Handling Flexible Update:

A. Monitoring Flexible Update:

As soon as user accepts update, downloading of an update gets started by Google Play in the background. We need to register a listener to monitor the state.

// Create a listener to track request state updates.
val listener = { state ->

    // (Optional) Provide a download progress bar.
    if (state.installStatus() == InstallStatus.DOWNLOADING)
       // Show update progress bar.
    }

    // Log state or install the update.
}

// Before starting an update, register a listener for updates.
appUpdateManager.registerListener(listener)

// Start an update.
// When status updates are no longer needed, unregister the listener.
appUpdateManager.unregisterListener(listener)

B. Installing Flexible Update:

Once you detect the InstallStatus.DOWNLOADED state, you need to restart the app to install the update. So, it’s recommended that you provide a notification (or some other UI indication like snackbar) that informs the user that installation is ready and requests user confirmation to restart the app.

override fun onStateUpdate(state: InstallState) {
    if (state.installStatus() == InstallStatus.DOWNLOADED) {
        // After the update is downloaded, show Snackbar
        // and request user confirmation to restart the app.
        PopupSnackbarForCompleteUpdate()
    }
}
/* Displays the snackbar notification and call to action. */
fun popupSnackbarForCompleteUpdate() {
    Snackbar.make(
            findViewById(R.id.activity_main_layout),
            "An update has just been downloaded.",
            Snackbar.LENGTH_INDEFINITE
    ).apply {
        setAction("RESTART") { appUpdateManager.completeUpdate() }
        setActionTextColor(resources.getColor(R.color.snackbar_action_text_color))
        show()
    }
}

When you call appUpdateManager.completeUpdate() in the foreground, the platform displays a full-screen UI which restart the app in the background. After the platform installs the update, the app restarts into its launcher activity.

5. Handling Immediate Update :

Google Play displays the update progress on top of your app’s UI till the entire duration of the update. During this, if the user closes or terminates the app, the update should continue to download and install in the background without additional user confirmation.

However, when the app returns to the foreground, you should confirm that the update is not stopped in the UpdateAvailability.DEVELOPER_TRIGGERED_UPDATE_IN_PROGRESS state. If the update is stopped in this state, resume the update :

// 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.
                appUpdateManager.startUpdateFlowForResult(appUpdateInfo, IMMEDIATE, this, MY_REQUEST_CODE);
            }
        }
    }

Monitoring the update state is required for only flexible downloads. For immediate updates, Google Play takes care of downloading and installing the update for you.

Points to remember while working with In-app update API:

  • In-app updates works 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.
  • In-app updates are not compatible with apps that use APK expansion files (.obb files).
  • When you publish your app as an Android App Bundle, the maximum allowed compressed download size of an app using in-app updates is 150MB.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

This site uses Akismet to reduce spam. Learn how your comment data is processed.