You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

非Play商店APK应用内无浏览器直接更新实现方案咨询

Hey there! I’ve tackled this exact problem before when building a non-Play Store app, so let’s break down the reliable ways to implement in-app APK downloads without bouncing the user to a browser—just like Google Play does.

This is the most stable approach because it leverages Android’s system-level download service, which keeps running even if your app is closed (just like Play Store updates). Here’s how to set it up:

Step 1: Add Required Permissions & FileProvider

First, update your AndroidManifest.xml to include necessary permissions and register a FileProvider (required for Android 7.0+ to avoid file exposure errors):

<!-- Basic internet access -->
<uses-permission android:name="android.permission.INTERNET" />
<!-- Required for installing APKs on Android 8.0+ -->
<uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES" />
<!-- Only needed for Android 9 and below -->
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" android:maxSdkVersion="28" />

<application ...>
    <!-- Register FileProvider to safely share APK files -->
    <provider
        android:name="androidx.core.content.FileProvider"
        android:authorities="${applicationId}.fileprovider"
        android:exported="false"
        android:grantUriPermissions="true">
        <meta-data
            android:name="android.support.FILE_PROVIDER_PATHS"
            android:resource="@xml/file_paths" />
    </provider>
</application>

Next, create res/xml/file_paths.xml to define where the APK will be stored:

<?xml version="1.0" encoding="utf-8"?>
<paths xmlns:android="http://schemas.android.com/apk/res/android">
    <!-- Stores APK in your app's external files directory (no extra permissions needed for Android 10+) -->
    <external-files-path name="apk_files" path="." />
</paths>

Step 2: Implement the Download & Install Logic

Add this code to your Activity/Fragment to start the download and handle installation once it’s done:

private fun startInAppApkDownload(apkDownloadUrl: String) {
    // Get system DownloadManager service
    val downloadManager = getSystemService(Context.DOWNLOAD_SERVICE) as DownloadManager
    
    // Configure download request
    val downloadRequest = DownloadManager.Request(Uri.parse(apkDownloadUrl))
        .setTitle("My App Update")
        .setDescription("Downloading the latest version...")
        .setNotificationVisibility(DownloadManager.Request.VISIBILITY_VISIBLE_NOTIFY_COMPLETED)
        // Save APK to your app's private external storage
        .setDestinationInExternalFilesDir(this, null, "MyApp_Latest.apk")

    // Enqueue the download and save its ID to track progress/completion
    val downloadId = downloadManager.enqueue(downloadRequest)

    // Register a broadcast receiver to listen for download completion
    val downloadCompleteReceiver = object : BroadcastReceiver() {
        override fun onReceive(context: Context?, intent: Intent?) {
            val completedDownloadId = intent?.getLongExtra(DownloadManager.EXTRA_DOWNLOAD_ID, -1)
            if (completedDownloadId == downloadId) {
                // Download finished—trigger installation
                installDownloadedApk(context)
                // Unregister the receiver to avoid memory leaks
                context?.unregisterReceiver(this)
            }
        }
    }
    registerReceiver(downloadCompleteReceiver, IntentFilter(DownloadManager.ACTION_DOWNLOAD_COMPLETE))
}

private fun installDownloadedApk(context: Context?) {
    context ?: return
    val apkFile = File(getExternalFilesDir(null), "MyApp_Latest.apk")
    
    // Get a safe URI for the APK (handles Android 7.0+ file restrictions)
    val apkUri = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
        FileProvider.getUriForFile(context, "${packageName}.fileprovider", apkFile)
    } else {
        Uri.fromFile(apkFile)
    }

    // Create install intent
    val installIntent = Intent(Intent.ACTION_INSTALL_PACKAGE).apply {
        setDataAndType(apkUri, "application/vnd.android.package-archive")
        addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
        addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
    }

    // Check for unknown app installation permission (Android 8.0+)
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
        if (packageManager.canRequestPackageInstalls()) {
            startActivity(installIntent)
        } else {
            // Redirect user to settings to enable permission
            val permissionIntent = Intent(Settings.ACTION_MANAGE_UNKNOWN_APP_SOURCES, Uri.parse("package:$packageName"))
            startActivity(permissionIntent)
        }
    } else {
        startActivity(installIntent)
    }
}

Step 3: Trigger the Download from Your Update Button

Just call startInAppApkDownload("YOUR_APK_DOWNLOAD_URL") when the user taps your update button.

Solution 2: Custom Downloader (For Advanced Control)

If you need more control over the download process (like custom progress bars or retry logic), you can use OkHttp or HttpURLConnection with a background coroutine:

// Using Kotlin Coroutines for background download
private fun startCustomDownload(apkUrl: String) {
    lifecycleScope.launch(Dispatchers.IO) {
        val apkFile = File(getExternalFilesDir(null), "MyApp_Latest.apk")
        val client = OkHttpClient()
        
        try {
            val request = Request.Builder().url(apkUrl).build()
            val response = client.newCall(request).execute()
            
            if (response.isSuccessful) {
                response.body?.byteStream()?.use { inputStream ->
                    apkFile.outputStream().use { outputStream ->
                        inputStream.copyTo(outputStream)
                    }
                }
                // Switch to main thread to trigger installation
                withContext(Dispatchers.Main) {
                    installDownloadedApk(this@YourActivity)
                }
            }
        } catch (e: IOException) {
            withContext(Dispatchers.Main) {
                Toast.makeText(this@YourActivity, "Download failed: ${e.message}", Toast.LENGTH_SHORT).show()
            }
        }
    }
}

Key Notes to Remember

  • HTTPS Only: Android P (API 28+) blocks HTTP requests by default, so make sure your APK download URL uses HTTPS. If you must use HTTP, add a network security config to your Manifest.
  • Download Failure Handling: For DownloadManager, you can query the download status using downloadManager.query(DownloadManager.Query().setFilterById(downloadId)) to check for failures and notify the user.
  • Scoped Storage: On Android 10+, saving to getExternalFilesDir() doesn’t require WRITE_EXTERNAL_STORAGE permission, which simplifies things.

That should give you a seamless in-app update experience just like Google Play. Let me know if you hit any snags!

内容的提问来源于stack exchange,提问作者Amit Chauhan

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.25 06:25:39