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. developer.android.com

Google announced in-app update feature at Google I/0 2019. The related implementation guide contains enough clues on how to implement both FLEXIBLE and IMMEDIATE update ‘flavors’. However the suggested approach may not work perfect especially in multi-activity applications. In this article I’ll try to highlight some key points of implementing in-app update in a complex real-world app and introduce a small library born as a result of these efforts.

motorro/AppUpdateWrapper A wrapper for Android AppUpdateManager to simplify in-app update flow. A complete lifecycle-aware component to take a... github.com

The focus of the story will be on a FLEXIBLE flow as carrying out a background update task requires considerably greater effort than IMMEDIATE flow.

The article assumes you already have some experience in implementing in-app update for Android. Otherwise please check out the official implementation guide or some introductory story like this one:

Exploring in-app updates on Android I'm sure there has often been a time when you've needed to send out an app update that has some form of urgency - maybe... medium.com

Managing a flow state

Two states of update flow

The FLEXIBLE flow provides background update checking and downloading while user continues to interact with application UI. AppUpdateManagerchecks for update for you and updates you with state changes while update is being downloaded. Starting an update check in your activity onResumehandler seems to be a good point to start the flow so the UI interaction takes place only at the topmost activity. If update is available you start the update flow and AppUpdateManager pops up a new activity-for-result to confirm update:

Download consent window

Suppose, user confirms update, update manager starts download… So far so good just like in an official guide.

But what if user cancels? The onActivityResult handler will receive RESULT_CANCELED result code. Having update check in subsequent onResume handler will bring the consent screen back unless we implement some special processing and memoing of user interaction.

What we have here is AppUpdateManager is handling a ‘system’ update statebut the burden of the update UI flow lays on the developer. And things become trickier if you add multi-activity setup, screen rotation, a telephone call that interrupts application flow, you name it… Plus you need to get a correct state of update if your activity has started while say a download is underway.

Designed for single activity application

Photo by Sasha Freemind on Unsplash

From what I’ve learned while trying to plug-in flexible flow in our main application it seems the AppUpdateManager is designed (and tested) for a single-activity design.

Some effort in lifecycle management required to make multi-activity application to start download in one activity and to complete update in another.

I haven’t managed to make several instances of AppUpdateManager work along each other in an application with several activities. Have to use a singleton instance which is an obvious solution but…

AppUpdateManager fails to handle more than one active InstallStateUpdatedListener. If they try to unsubscribe within event dispatch manager crashes with ConcurrentModificationException. Take a look at this test:

Crashes when unsubscribing

AppUpdateWrapper to the rescue!

Photo by dylan nolte on Unsplash

All in all, after some considerable time of trial and error spent I’ve come to the solution which IMO simplifies most of the update flow implementation in your application.

Here is how the minimum activity setup looks like with AppUpdateWrapperat your side:

Minimum activity setup for update flow

Features

You only need to implement a single AppUpdateView interface with minimum of three required methods to run both IMMEDIATE and FLEXIBLE flows. You may implement it in your activity as shown above or delegate to any view component of your choice.

The AppUpdateWrapper is a lifecycle-aware component designed as a plugin.

Works nice across multiple activities including a workaround to crashes with event listeners.

Under the hood based on a ‘state-machine’ pattern for clean and testable code to test each key point.

Bonus

As a bonus the FLEXIBLE flow of a library offers a built-in solution to postpone subsequent update prompts if user has already canceled the update:

The library implements couple of basic use-cases: