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

火山引擎 最新活动