Android implements the automatic update function of Android APP
1. Project introduction
Version iteration and user update experience are crucial throughout the life cycle of a mobile application. The traditional approach is to rely on Google Play Store to force updates, but in some scenarios, we need to:
More instant control of update processes(such as grayscale, forced upgrades, reminder upgrades, etc.);
Support out-of-market distribution, such as internal enterprise application distribution and third-party application stores;
Custom update UI, consistent with the application style.
This project example will show two mainstream solutions:
Google Play In‑App Updates(Official solution, suitable for applications on the Play Store)
Self-built service + APK Download & Install(Applicable to non-Play distribution scenarios)
Through this tutorial, you will learn how to detect new versions in the app, pop up upgrade dialogs, download APKs in the background, and seamlessly trigger the installation process, greatly improving the user experience.
2. Related knowledge
-
Google Play Core Library
:core:
Includes the In‑App Updates API, allowing applications to check and trigger the "Flexible Update" or "Update Now" process at runtime without the need for users to go to the Play Store interface.
-
FileProvider & Installation Intent
For self-built update solutions, you need to
Configuration
FileProvider
, and passIntent.ACTION_VIEW
Carrying APKcontent://
URI, call the system installation interface.
-
WorkManager / DownloadManager
Long tasks (such as background download APK) should be used
WorkManager
Or systemDownloadManager
, ensure that the download can run stably in the background and can be continuously transmitted after restarting.
-
Runtime Permissions & Compatibility
Android 8.0+ (API 26+) installation requires the permission to obtain "Allow installation of unknown applications" (
REQUEST_INSTALL_PACKAGES
)。Android 7.0+ (API 24+) file URI must go
FileProvider
, otherwise it will be thrownFileUriExposedException
。
Ideas for project implementation
-
Version detection
Play Solution: Calling Play Core
()
Check for update status.Self-built plan: Initiate a network request to your own server (such as GET
/latest_version.json
), get the latest version number, APK download address, update instructions, etc.
-
Pop-up interaction
Select Update Now (force) or Flexible Update (allowing the background to restart the installation when running) according to the policy and display the update log.
-
Download APK
Play Solution: Automatically downloaded by Play Core.
Self-built plan:use
DownloadManager
Start the download and listen to the broadcast to get the download completion notification.
-
Trigger installation
After the download is completed, construct
Intent.ACTION_VIEW
, specify the MIME typeapplication/-archive
,useFileProvider
Share the APK URI and start the installation process.
4. Complete code (All‑in‑One, with detailed comments)
// ======================================= // File: + MainActivity// (This example writes Manager and Activity in one place, and distinguishes it from comments)// ======================================= package ; import ; import ; import ; import ; import ; import ; import ; import ; import ; import ; import ; import ; import ; import ; import ; import ; import ; import ; import ; import ; import ; import ; import ; import ; // —— Play Core library dependencies (update now/flexible update)// implementation ":core:1.10.3" import ; import ; import ; import ; import ; import ; import ; import ; import ; import ; import ; public class MainActivity extends AppCompatActivity { // ------------------------------------------------------------------------------------------------------------------------------ private static final int REQUEST_CODE_UPDATE = 100; // Play update request code private static final int REQUEST_INSTALL_PERMISSION = 101; // Dynamic installation permissions private static final String TAG = "AutoUpdate"; private long downloadId; // DownloadManager returns ID private DownloadManager downloadManager; // ---------- onCreate ---------- @Override protected void onCreate(Bundle savedInstanceState) { (savedInstanceState); setContentView(.activity_main); // See below for layout // The button triggers two updates Button btnPlayUpdate = findViewById(); Button btnCustomUpdate = findViewById(); (new () { @Override public void onClick(View v) { checkPlayUpdate(); // Play Store Update } }); (new () { @Override public void onClick(View v) { checkCustomUpdate(); // Self-built server update } }); // Initialize DownloadManager downloadManager = (DownloadManager) getSystemService(DOWNLOAD_SERVICE); // Register to download and complete the broadcast registerReceiver(onDownloadComplete, new IntentFilter(DownloadManager.ACTION_DOWNLOAD_COMPLETE)); } // --------- 1. Play In‑App Updates Check -------- private void checkPlayUpdate() { // Create AppUpdateManager appUpdateManager = (this); // Get update information asynchronously Task<AppUpdateInfo> appUpdateInfoTask = (); (info -> { // Determine whether there is an update and supports immediate update if (() == UpdateAvailability.UPDATE_AVAILABLE && ()) { try { // Initiate a flexible update request ( info, , this, REQUEST_CODE_UPDATE); } catch (Exception e) { (TAG, "Play update failed to start", e); } } else { (this, "No update available or this update type is not supported", Toast.LENGTH_SHORT).show(); } }); } // ---------- 2. Self-built server version detection --------- private void checkCustomUpdate() { new Thread(() -> { try { // 1) Request server JSON URL url = new URL("/latest_version.json"); HttpURLConnection conn = (HttpURLConnection) (); (5000); ("GET"); InputStream in = (); Scanner sc = new Scanner(in).useDelimiter("\\A"); String json = () ? () : ""; JSONObject obj = new JSONObject(json); final int serverVersionCode = ("versionCode"); final String apkUrl = ("apkUrl"); final String changeLog = ("changeLog"); // 2) Get the local version number int localVersionCode = getPackageManager() .getPackageInfo(getPackageName(), 0).versionCode; if (serverVersionCode > localVersionCode) { // There is a new version, go back to the main thread pop-up prompt runOnUiThread(() -> showUpdateDialog(apkUrl, changeLog) ); } else { runOnUiThread(() -> (this, "It's the latest version", Toast.LENGTH_SHORT).show() ); } } catch (Exception e) { (TAG, "Check for update failed", e); } }).start(); } // ---------- 3. An update dialog box pops up --------- private void showUpdateDialog(String apkUrl, String changeLog) { new (this) .setTitle("Discover a new version") .setMessage(changeLog) .setCancelable(false) .setPositiveButton("Update now", (dialog, which) -> { startDownload(apkUrl); }) .setNegativeButton("Let's talk about it later", null) .show(); } // ---------- 4. Start the system DownloadManager Download APK --------- private void startDownload(String apkUrl) { request = new ((apkUrl)); (.NETWORK_WIFI | .NETWORK_MOBILE); ("Downloading update package"); (Environment.DIRECTORY_DOWNLOADS, ""); // Start downloading downloadId = (request); } // ---------- 5. The monitoring download is completed and the installation is triggered. private BroadcastReceiver onDownloadComplete = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { long id = (DownloadManager.EXTRA_DOWNLOAD_ID, -1); if (id != downloadId) return; // Download is completed, install the APK File apkFile = new File(( Environment.DIRECTORY_DOWNLOADS), ""); // Android 8.0+ requires request for installation of unknown application permissions if (.SDK_INT >= Build.VERSION_CODES.O) { boolean canInstall = getPackageManager().canRequestPackageInstalls(); if (!canInstall) { // Request "install unknown application" permission (, new String[]{.REQUEST_INSTALL_PACKAGES}, REQUEST_INSTALL_PERMISSION); return; } } installApk(apkFile); } }; // ---------- 6. Process the results of the application for permissions from unknown sources --------- @Override public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) { if (requestCode == REQUEST_INSTALL_PERMISSION) { if (>0 && grantResults[0]==PackageManager.PERMISSION_GRANTED) { // Trigger the installation again (assuming the APK is still downloading the directory) File apkFile = new File(( Environment.DIRECTORY_DOWNLOADS), ""); installApk(apkFile); } else { (this, "Installation permission denied, cannot be updated automatically", Toast.LENGTH_LONG).show(); } } (requestCode, permissions, grantResults); } // ---------- 7. Install APK helper method -------- private void installApk(File apkFile) { Uri apkUri = (this, getPackageName() + ".fileprovider", apkFile); Intent intent = new Intent(Intent.ACTION_VIEW); (apkUri, "application/-archive"); (Intent.FLAG_GRANT_READ_URI_PERMISSION); (Intent.FLAG_ACTIVITY_NEW_TASK); try { startActivity(intent); } catch (ActivityNotFoundException e) { (this, "Cannot start installer", Toast.LENGTH_LONG).show(); } } // ---------- 8. Log out of Activity when destroyed Receiver -------- @Override protected void onDestroy() { (); unregisterReceiver(onDownloadComplete); } }
<!-- ====================================== document: Notice:Requires configuration FileProvider With permissions ====================================== --> <manifest xmlns:andro package=""> <!-- Install unknown source permissions(Android 8.0+ Need to apply dynamically) --> <uses-permission android:name=".REQUEST_INSTALL_PACKAGES"/> <uses-permission android:name=""/> <uses-permission android:name=".WRITE_EXTERNAL_STORAGE"/> <application android:allowBackup="true" android:label="@string/app_name" android:theme="@style/"> <!-- FileProvider statement --> <provider android:name="" android:authorities="${applicationId}.fileprovider" android:exported="false" android:grantUriPermissions="true"> <meta-data android:name=".FILE_PROVIDER_PATHS" android:resource="@xml/file_paths" /> </provider> <activity android:name=".MainActivity"> <intent-filter> <action android:name=""/> <category android:name=""/> </intent-filter> </activity> </application> </manifest>
<!-- ====================================== document: res/xml/file_paths.xml FileProvider Path configuration ====================================== --> <paths xmlns:andro> <external-path name="download" path="Download/"/> </paths>
<!-- ====================================== document: res/layout/activity_main.xml Simple sample interface ====================================== --> <LinearLayout xmlns:andro android:orientation="vertical" android:gravity="center" android:layout_width="match_parent" android:layout_height="match_parent" android:padding="24dp"> <Button android: android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Play In‑App Update"/> <View android:layout_height="16dp" android:layout_width="match_parent"/> <Button android: android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Self-built service update"/> </LinearLayout>
5. Method interpretation
checkPlayUpdate()
Check update availability on Google Play and start the download and installation process in a "flexible update" way.checkCustomUpdate()
passHttpURLConnection
Request server JSON, parse the latestversionCode
andapkUrl
, compare the local version and decide whether to pop up the window.showUpdateDialog(...)
Based on server returnchangeLog
BuildAlertDialog
, provides two interactions: "Update now" and "Say later".startDownload(String apkUrl)
Usage systemDownloadManager
Initiate a background download and save it to the public directory, supporting breakpoint relay and system download notifications.BroadcastReceiver onDownloadComplete
MonitorDownloadManager.ACTION_DOWNLOAD_COMPLETE
Broadcast, confirm that the installation process is triggered after this download.onRequestPermissionsResult(...)
Process the authorization results of Android 8.0+ "Installation Unknown Source" permission, and continue to call after authorization.installApk()
。installApk(File apkFile)
passFileProvider
Get the content URI of the APK and use itIntent.ACTION_VIEW
Call the system installer.
6. Project Summary
Advantages
Play Core In‑App Update: Official support, the experience is consistent with the Play Store, without the need to manually manage download logic.
Self-built plan: Flexible and controllable, supports any distribution channel, customizes UI and grayscale policies.
Pay attention and optimization
-
Permissions and compatibility
Android 7.0+ must be used
FileProvider
。Android 8.0+ requires dynamic application
REQUEST_INSTALL_PACKAGES
。
-
Download failed and try again
Can be combined
WorkManager
Added the logic of retry and network disconnection.
-
Security
It is recommended to perform signature verification of the APK (calculate SHA256 and the server) to prevent it from being tampered with.
-
UI Experience
Make more status prompts for "Update Now" and "Background Update".
Download progress bar, progress notification, etc. can be displayed.
-
Grayscale/forced upgrade
You can add policy fields in the server JSON, such as
forceUpdate
, prohibit "Say it later" in the dialog box.
This is the article about Android implementing the automatic update function of Android APP. For more relevant content on Android APP, please search for my previous articles or continue browsing the related articles below. I hope everyone will support me in the future!
Related Articles
How to preset APK in Android system source code
Today, the editor will share with you an article about presetting APKs in the Android system source code. The editor thinks the content is pretty good. Now I share it with you. It has good reference value. Friends in need will follow the editor to take a look.2018-12-12Detailed explanation of the solution to the compatibility of Android immersive implementation
This article mainly introduces a detailed explanation of the solutions to the compatibility of Android immersive implementation. The editor thinks it is quite good. I will share it with you now and give you a reference. Let's take a look with the editor2017-11-11Android method to change the default location of Toast
Below, the editor will introduce you to an article about how to change the default location of Toast on Android. The editor thinks it is quite good, so I will share it with you now and give you a reference. Let's take a look with the editor2017-04-04Some thoughts on getting current activities in Android development
This article mainly introduces some thoughts on obtaining current activities during Android development. Interested friends can refer to it.2016-02-02Detailed explanation of the five major layout methods of Android
This article mainly introduces the knowledge and information of the five major layouts of Android. Here we have compiled detailed layout information, implementation sample code, and implementation renderings. Interested friends can refer to it.2016-09-09Android method to implement marble effect based on TextView attribute android:ellipsize
This article mainly introduces the method of Android to achieve marble effects based on TextView attributes android:ellipsize. It involves the related attributes and usage methods of TextView involved in Android marble effects. Friends who need it can refer to it2016-08-08Unicode code values for some special characters in Android (such as: ←↑→↓ and other arrow symbols)
This article mainly introduces the Unicode code values of some special characters in Android (such as: ←↑→↓ and other arrow symbols). Friends who need it can refer to it.2017-03-03Android Web jump to the app specified page and pass parameter instance
This article mainly introduces the Android Web to jump to the app specified page and pass parameter instances. It has good reference value and hopes it will be helpful to everyone. Let's take a look with the editor2020-03-032 ways to realize real-time viewPager
This article mainly introduces two ways to realize real-time viewPager on Android, which has certain reference value. Interested friends can refer to it.2016-10-10Android Zoom Animation Summary
ScaleAnimation is the scaling animation, with many application scenarios, such as the common hidden menu click to display. This article mainly introduces the use of Android scaling animation ScaleAnimation. Friends who need it can refer to it.2024-03-03