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

Android下载管理器:如何监听通知栏取消下载的状态?

Handling Download Cancellation with Android's DownloadManager

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 cancelled
  • DownloadManager.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_CLICKED only fires when the user taps the main notification—ignore it for cancellation events.
  • Always close cursors after querying DownloadManager to prevent memory leaks.

内容的提问来源于stack exchange,提问作者Ankit Saini

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.15 03:31:36