如何结合Retrofit与DownloadManager实现高效文件下载?
如何将Retrofit/REST API与DownloadManager结合使用
DownloadManager是Android系统提供的高效下载工具,但它仅支持直接访问资源URL。要和你的REST后端搭配,核心是让DownloadManager能获取到合法的文件访问路径,下面分场景给出具体实现方案:
方案1:后端返回临时可下载URL(最优解)
如果你的后端支持,优先让下载接口返回一个带签名/时效的临时直链(比如CDN生成的一次性URL)。这种方式不需要额外处理,直接把URL传给DownloadManager即可,同时保留原有Retrofit的鉴权、参数校验逻辑。
实现步骤
- 用Retrofit调用原有的业务接口(比如带文件ID、用户鉴权的POST请求)
- 接口返回临时下载URL
- 拿到URL后配置并启动DownloadManager
代码示例
// Retrofit业务接口定义 interface FileApi { @POST("api/v1/file/get-download-url") suspend fun requestDownloadUrl(@Body req: DownloadReq): Response<DownloadResp> } data class DownloadReq(val fileId: String) data class DownloadResp(val tempUrl: String) // 获取临时URL后启动DownloadManager val api = Retrofit.Builder().baseUrl("https://your-server.com/").build().create(FileApi::class.java) val resp = api.requestDownloadUrl(DownloadReq("file_12345")) resp.body()?.tempUrl?.let { url -> val downloadManager = context.getSystemService(Context.DOWNLOAD_SERVICE) as DownloadManager val request = DownloadManager.Request(Uri.parse(url)) .setTitle("XX文档") .setDescription("正在下载") .setDestinationInExternalPublicDir(Environment.DIRECTORY_DOWNLOADS, "document.pdf") .setNotificationVisibility(DownloadManager.Request.VISIBILITY_VISIBLE_NOTIFY_COMPLETED) downloadManager.enqueue(request) }
方案2:给DownloadManager添加请求头(适配需要鉴权的直链)
如果你的文件直链需要鉴权(比如Header带Token),或者需要自定义请求头,DownloadManager支持通过addRequestHeader()方法添加这些参数,无需依赖Retrofit即可完成鉴权请求。
代码示例
val downloadManager = context.getSystemService(Context.DOWNLOAD_SERVICE) as DownloadManager val request = DownloadManager.Request(Uri.parse("https://your-server.com/files/123.pdf")) .setTitle("XX文档") .setDestinationInExternalFilesDir(context, Environment.DIRECTORY_DOWNLOADS, "document.pdf") // 添加Bearer Token鉴权 .addRequestHeader("Authorization", "Bearer ${yourUserToken}") // 添加自定义业务Header .addRequestHeader("X-App-Version", BuildConfig.VERSION_NAME) downloadManager.enqueue(request)
方案3:将Retrofit的复杂请求转为DownloadManager可识别的URL
如果你的下载必须依赖Retrofit的复杂请求逻辑(比如POST带复杂参数、多步骤鉴权),可以让后端提供对应的GET接口,将POST参数转为GET查询参数,然后用DownloadManager请求该GET URL,同时添加必要的请求头。
比如原POST请求体:
{"fileId":"123","type":"pdf","expire":3600}
转为GET URL:https://your-server.com/api/download?fileId=123&type=pdf&expire=3600,再用方案2的方式添加鉴权头即可。
方案4:监听DownloadManager的下载进度
替换Retrofit拦截器后,要获取下载进度可以通过ContentObserver监听DownloadManager的下载状态:
代码示例
class DownloadProgressObserver( private val context: Context, private val downloadId: Long, private val onProgress: (Int) -> Unit ) : ContentObserver(Handler(Looper.getMainLooper())) { override fun onChange(selfChange: Boolean) { super.onChange(selfChange) queryProgress() } private fun queryProgress() { val cursor = context.contentResolver.query( DownloadManager.getUriForDownloadedFile(downloadId), arrayOf( DownloadManager.COLUMN_BYTES_DOWNLOADED_SO_FAR, DownloadManager.COLUMN_TOTAL_SIZE_BYTES ), null, null, null ) cursor?.use { if (it.moveToFirst()) { val downloaded = it.getLong(0) val total = it.getLong(1) if (total > 0) { val progress = ((downloaded * 100) / total).toInt() onProgress(progress) } } } } } // 使用方式 val downloadId = downloadManager.enqueue(request) val observer = DownloadProgressObserver(context, downloadId) { progress -> // 更新UI进度条 binding.progressBar.progress = progress } // 注册观察者 context.contentResolver.registerContentObserver( DownloadManager.getUriForDownloadedFile(downloadId), true, observer ) // 记得在页面销毁时注销 override fun onDestroy() { super.onDestroy() context.contentResolver.unregisterContentObserver(observer) }
内容的提问来源于stack exchange,提问作者SoulReaver313
相关产品推荐
相关产品推荐

