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

Android中如何为DownloadManager添加ProgressBar与完成监听

实现DownloadManager下载进度监听与完成状态更新

核心思路

DownloadManager没有直接的进度回调接口,需要通过ContentObserver监听下载任务的状态变化,定期查询下载进度来更新ProgressBar;同时通过查询下载状态判断任务是否完成,进而更新UI状态。

实现步骤

1. 保存下载任务ID

调用dm.enqueue(request)会返回唯一的下载ID,需保存该ID追踪对应下载任务:

// 类成员变量,保存当前下载任务ID
private var currentDownloadId: Long = -1L
// 类成员变量,保存ContentObserver用于后续注销
private var downloadObserver: ContentObserver? = null

private fun downloadImageNew(filename: String, downloadUrlOfImage: String) {
    try {
        val dm = requireContext().getSystemService(Context.DOWNLOAD_SERVICE) as DownloadManager?
        val downloadUri = Uri.parse(downloadUrlOfImage)
        val request = DownloadManager.Request(downloadUri)
        request.setAllowedNetworkTypes(DownloadManager.Request.NETWORK_WIFI or DownloadManager.Request.NETWORK_MOBILE)
            .setTitle(filename)
            .setMimeType("image/jpeg")
            // 关闭系统默认通知,避免和自定义ProgressBar重复
            .setNotificationVisibility(DownloadManager.Request.VISIBILITY_HIDDEN)
            .setDestinationInExternalPublicDir(Environment.DIRECTORY_PICTURES, File.separator + filename + ".jpg")
        
        // 保存下载ID并启动任务
        currentDownloadId = dm!!.enqueue(request)
        Toast.makeText(requireContext(), "图片下载已启动", Toast.LENGTH_SHORT).show()
        
        // 注册监听进度的ContentObserver
        registerDownloadObserver()
    } catch (e: Exception) {
        Toast.makeText(requireContext(), "图片下载失败", Toast.LENGTH_SHORT).show()
    }
}

2. 注册ContentObserver监听进度

自定义ContentObserver监听DownloadManager的任务状态变化:

private fun registerDownloadObserver() {
    downloadObserver = object : ContentObserver(Handler(Looper.getMainLooper())) {
        override fun onChange(selfChange: Boolean) {
            super.onChange(selfChange)
            queryDownloadStatus()
        }
    }
    // 注册监听DownloadManager的查询URI
    requireContext().contentResolver.registerContentObserver(
        DownloadManager.Query.EXTERNAL_CONTENT_URI,
        true,
        downloadObserver!!
    )
}

3. 查询进度并更新UI

通过DownloadManager的query方法获取任务详情,计算进度并处理完成/失败状态:

private fun queryDownloadStatus() {
    val dm = requireContext().getSystemService(Context.DOWNLOAD_SERVICE) as DownloadManager?
    val query = DownloadManager.Query().setFilterById(currentDownloadId)
    dm?.query(query)?.use { cursor ->
        if (cursor.moveToFirst()) {
            // 获取下载状态、已下载大小、总大小
            val status = cursor.getInt(cursor.getColumnIndexOrThrow(DownloadManager.COLUMN_STATUS))
            val downloadedBytes = cursor.getLong(cursor.getColumnIndexOrThrow(DownloadManager.COLUMN_BYTES_DOWNLOADED_SO_FAR))
            val totalBytes = cursor.getLong(cursor.getColumnIndexOrThrow(DownloadManager.COLUMN_TOTAL_SIZE_BYTES))
            
            // 更新ProgressBar(假设布局中id为progress_bar)
            if (totalBytes > 0) {
                val progress = (downloadedBytes * 100 / totalBytes).toInt()
                binding.progressBar.progress = progress
            }
            
            // 处理不同状态
            when (status) {
                DownloadManager.STATUS_SUCCESSFUL -> {
                    // 下载完成:隐藏进度条,更新状态文本
                    binding.progressBar.visibility = View.GONE
                    binding.downloadStatusTv.text = "下载完成"
                    Toast.makeText(requireContext(), "图片已保存至图库", Toast.LENGTH_SHORT).show()
                    // 注销Observer避免内存泄漏
                    downloadObserver?.let {
                        requireContext().contentResolver.unregisterContentObserver(it)
                        downloadObserver = null
                    }
                }
                DownloadManager.STATUS_FAILED -> {
                    // 下载失败:更新状态,注销Observer
                    binding.progressBar.visibility = View.GONE
                    binding.downloadStatusTv.text = "下载失败"
                    Toast.makeText(requireContext(), "图片下载失败,请重试", Toast.LENGTH_SHORT).show()
                    downloadObserver?.let {
                        requireContext().contentResolver.unregisterContentObserver(it)
                        downloadObserver = null
                    }
                }
                DownloadManager.STATUS_RUNNING -> {
                    // 下载中:显示进度条,更新状态文本
                    binding.progressBar.visibility = View.VISIBLE
                    binding.downloadStatusTv.text = "下载中..."
                }
                // 可按需处理暂停、等待等其他状态
            }
        }
    }
}

4. 生命周期中注销Observer

在Fragment/Activity的onDestroy方法中注销Observer,防止内存泄漏:

override fun onDestroy() {
    super.onDestroy()
    downloadObserver?.let {
        requireContext().contentResolver.unregisterContentObserver(it)
        downloadObserver = null
    }
}

注意事项

  • 确保布局中存在ProgressBar和状态文本控件,根据实际ID修改代码中的控件引用。
  • Android 10及以上版本需适配Scoped Storage,若使用setDestinationInExternalPublicDir,需申请READ_EXTERNAL_STORAGE和WRITE_EXTERNAL_STORAGE权限。
  • 多下载任务场景需维护下载ID与对应UI控件的映射,避免进度混淆。

内容的提问来源于stack exchange,提问作者Jojo Drink

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.08 01:57:38