使用Retrofit下载至Downloads子文件夹后无法通过ACTION_GET_CONTENT访问
Retrofit下载文件到Downloads子文件夹无法通过ACTION_GET_CONTENT/ACTION_OPEN_DOCUMENT访问的解决方法
问题现象
使用Retrofit下载文件至Downloads目录的自定义子文件夹(如Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS)/MyApp):
- 系统原生文件管理器可正常查看文件
- 通过
ACTION_GET_CONTENT或ACTION_OPEN_DOCUMENTIntent唤起的文件选择器无法找到该文件 - 手动在文件管理器中重命名/复制该文件后,文件选择器可正常识别
- 直接保存到Downloads根目录或使用DownloadManager下载时无此问题
问题原因
Android的媒体库依赖媒体扫描器索引外部存储文件。DownloadManager会自动触发媒体扫描通知系统文件变化;而手动写入文件到自定义子文件夹时,系统不会主动扫描该路径,导致基于媒体库的文件选择器无法发现新增文件。
解决方法
在文件写入完成后,调用MediaScannerConnection手动触发媒体扫描,通知系统更新文件索引。
修改后的下载函数
suspend fun download(url: String, targetPath: String, progressRetrofit: RetrofitProgress, context: Context) = flow { try { val response = apiService.download(url).awaitResponse() val body = response.body() if (response.isSuccessful && body != null) { try { val file = File(targetPath) // 确保父文件夹存在 file.parentFile?.let { parentDir -> if (!parentDir.exists()) { parentDir.mkdirs() } } body.byteStream().use { inputStream -> FileOutputStream(file).use { outputStream -> val data = ByteArray(1024) var read: Int var currentDownloadSize = 0L val fileSize = body.contentLength() while (inputStream.read(data).also { read = it } != -1) { outputStream.write(data, 0, read) currentDownloadSize += read withContext(Dispatchers.Main) { progressRetrofit.onProgressUpdate( (currentDownloadSize * 100 / fileSize).toInt(), fileSize, currentDownloadSize ) } } withContext(Dispatchers.Main) { progressRetrofit.onProgressUpdate( (currentDownloadSize * 100 / fileSize).toInt(), fileSize, currentDownloadSize ) } outputStream.flush() outputStream.close() } } // 触发媒体扫描,通知系统文件新增 MediaScannerConnection.scanFile( context, arrayOf(targetPath), null // 若已知文件MIME类型可传入,如"application/pdf" ) { _, _ -> // 扫描完成回调,按需处理 } emit(NetworkResult.Success(true)) } catch (e: Exception) { emit(NetworkResult.Failure(e.message.toString())) errorMessage(e.message.toString(), true) } } else { emit(NetworkResult.Failure(response.message())) errorMessage(response.errorBody().toString(), true) } } catch (e: Exception) { emit(NetworkResult.Failure(e.message.toString())) errorMessage(e.message.toString(), true) } }
注意事项
- 确保已适配Android存储权限:Android 10及以上可使用Scoped Storage,无需申请
WRITE_EXTERNAL_STORAGE;低版本需提前申请该权限 - 若明确文件MIME类型,在
scanFile第三个参数传入对应值,可提升扫描效率 - 建议传入Application Context调用
MediaScannerConnection,避免内存泄漏
内容的提问来源于stack exchange,提问作者mahdi asd
相关产品推荐
相关产品推荐

