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

Android使用DownloadManager单通知逐个下载多文件并显示整体进度

可行性分析与实现方案

当然可行!不过默认的DownloadManager不会直接提供这个功能——它每个下载任务都会创建独立的通知。要实现单通知展示多文件的整体进度,你需要结合DownloadManager的监听机制+自定义通知来搞定,下面是具体的思路和实现步骤:

核心思路

通过监听所有下载任务的进度变化,实时计算已下载总字节数与所有文件的总大小的比例,然后用自定义通知展示这个整体进度,同时关闭DownloadManager默认的单个任务通知。

具体实现步骤

1. 提前计算总下载大小

在发起所有下载请求前,先获取每个文件的总大小(可以通过Http请求头的Content-Length获取),累加得到总下载大小。如果有些文件无法提前获取大小(比如服务器未返回长度),可以后续通过DownloadManager的查询接口补全,或者做兼容处理(比如按已下载比例估算)。

2. 关闭DownloadManager默认通知

给每个DownloadManager.Request设置隐藏通知,避免系统弹出单个任务的通知:

val request = DownloadManager.Request(Uri.parse(fileUrl))
    .setNotificationVisibility(DownloadManager.Request.VISIBILITY_HIDDEN) // 隐藏默认通知
    // 其他配置:存储路径、标题等

3. 监听所有下载任务的进度变化

注册ContentObserver监听DownloadManager的内容变化,每次变化时查询所有任务的已下载字节数,计算整体进度:

// 自定义ContentObserver,监听下载进度变化
class DownloadBatchObserver(handler: Handler, private val updateCallback: () -> Unit) : ContentObserver(handler) {
    override fun onChange(selfChange: Boolean) {
        super.onChange(selfChange)
        updateCallback.invoke() // 触发进度计算
    }
}

// 在Activity/Fragment或Service中注册监听
val downloadManager = getSystemService(DOWNLOAD_SERVICE) as DownloadManager
val observer = DownloadBatchObserver(Handler(Looper.getMainLooper())) {
    calculateOverallProgress()
}
contentResolver.registerContentObserver(DownloadManager.Query.CONTENT_URI, true, observer)

4. 计算整体进度并更新自定义通知

编写方法查询所有下载任务的进度,计算整体比例,然后更新自定义通知的进度条:

// 保存所有发起的下载任务ID,避免查询到其他App的任务
private val downloadTaskIds = mutableListOf<Long>()

private fun calculateOverallProgress() {
    val downloadManager = getSystemService(DOWNLOAD_SERVICE) as DownloadManager
    val query = DownloadManager.Query().setFilterById(*downloadTaskIds.toLongArray())
    
    val cursor = downloadManager.query(query)
    var totalDownloaded = 0L
    var totalSize = 0L

    if (cursor.moveToFirst()) {
        do {
            val downloaded = cursor.getLong(cursor.getColumnIndexOrThrow(DownloadManager.COLUMN_BYTES_DOWNLOADED_SO_FAR))
            val total = cursor.getLong(cursor.getColumnIndexOrThrow(DownloadManager.COLUMN_TOTAL_SIZE_BYTES))
            
            totalDownloaded += downloaded
            totalSize += total
        } while (cursor.moveToNext())
    }
    cursor.close()

    val progress = if (totalSize == 0) 0 else (totalDownloaded * 100 / totalSize).toInt()
    updateBatchNotification(progress, totalDownloaded, totalSize)
}

private fun updateBatchNotification(progress: Int, downloaded: Long, total: Long) {
    val channelId = "BATCH_DOWNLOAD_CHANNEL"
    // 适配Android 8.0+的通知渠道
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
        val channel = NotificationChannel(channelId, "批量下载通知", NotificationManager.IMPORTANCE_LOW)
        val notificationManager = getSystemService(NOTIFICATION_SERVICE) as NotificationManager
        notificationManager.createNotificationChannel(channel)
    }

    val notificationBuilder = NotificationCompat.Builder(this, channelId)
        .setSmallIcon(R.drawable.ic_download)
        .setContentTitle("批量下载中")
        .setContentText("已下载 ${formatFileSize(downloaded)} / ${formatFileSize(total)}")
        .setProgress(100, progress, false) // 显示精确进度条
        .setOngoing(true) // 标记为正在进行的通知,避免被误删

    // 全部下载完成时更新通知状态
    if (progress == 100) {
        notificationBuilder.setContentText("全部下载完成")
            .setOngoing(false)
            .setAutoCancel(true)
    }

    val notificationManager = getSystemService(NOTIFICATION_SERVICE) as NotificationManager
    notificationManager.notify(10001, notificationBuilder.build()) // 固定通知ID,确保更新同一个通知
}

// 辅助方法:格式化文件大小为可读格式
private fun formatFileSize(size: Long): String {
    return when {
        size >= 1024 * 1024 * 1024 -> String.format("%.2f GB", size.toFloat() / (1024 * 1024 * 1024))
        size >= 1024 * 1024 -> String.format("%.2f MB", size.toFloat() / (1024 * 1024))
        size >= 1024 -> String.format("%.2f KB", size.toFloat() / 1024)
        else -> "$size B"
    }
}

5. 收尾处理

  • 当所有任务完成或取消时,记得注销ContentObserver,避免内存泄漏。
  • 如果App在后台运行,可能会被系统限制监听能力,这时可以考虑将监听逻辑放到前台服务中(刚好我们的自定义通知可以作为前台服务的通知,符合Android的后台限制要求)。

注意事项

  • 权限配置:确保申请了INTERNET权限;如果下载到外部存储,Android 10及以上需要WRITE_EXTERNAL_STORAGE权限(或使用MediaStore进行文件存储)。
  • 任务ID管理:务必保存自己发起的所有下载任务ID,避免查询到其他应用的下载任务,导致进度计算错误。
  • 异常处理:要处理下载失败的任务(比如重新发起下载,或者在通知中提示用户),避免整体进度卡住。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.22 08:04:50