Open In App

How to implement in-app update prompt in Android?

Last Updated : 25 May, 2025
Comments
Improve
Suggest changes
Like Article
Like
Report

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.

Flexible-updates-and-Immediate-updates

Types of In-App Updates:

There are two types of updates flows:

  1. Flexible Updates: The user can continue using the app while the update downloads in the background. Once downloaded, the app prompts for a restart.
  2. 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()
                }
            }
    }
}



Next Article

Similar Reads