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

Android应用下载完成未触发DOWNLOAD_COMPLETE广播问题求助

问题分析与解决方案

你的DownloadReceiver未触发,核心原因及对应修复方案如下:

1. 非静态内部类无法被系统实例化

你的DownloadReceiver是AppUpdater的非静态内部类(inner class),而通过Manifest静态注册的BroadcastReceiver必须具备无参构造函数,且不能依赖外部类实例。系统触发广播时无法创建非静态内部类的实例(需要先生成外部类AppUpdater对象),这是最可能的触发失败原因。

修复方案:

将DownloadReceiver改为静态内部类,或提取为独立顶层类:

class AppUpdater(private val context: Context) {
    private lateinit var apkUrl: String
    var isUpdateAvailable = mutableStateOf(false)
    val downloadManager: DownloadManager = context.getSystemService(DownloadManager::class.java)

    // 将downloadId移至静态域,确保广播能匹配到
    companion object {
        private var downloadId: Long = -1
    }

    fun checkForUpdates() {
        HttpHandler.asyncHandle(Constant.UPDATE_URL, Constant.AUTHORIZATION_TOKEN, ::callback)
    }

    private fun callback(response: okhttp3.Response) {...}

    fun downloadApkFile() {
        val request = DownloadManager.Request(Uri.parse(apkUrl))
            .setNotificationVisibility(DownloadManager.Request.VISIBILITY_VISIBLE_NOTIFY_COMPLETED)
            .addRequestHeader("Authorization", "Bearer ${Constant.AUTHORIZATION_TOKEN}")
            .addRequestHeader("Accept", "application/octet-stream")
            .setDestinationInExternalPublicDir(
                Environment.DIRECTORY_DOWNLOADS,
                "MyApp.apk"
            )
            .setAllowedOverMetered(true)
            .setAllowedOverRoaming(true)
        downloadId = downloadManager.enqueue(request)
    }

    // 改为静态内部类
    class DownloadReceiver : BroadcastReceiver() {
        override fun onReceive(context: Context, intent: Intent) {
            Log.w("AppUpdater", "APK Download Complete")
            val id = intent.getLongExtra(DownloadManager.EXTRA_DOWNLOAD_ID, -1)
            if (id == downloadId) {...}
        }
    }
}

Manifest中的注册代码无需修改,.AppUpdater$DownloadReceiver仍可正确指向该静态内部类。

2. Android 8.0+ 隐式广播兼容性问题

尽管ACTION_DOWNLOAD_COMPLETE是少数允许静态注册的隐式广播,但部分场景下可能存在兼容性问题。若静态注册仍不生效,可尝试动态注册接收器:

修复方案:

在AppUpdater初始化或downloadApkFile方法中动态注册,在Context生命周期结束前注销:

class AppUpdater(private val context: Context) {
    private lateinit var apkUrl: String
    private var downloadId: Long = -1
    var isUpdateAvailable = mutableStateOf(false)
    val downloadManager: DownloadManager = context.getSystemService(DownloadManager::class.java)
    private lateinit var downloadReceiver: DownloadReceiver

    init {
        downloadReceiver = DownloadReceiver()
        val filter = IntentFilter(DownloadManager.ACTION_DOWNLOAD_COMPLETE)
        context.registerReceiver(downloadReceiver, filter)
    }

    // 提供注销方法,在持有Context销毁时调用
    fun release() {
        context.unregisterReceiver(downloadReceiver)
    }

    fun checkForUpdates() {
        HttpHandler.asyncHandle(Constant.UPDATE_URL, Constant.AUTHORIZATION_TOKEN, ::callback)
    }

    private fun callback(response: okhttp3.Response) {...}

    fun downloadApkFile() {
        val request = DownloadManager.Request(Uri.parse(apkUrl))
            .setNotificationVisibility(DownloadManager.Request.VISIBILITY_VISIBLE_NOTIFY_COMPLETED)
            .addRequestHeader("Authorization", "Bearer ${Constant.AUTHORIZATION_TOKEN}")
            .addRequestHeader("Accept", "application/octet-stream")
            .setDestinationInExternalPublicDir(
                Environment.DIRECTORY_DOWNLOADS,
                "MyApp.apk"
            )
            .setAllowedOverMetered(true)
            .setAllowedOverRoaming(true)
        downloadId = downloadManager.enqueue(request)
    }

    inner class DownloadReceiver : BroadcastReceiver() {
        override fun onReceive(context: Context, intent: Intent) {
            Log.w("AppUpdater", "APK Download Complete")
            val id = intent.getLongExtra(DownloadManager.EXTRA_DOWNLOAD_ID, -1)
            if (id == downloadId) {...}
        }
    }
}

注意:动态注册的接收器会随注册Context的生命周期失效,务必在合适时机调用release()方法注销,避免内存泄漏。

3. 下载任务本身未成功完成

若下载任务因网络错误、权限不足等原因失败,ACTION_DOWNLOAD_COMPLETE仍会触发,但你可能误以为未触发。可通过以下代码查询下载状态确认:

fun checkDownloadStatus() {
    val query = DownloadManager.Query().setFilterById(downloadId)
    val cursor = downloadManager.query(query)
    if (cursor.moveToFirst()) {
        val status = cursor.getInt(cursor.getColumnIndexOrThrow(DownloadManager.COLUMN_STATUS))
        when(status) {
            DownloadManager.STATUS_SUCCESSFUL -> Log.d("AppUpdater", "下载成功")
            DownloadManager.STATUS_FAILED -> Log.d("AppUpdater", "下载失败:${cursor.getInt(cursor.getColumnIndexOrThrow(DownloadManager.COLUMN_REASON))}")
        }
    }
    cursor.close()
}

额外注意事项

  • 确保downloadId正确保存:避免因AppUpdater实例销毁导致ID丢失,静态内部类方案中已通过静态域解决此问题。
  • 权限检查:Android 10及以上版本下载到公共目录,需申请android.permission.MANAGE_EXTERNAL_STORAGE;若改用应用私有目录(如getExternalFilesDir(Environment.DIRECTORY_DOWNLOADS)),则无需此权限,权限不足会直接导致下载失败。

内容的提问来源于stack exchange,提问作者Nguyen Duc

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.28 09:53:30