In-App Update in Android 2023

Kaushal Vasava
9 min readNov 19, 2023

--

I will show you how to implement in-app update in this article.

Introduction

In this article, we will learn about the In-app update feature in Android what is all about In-app update, what are the benefits of using the In-app update in your android application. Why dowe need to Implement this?

As a Developer we always want our users to have the updated version of their application but there are a lot of people who actually turned off their auto update from google play store and he/she doesn’t know about any update available or not.

To overcome the problem Google Introduced this feature called In-app update from this feature you can easily prompt the user to update the application and with the user permission you can update the application also while updating the app user can be able to interact with the application. Now the user doesn’t need to go to google play store to check there is any update available or not.

When your users keep your app up to date on their devices, they can 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 might need to be reminded to install updates. In-app updates is a Google Play Core libraries feature that prompts active users to update your app.

The in-app updates feature is supported on devices running Android 5.0 (API level 21) or higher. Additionally, in-app updates are only supported for Android mobile devices, Android tablets, and ChromeOS devices.

Update flows

Your app can use the Google Play Core libraries to support the following UX flows for in-app updates:

There are two modes of an In-app update.

  • Flexible Update
  • Immediate Update

Flexible updates

Flexible updates provide background download and installation with graceful state monitoring. This UX flow is appropriate when it’s acceptable for the user to use the app while downloading the update. For example, you might want to encourage users to try a new feature that’s not critical to the core functionality of your app.

An example of a flexible update flow.

Immediate updates

Immediate updates are fullscreen UX flows that require the user to update and restart the app in order to continue using it. This UX flow is best for cases where an update is critical to the core functionality of your app. After a user accepts an immediate update, Google Play handles the update installation and app restart.

An example of an immediate update flow.

Implementation:

Let’s start with the In-App update implementation, what we need to do first.

Step 1: Add Dependency

Add the Google App UpdateDependency to your android build.gradle file, like the below code snippet.

// So, make sure you also include that repository in your project's build.gradle file.
implementation("com.google.android.play:app-update:2.0.1")
// For Kotlin users also import the Kotlin extensions library for Play In-App Update:
implementation("com.google.android.play:app-update-ktx:2.0.1")

Step 2: Create an AppManager Instance

We need to create an instance of the AppManager to get the in-app update info, you can define this in your OnCreate method like below code snippet:

// Creates instance of the manager in your onCreate() of Activity or onCreateView() of Fragment Method 
private val appUpdateManager: AppUpdateManager? = AppUpdateManagerFactory.create(this)

Step 3: Check for the Update

Before requesting an update, check if there is an update available for your app. Use AppUpdateManager to check for an update:

private fun checkUpdate() {
val appUpdateInfoTask = appUpdateManager?.appUpdateInfo
appUpdateInfoTask.addOnSuccessListener { appUpdateInfo ->
// This example applies an flexible update. To apply a immediate update
// instead, pass in AppUpdateType.IMMEDIATE
if (appUpdateInfo.updateAvailability() == UpdateAvailability.UPDATE_AVAILABLE
&& appUpdateInfo.isUpdateTypeAllowed(AppUpdateType.FLEXIBLE)
) {
// Request the update
} else {
Log.d(TAG, "No Update available")
}
}
}

In the above code, I created a function with the name checkUpdate() in which I first get the app update information using the appUpdateManager instance which I was created earlier, in step 2 & assigned to another variable called appUpdateInfoTask now everything seems perfectly going good, and now we have the info available we just need to add a listener to check whether the new update is available or not.

In addOnSuccessListener we need to first check If update available or not with the function updateAvailability() this will return the Boolean value, and also set the mode Flexible | Immediate. If both the condition meets then we have the update available and we are good to go for the update request.

The returned AppUpdateInfo instance contains the update availability status. Depending on the status of the update, the instance also contains the following:

  • If an update is available and the update is allowed, the instance also contains an intent to start the update.
  • If an in-app update is already in progress, the instance also reports the status of the in-progress update.

Step 4. Define Launcher to get a callback for update status

After starting an update, registered activity result launcher callback gets the confirmation dialog result:

