Android下载管理器:如何监听通知栏取消下载的状态?
Great question—this is a common gotcha with Android's DownloadManager since it doesn’t expose a dedicated broadcast for download cancellation. Let’s break down what you need to know and how to handle this scenario:
First: The Hard Truth
Android’s DownloadManager does not provide a separate broadcast for when a download is cancelled (whether via the notification’s "Cancel" button or programmatically). The only system broadcasts it sends are the two you’ve already observed:
DownloadManager.ACTION_DOWNLOAD_COMPLETE: Triggers when a download finishes (successfully), fails, or is cancelledDownloadManager.ACTION_NOTIFICATION_CLICKED: Triggers only when the user taps the download notification (not the Cancel button)
Solution 1: Check Status in the COMPLETE Broadcast
When a download is cancelled, ACTION_DOWNLOAD_COMPLETE still fires—you just need to check the download’s status and reason to identify cancellation. Here’s how to implement this in your BroadcastReceiver:
override fun onReceive(context: Context?, intent: Intent?) { if (intent?.action == DownloadManager.ACTION_DOWNLOAD_COMPLETE) { val downloadId = intent.getLongExtra(DownloadManager.EXTRA_DOWNLOAD_ID, -1L) if (downloadId == -1L) return val downloadManager = context?.getSystemService(Context.DOWNLOAD_SERVICE) as DownloadManager val query = DownloadManager.Query().setFilterById(downloadId) val cursor = downloadManager.query(query) if (cursor.moveToFirst()) { val statusColumn = cursor.getColumnIndex(DownloadManager.COLUMN_STATUS) val status = cursor.getInt(statusColumn) when (status) { DownloadManager.STATUS_SUCCESSFUL -> { // Handle successful download } DownloadManager.STATUS_FAILED -> { val reasonColumn = cursor.getColumnIndex(DownloadManager.COLUMN_REASON) val reason = cursor.getInt(reasonColumn) if (reason == DownloadManager.ERROR_CANCELED) { // This is the cancellation event you're looking for! Toast.makeText(context, "Download cancelled", Toast.LENGTH_SHORT).show() // Add your cleanup logic here (e.g., delete partial files) } else { // Handle other failure reasons (network error, insufficient storage, etc.) } } } } cursor.close() // Always close cursors to avoid memory leaks } }
Solution 2: Real-Time Tracking with ContentObserver
If you need to detect cancellation immediately (without waiting for the ACTION_DOWNLOAD_COMPLETE broadcast), you can register a ContentObserver to monitor changes to the DownloadManager’s content URI. This lets you catch status updates as they happen:
// Create a custom ContentObserver class DownloadCancellationObserver( handler: Handler, private val onCancelled: (downloadId: Long) -> Unit ) : ContentObserver(handler) { override fun onChange(selfChange: Boolean, uri: Uri?) { super.onChange(selfChange, uri) uri ?: return // Extract the download ID from the URI val downloadId = ContentUris.parseId(uri) val context = handler.looper.context val downloadManager = context.getSystemService(Context.DOWNLOAD_SERVICE) as DownloadManager val query = DownloadManager.Query().setFilterById(downloadId) val cursor = downloadManager.query(query) if (cursor.moveToFirst()) { val status = cursor.getInt(cursor.getColumnIndex(DownloadManager.COLUMN_STATUS)) val reason = cursor.getInt(cursor.getColumnIndex(DownloadManager.COLUMN_REASON)) if (status == DownloadManager.STATUS_FAILED && reason == DownloadManager.ERROR_CANCELED) { onCancelled(downloadId) } } cursor.close() } } // Register the observer in your Activity/Fragment private lateinit var cancellationObserver: DownloadCancellationObserver override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) cancellationObserver = DownloadCancellationObserver(Handler(Looper.getMainLooper())) { downloadId -> // Handle cancellation for this download ID Toast.makeText(this, "Download $downloadId was cancelled", Toast.LENGTH_SHORT).show() } contentResolver.registerContentObserver( DownloadManager.EXTRA_DOWNLOAD_URI, true, cancellationObserver ) } // Don't forget to unregister when your component is destroyed override fun onDestroy() { super.onDestroy() contentResolver.unregisterContentObserver(cancellationObserver) }
Bonus: Handle Programmatic Cancellation
If you allow users to cancel downloads from your app’s UI (not just the notification), make sure to trigger your cancellation logic when you call downloadManager.remove(downloadId). This ensures you cover both user-initiated (notification) and app-initiated cancellation scenarios.
Quick Reminders
ACTION_NOTIFICATION_CLICKEDonly fires when the user taps the main notification—ignore it for cancellation events.- Always close cursors after querying
DownloadManagerto prevent memory leaks.
内容的提问来源于stack exchange,提问作者Ankit Saini




