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
相关产品推荐
相关产品推荐

