How to implement in-app update prompt in Android?
Last Updated :
25 May, 2025
Normally to check for an app's update, a user need to open play store, navigate to the app's page and click on check for updates. But, what if we can notify the user as soon as there's an update release for the app on the app's screen itself. That's where In-App Updates comes into motion. In-app updates is a Google Play Core libraries feature that prompts the user to update their app immediately when the user opens it.
Types of In-App Updates:
There are two types of updates flows:
- Flexible Updates: The user can continue using the app while the update downloads in the background. Once downloaded, the app prompts for a restart.
- Immediate Update: A full-screen update prompt forces the user to update before continuing.
Step by Step Implementation
Step 1: Create a new project
To create a new project in the Android Studio, please refer to How to Create/Start a New Project in Android Studio?
Step 2: Add dependencies for in-app updates
Navigate to Gradle Scripts > build.gradle.kts (Module: app) and add the following dependencies under the dependencies{} scope.
dependencies {
...
implementation ("com.google.android.play:app-update:2.1.0")
implementation ("com.google.android.play:app-update-ktx:2.1.0")
}
Step 3: Add view binding support
Navigate to Gradle Scripts > build.gradle.kts (Module: app) and add the following code anywhere under the android{} scope.
android {
...
buildFeatures {
viewBinding = true
}
...
}
Now, select on the Sync prompt on the top-right corner of the code screen.
Step 4: Check for Updates
Navigate to app > java > {package-name} > MainActivity. First, initialize the App Update Manager and Activity Result Launcher
// kotlin
private lateinit var appUpdateManager: AppUpdateManager
private lateinit var activityResultLauncher: ActivityResultLauncher<IntentSenderRequest>
// java
private AppUpdateManager appUpdateManager;
private ActivityResultLauncher<IntentSenderRequest> activityResultLauncher;
Then, create a function to check for app updates and call the function in the overridden onCreate() method.
Java
private void checkForInAppUpdates() {
// Creates an instance of the AppUpdateManager
appUpdateManager = AppUpdateManagerFactory.create(this);
// Registers an activity result launcher to handle the update process result
activityResultLauncher =
registerForActivityResult(new ActivityResultContracts.StartIntentSenderForResult(),
result -> {
if (result.getResultCode() != RESULT_OK) {
// If the update fails, show a Toast message and log the failure
Toast.makeText(this, "Update failed: " + result.getResultCode(), Toast.LENGTH_SHORT).show();
Log.d("Update", "Update flow failed! Result code: " + result.getResultCode());
}
});
// Registers a listener to monitor the update installation process
appUpdateManager.registerListener(listener);
// Fetches the update information
appUpdateManager.getAppUpdateInfo().addOnSuccessListener(appUpdateInfo -> {
// Checks if an update is available and allowed for flexible updates
if (appUpdateInfo.updateAvailability() == UpdateAvailability.UPDATE_AVAILABLE &&
appUpdateInfo.isUpdateTypeAllowed(AppUpdateType.FLEXIBLE)) {
try {
// Requests an in-app update
appUpdateManager.startUpdateFlowForResult(
appUpdateInfo,
activityResultLauncher,
AppUpdateOptions.newBuilder(AppUpdateType.FLEXIBLE).build()
);
} catch (IntentSender.SendIntentException e) {
e.printStackTrace();
}
}
});
}
Kotlin
// checking for in-app updates
private fun checkForInAppUpdates() {
appUpdateManager = AppUpdateManagerFactory.create(this)
activityResultLauncher =
registerForActivityResult(ActivityResultContracts.StartIntentSenderForResult()) { result: ActivityResult ->
if (result.resultCode != RESULT_OK) {
Toast.makeText(this, "Update failed: ${result.resultCode}", Toast.LENGTH_SHORT).show()
Log.d("Update", "Update flow failed! Result code: ${result.resultCode}")
}
}
appUpdateManager.registerListener(listener)
val appUpdateInfoTask = appUpdateManager.appUpdateInfo
appUpdateInfoTask.addOnSuccessListener { appUpdateInfo ->
if (appUpdateInfo.updateAvailability() == UpdateAvailability.UPDATE_AVAILABLE &&
appUpdateInfo.isUpdateTypeAllowed(AppUpdateType.FLEXIBLE)
) {
// requesting for update
appUpdateManager.startUpdateFlowForResult(
appUpdateInfo,
activityResultLauncher,
AppUpdateOptions.newBuilder(AppUpdateType.FLEXIBLE).build()
)
}
}
}
Step 5: Handle Update Completion
Now, you might have noticed we have registered a listener to out app update manager to listen for update complement. This will help us prompting the user to restart the app in order to finish pending installations.
First, create the listener
Java
private final InstallStateUpdatedListener listener = state -> {
// Checks if the update has been downloaded
if (state.installStatus() == InstallStatus.DOWNLOADED) {
// Shows a Snackbar prompting the user to restart the app to complete the update
popupSnackbarForCompleteUpdate();
}
};
Kotlin
private val listener = InstallStateUpdatedListener { state ->
if (state.installStatus() == InstallStatus.DOWNLOADED) {
popupSnackbarForCompleteUpdate()
}
}
Then, create the function for a snackbar prompting to restart the app and finish installation.
Java
private void popupSnackbarForCompleteUpdate() {
Snackbar.make(
binding.getRoot(),
"Update ready to install",
Snackbar.LENGTH_INDEFINITE
).setAction("Restart", v -> appUpdateManager.completeUpdate()).show();
}
Kotlin
private fun popupSnackbarForCompleteUpdate() {
Snackbar.make(
binding.root,
"Update ready to install",
Snackbar.LENGTH_INDEFINITE
).apply {
setAction("Restart") { appUpdateManager.completeUpdate() }
show()
}
}
Step 6: Resume Pending Updates
Now, override the onResume() method to prompt user for the restart since the app will be temporarily paused due to the update. Also, remember to override the onStop() method to unregister the listener.
Java
@Override
protected void onStop() {
super.onStop();
appUpdateManager.unregisterListener(listener);
}
@Override
protected void onResume() {
super.onResume();
appUpdateManager.getAppUpdateInfo()
.addOnSuccessListener(appUpdateInfo -> {
if (appUpdateInfo.installStatus() == InstallStatus.DOWNLOADED) {
popupSnackbarForCompleteUpdate();
}
});
}
Kotlin
override fun onResume() {
super.onResume()
appUpdateManager.appUpdateInfo
.addOnSuccessListener { appUpdateInfo ->
if (appUpdateInfo.installStatus() == InstallStatus.DOWNLOADED) {
popupSnackbarForCompleteUpdate()
}
}
}
override fun onStop() {
super.onStop()
appUpdateManager.unregisterListener(listener)
}
Step 7: Working with MainActivity
Here's the entire code for the MainActivity file.
MainActivity.java
package org.geeksforgeeks.demo;
import android.content.IntentSender;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.Toast;
import androidx.activity.result.ActivityResult;
import androidx.activity.result.ActivityResultLauncher;
import androidx.activity.result.IntentSenderRequest;
import androidx.activity.result.contract.ActivityResultContracts;
import androidx.appcompat.app.AppCompatActivity;
import androidx.core.view.ViewCompat;
import androidx.core.view.WindowInsetsCompat;
import com.google.android.material.snackbar.Snackbar;
import com.google.android.play.core.appupdate.AppUpdateInfo;
import com.google.android.play.core.appupdate.AppUpdateManager;
import com.google.android.play.core.appupdate.AppUpdateManagerFactory;
import com.google.android.play.core.appupdate.AppUpdateOptions;
import com.google.android.play.core.install.InstallStateUpdatedListener;
import com.google.android.play.core.install.model.AppUpdateType;
import com.google.android.play.core.install.model.InstallStatus;
import com.google.android.play.core.install.model.UpdateAvailability;
import org.geeksforgeeks.demo.databinding.ActivityMainBinding;
public class MainActivity extends AppCompatActivity {
// View binding for accessing UI elements
private ActivityMainBinding binding;
// Variables for in-app update functionality
private AppUpdateManager appUpdateManager;
private ActivityResultLauncher<IntentSenderRequest> activityResultLauncher;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
binding = ActivityMainBinding.inflate(getLayoutInflater());
setContentView(binding.getRoot());
ViewCompat.setOnApplyWindowInsetsListener(findViewById(R.id.main), (v, insets) -> {
WindowInsetsCompat systemBars = insets.getInsets(WindowInsetsCompat.Type.systemBars());
v.setPadding(systemBars.left, systemBars.top, systemBars.right, systemBars.bottom);
return insets;
});
// Check for in-app updates
checkForInAppUpdates();
}
// Function to check and handle in-app updates
private void checkForInAppUpdates() {
// Creates an instance of the AppUpdateManager
appUpdateManager = AppUpdateManagerFactory.create(this);
// Registers an activity result launcher to handle the update process result
activityResultLauncher =
registerForActivityResult(new ActivityResultContracts.StartIntentSenderForResult(),
result -> {
if (result.getResultCode() != RESULT_OK) {
// If the update fails, show a Toast message and log the failure
Toast.makeText(this, "Update failed: " + result.getResultCode(), Toast.LENGTH_SHORT).show();
Log.d("Update", "Update flow failed! Result code: " + result.getResultCode());
}
});
// Registers a listener to monitor the update installation process
appUpdateManager.registerListener(listener);
// Fetches the update information
appUpdateManager.getAppUpdateInfo().addOnSuccessListener(appUpdateInfo -> {
// Checks if an update is available and allowed for flexible updates
if (appUpdateInfo.updateAvailability() == UpdateAvailability.UPDATE_AVAILABLE &&
appUpdateInfo.isUpdateTypeAllowed(AppUpdateType.FLEXIBLE)) {
try {
// Requests an in-app update
appUpdateManager.startUpdateFlowForResult(
appUpdateInfo,
activityResultLauncher,
AppUpdateOptions.newBuilder(AppUpdateType.FLEXIBLE).build()
);
} catch (IntentSender.SendIntentException e) {
e.printStackTrace();
}
}
});
}
// Listener to track the state of the update installation process
private final InstallStateUpdatedListener listener = state -> {
// Checks if the update has been downloaded
if (state.installStatus() == InstallStatus.DOWNLOADED) {
// Shows a Snackbar prompting the user to restart the app to complete the update
popupSnackbarForCompleteUpdate();
}
};
// Displays a Snackbar to notify the user that the update is ready to install
private void popupSnackbarForCompleteUpdate() {
Snackbar.make(
binding.getRoot(),
"Update ready to install",
Snackbar.LENGTH_INDEFINITE
).setAction("Restart", v -> appUpdateManager.completeUpdate()).show();
}
// Unregisters the update listener when the app goes into the background
@Override
protected void onStop() {
super.onStop();
appUpdateManager.unregisterListener(listener);
}
// Checks if an update was downloaded while the app was inactive and prompts the user
@Override
protected void onResume() {
super.onResume();
appUpdateManager.getAppUpdateInfo()
.addOnSuccessListener(appUpdateInfo -> {
if (appUpdateInfo.installStatus() == InstallStatus.DOWNLOADED) {
popupSnackbarForCompleteUpdate();
}
});
}
}
MainActivity.kt
package org.geeksforgeeks.demo
import android.os.Bundle
import android.util.Log
import android.widget.Toast
import androidx.activity.enableEdgeToEdge
import androidx.activity.result.ActivityResult
import androidx.activity.result.ActivityResultLauncher
import androidx.activity.result.IntentSenderRequest
import androidx.activity.result.contract.ActivityResultContracts
import androidx.appcompat.app.AppCompatActivity
import androidx.core.view.ViewCompat
import androidx.core.view.WindowInsetsCompat
import com.google.android.material.snackbar.Snackbar
import com.google.android.play.core.appupdate.AppUpdateManager
import com.google.android.play.core.appupdate.AppUpdateManagerFactory
import com.google.android.play.core.appupdate.AppUpdateOptions
import com.google.android.play.core.install.InstallStateUpdatedListener
import com.google.android.play.core.install.model.AppUpdateType
import com.google.android.play.core.install.model.InstallStatus
import com.google.android.play.core.install.model.UpdateAvailability
import org.geeksforgeeks.demo.databinding.ActivityMainBinding
class MainActivity : AppCompatActivity() {
// View binding for accessing UI elements
private val binding: ActivityMainBinding by lazy {
ActivityMainBinding.inflate(layoutInflater)
}
// Variables for in-app update functionality
private lateinit var appUpdateManager: AppUpdateManager
private lateinit var activityResultLauncher: ActivityResultLauncher<IntentSenderRequest>
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
enableEdgeToEdge()
setContentView(binding.root)
ViewCompat.setOnApplyWindowInsetsListener(findViewById(R.id.main)) { v, insets ->
val systemBars = insets.getInsets(WindowInsetsCompat.Type.systemBars())
v.setPadding(systemBars.left, systemBars.top, systemBars.right, systemBars.bottom)
insets
}
// Checks if an in-app update is available
checkForInAppUpdates()
}
// Function to check and handle in-app updates
private fun checkForInAppUpdates() {
// Creates an instance of the AppUpdateManager
appUpdateManager = AppUpdateManagerFactory.create(this)
// Registers an activity result launcher to handle the update process result
activityResultLauncher =
registerForActivityResult(ActivityResultContracts.StartIntentSenderForResult()) { result: ActivityResult ->
if (result.resultCode != RESULT_OK) {
// If the update fails, show a Toast message and log the failure
Toast.makeText(this, "Update failed: ${result.resultCode}", Toast.LENGTH_SHORT).show()
Log.d("Update", "Update flow failed! Result code: ${result.resultCode}")
}
}
// Registers a listener to monitor the update installation process
appUpdateManager.registerListener(listener)
// Fetches the update information
val appUpdateInfoTask = appUpdateManager.appUpdateInfo
appUpdateInfoTask.addOnSuccessListener { appUpdateInfo ->
// Checks if an update is available and allowed for flexible updates
if (appUpdateInfo.updateAvailability() == UpdateAvailability.UPDATE_AVAILABLE &&
appUpdateInfo.isUpdateTypeAllowed(AppUpdateType.FLEXIBLE)
) {
// Requests an in-app update
appUpdateManager.startUpdateFlowForResult(
appUpdateInfo,
activityResultLauncher,
AppUpdateOptions.newBuilder(AppUpdateType.FLEXIBLE).build()
)
}
}
}
// Listener to track the state of the update installation process
private val listener = InstallStateUpdatedListener { state ->
// Checks if the update has been downloaded
if (state.installStatus() == InstallStatus.DOWNLOADED) {
// Shows a Snackbar prompting the user to restart the app to complete the update
popupSnackbarForCompleteUpdate()
}
}
// Displays a Snackbar to notify the user that the update is ready to install
private fun popupSnackbarForCompleteUpdate() {
Snackbar.make(
binding.root,
"Update ready to install",
Snackbar.LENGTH_INDEFINITE
).apply {
setAction("Restart") {
appUpdateManager.completeUpdate()
}
show()
}
}
// Unregisters the update listener when the app goes into the background
override fun onStop() {
super.onStop()
appUpdateManager.unregisterListener(listener)
}
// Checks if an update was downloaded while the app was inactive and prompts the user
override fun onResume() {
super.onResume()
appUpdateManager.appUpdateInfo
.addOnSuccessListener { appUpdateInfo ->
if (appUpdateInfo.installStatus() == InstallStatus.DOWNLOADED) {
popupSnackbarForCompleteUpdate()
}
}
}
}
Similar Reads
How to Implement In-App Purchases in Android? In this article, we'll look at how to use the billing library to make in-app purchases on Android smartphones. We'll create an app as part of the process to help us learn how In-App purchases work. So, without further ado, let's get started. We'll start by creating an app with an Empty Activity as t
4 min read
How to Implement Polling in Android? Many times you may have seen on some apps like youtube, LinkedIn, etc. polling is done and users choose their options whatever they want to choose. Here we are going to implement polling in Android Studio. What we are going to build in this article? In this article, we will ask the user a question a
4 min read
How to Implement Tooltip in Android Studio? A tooltip is a message which appears when a cursor is positioned over an icon, image, hyperlink, or any other GUI component. In this application, we will be using an EditText to take the message from the user and then display that message as a tooltip over a view. Here is a sample video of the appli
4 min read
How to Update Data in API using Retrofit in Android? We have seen reading data from API as well as posting data to our database with the help of the API. In this article, we will take a look at updating our data in our API. We will be using the Retrofit library for updating our data in our API. What we are going to build in this article? We will be
6 min read
How to Implement Swipe Down to Refresh in Android Certain applications show real-time data to the users, such as stock prices, availability of a product on online stores, etc. Showing real-time data requires continuous syncing of the application, and could be possible by implementing a program such as a thread. A Thread could be initiated by the ap
3 min read