private val updateLauncher = registerForActivityResult(
ActivityResultContracts.StartIntentSenderForResult()
) { result ->
// handle callback
if (result.data == null) return@registerForActivityResult
if (result.resultCode == UPDATE_REQUEST_CODE) {
Toast.makeText(context, "Downloading stated", Toast.LENGTH_SHORT).show()
if (result.resultCode != Activity.RESULT_OK) {
Toast.makeText(context, "Downloading failed" , Toast.LENGTH_SHORT).show()
}
}

There are several values you might 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 finished by the time 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 from proceeding.

Step 5: Request for the Update

In Step 5, we will learn how to request for the update, To update the request we need to use AppUpdateManager.startUpdateFlowForResult() like the below code snippet:

try {
appUpdateManager?.startUpdateFlowForResult(
// Pass the intent that is returned by 'getAppUpdateInfo()'.
appUpdateInfo,
// an activity result launcher registered via registerForActivityResult
updateResultStarter,
//pass 'AppUpdateType.FLEXIBLE' to newBuilder() for
// flexible updates.
AppUpdateOptions.newBuilder(AppUpdateType.FLEXIBLE).build(),
// Include a request code to later monitor this update request.
UPDATE_REQUEST_CODE
)
} catch (exception: IntentSender.SendIntentException) {
Toast.makeText(context, "Something wrong went wrong!", Toast.LENGTH_SHORT).show()
}

Define IntentSenderForResultStarter

private val updateResultStarter =
IntentSenderForResultStarter { intent, _, fillInIntent, flagsMask, flagsValues, _, _ ->
val request = IntentSenderRequest.Builder(intent)
.setFillInIntent(fillInIntent)
.setFlags(flagsValues, flagsMask)
.build()
// launch updateLauncher
updateLauncher.launch(request)
}

Step 6: Monitor the flexible update state

After the download begins for a flexible update, your app needs to monitor the update state to know when the update can be installed and to display the progress in your app’s UI.

You can monitor the state of an update in progress by registering a listener for install status updates. You can also provide a progress bar in the app’s UI to inform users of the download’s progress.

// Create a listener to track request state updates.
val listener = InstallStateUpdatedListener { state ->
// (Optional) Provide a download progress bar.
if (state.installStatus() == InstallStatus.DOWNLOADING) {
val bytesDownloaded = state.bytesDownloaded()
val totalBytesToDownload = state.totalBytesToDownload()
// Show update progress bar.
}
if (state.installStatus() == InstallStatus.DOWNLOADED) {
Snackbar.make(
view,
"New app is ready",
Snackbar.LENGTH_INDEFINITE
).setAction("Restart") {
appUpdateManager.completeUpdate()
}.show()
}
// Log state or install the update.
}

After monitoring the update status and you detect the InstallStatus.DOWNLOADED state. Now you need to restart the app to install the update so for that we need to just call completeUpdate() function like the below code snippet:

appUpdateManager.completeUpdate()

And after the complete update, the app will restart and now you are in the new version of the application.

Step 7: Register the Listener

First register the listener before starting the update, like the below code:

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

Step 8: Unregister the Listener

when there is no need longer for the listener then we need to unregister the listener, to avoid the memory leak. like the below code:

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

Note: Monitoring the update state is required for only flexible downloads

Handle an immediate update

When you start an immediate update and the user consents to begin the update, Google Play displays the update progress on top of your app’s UI throughout the entire duration of the update. If the user closes or terminates your app during the update, 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 update is not stalled in the UpdateAvailability.DEVELOPER_TRIGGERED_UPDATE_IN_PROGRESS state. If the update is stalled 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,
activityResultLauncher,
AppUpdateOptions.newBuilder(AppUpdateType.IMMEDIATE).build())
}
}
}

How to Test:

Now the last thing is how to test this, this is a tricky part, the following things for testing you need to be followed.

Test with internal app sharing

Use internal app sharing to test in-app updates by performing the following steps:

  1. Make sure your test device has a version of your app installed that supports in-app updates and was installed using an internal app sharing URL.
  2. Follow the Play Console instructions to share your app internally. Upload a version of your app that uses a version code that is higher than the one you already have installed on the test device.
  3. On the test device, click the internal app sharing link for the updated version of your app but do not install the app from the Play Store page that appears after you click the link.
  4. Open the app from the device’s app drawer or home screen. The update should now be available to your app, and you can test your implementation of in-app updates.

I divided it into two parts.

Part1

  • Generate a signed APK for Production.
  • Go to App-Internal-Sharing and upload the build over there, and share the link with the tester
  • Now install that build with the shareable link.

Part2

  • Generate a signed APK for Production with another version code and version name, make sure the version which you upload earlier is lower than this.
  • Go to App-Internal-Sharing and upload the build over there and share the link with the tester.
  • Now click to that link and don’t click to the Update button.
  • Now just open the application which you installed earlier you will get the update dialog.

Note: Every time you upload the Build make sure you close the Google Play Store App

Troubleshoot

This section describes some possible solutions to situations where in-app updates might not work as expected during testing:

  • In-app updates are only available to user accounts that own the app. Make sure the account that 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 using to test in-app updates has the same application ID and signing key as the version available from Google Play.
  • Google Play can only update an app to a higher version code. Make sure that the app that you are testing has a lower version code than the update version code.
  • The inAppUpdatePriority field is not supported when uploading your app to internal app sharing.

Conclusion

This article taught you how to Implement an in-App update in your android application with the two different modes, one is Flexible and one is Immediate, how you can request again, how to monitor the status of the update, and how to test with the Internal app-sharing.

If you want to read more about, check out the official developer documentation below:

In-app updates | Android Developers

Check this Demo Project with Flexible mode In-App update

Thank you for reading. 🙌🙏✌.

Don’t forget to clap 👏 and follow me for more such useful articles about Android Development, Kotlin & KMP.

If you need any help related to Android, Kotlin and KMP. I’m always happy to help you.

Follow me on:

Medium, LinkedIn, Twitter, GitHub, and Instagram.

--

--

Kaushal Vasava

Android Developer | Kotlin | Jetpack Compose | Kotlin MultiPlatform | LinkedIn's Top Voice (3K+ Followers) | Apps with 100K+ Downloads on the Playstore