如何实现从当前页返回上一页时展示图片下载进度条
实现方案
核心思路
要实现跨页面展示下载进度,核心是让下载任务的生命周期脱离当前页面的生命周期,同时用可全局观察的数据流托管下载进度,所有需要展示进度的页面直接订阅该数据流即可。
具体实现步骤
1. 定义下载状态结构体
统一封装进度、成功、失败等状态,方便上下游传递:
sealed class DownloadState { // 下载中:已完成数量、总数量 data class InProgress(val completedCount: Int, val totalCount: Int): DownloadState() // 全部下载成功 object Success: DownloadState() // 下载失败:对应错误提示资源ID data class Failed(val errorRes: Int): DownloadState() // 空闲状态 object Idle: DownloadState() }
2. 实现全局单例下载管理器
用生命周期和Application一致的单例托管下载任务,内部用StateFlow暴露可观察的下载状态:
// 如果用依赖注入框架(如Hilt)直接标注@Singleton即可 object ImageDownloadManager { private val _downloadState = MutableStateFlow<DownloadState>(DownloadState.Idle) // 对外暴露只读的状态流,所有页面都可以订阅这个流拿进度 val downloadState: StateFlow<DownloadState> = _downloadState.asStateFlow() fun startDownloadTask(baseUrl: String, number: String, dispatcherProvider: DispatcherProvider, dataHandler: DataHandler, viewModel: YourViewModel) { // 避免重复启动下载 if (_downloadState.value is DownloadState.InProgress) return val totalImageCount = 18 // 0~340步长20总共18张,可根据实际需求动态计算 var completedCount = 0 // 初始化为进度0状态 _downloadState.value = DownloadState.InProgress(0, totalImageCount) // 用自定义的全局协程Scope替代GlobalScope,绑Application生命周期 GlobalScope.launch(dispatcherProvider.io()) { for (angle in 0..340 step 20) { val formattedAngle = "%03d".format(angle) dataHandler.MyRequestHandler.getImage( baseUrl.replace("340", formattedAngle), formattedAngle ) { response -> when { response.isSuccessful && response.body() != null && response.networkResponse()?.code() == 200 -> { try { // 原有的图片存储逻辑保持不变 val imageByteArray = response.body()?.source()?.readByteArray() val imageEntity = viewModel.imageEntityConverter( ImageModels("xyz", number, formattedAngle, "ownImage", imageByteArray) ) viewModel.insertNumber(imageEntity) // 原子更新进度 completedCount++ _downloadState.value = DownloadState.InProgress(completedCount, totalImageCount) // 全部下载完成更新状态 if (completedCount == totalImageCount) { _downloadState.value = DownloadState.Success } } catch (e: Exception) { Timber.e(e, "图片下载失败") _downloadState.value = DownloadState.Failed(R.string.general_request_error) } } response.networkResponse()?.code() == 404 -> { _downloadState.value = DownloadState.Failed(R.string.error_fin_not_found_message) } else -> { _downloadState.value = DownloadState.Failed(R.string.general_request_error) } } } } } } // 可选:提供取消下载的入口 fun cancelDownload() { _downloadState.value = DownloadState.Idle // 可补充取消所有未完成网络请求的逻辑 } }
3. 修改当前下载页的逻辑
下载触发后直接返回上一页,不需要在下载回调里处理页面跳转,下载任务会在后台持续运行:
private fun downloadImage() { // 触发全局下载任务 ImageDownloadManager.startDownloadTask(url, number, dispatcherProvider, dataHandler, viewModel) // 直接返回上一页即可,不需要等待下载回调 activity.popSubPage() }
4. 上一页订阅进度更新UI
在上一页的ViewModel中订阅下载状态流,UI层根据状态更新顶部进度条和RecyclerView对应条目:
- ViewModel层逻辑:
class PreviousPageViewModel: ViewModel() { val downloadState = ImageDownloadManager.downloadState .stateIn(viewModelScope, SharingStarted.WhileSubscribed(5000), DownloadState.Idle) }
- UI层逻辑(Activity/Fragment):
lifecycleScope.launch { repeatOnLifecycle(Lifecycle.State.STARTED) { viewModel.downloadState.collect { state -> when(state) { is DownloadState.InProgress -> { // 更新顶部进度条 topProgressBar.visibility = View.VISIBLE val progress = (state.completedCount * 100 / state.totalCount).toInt() topProgressBar.progress = progress // 更新RecyclerView对应下载条目的进度 val targetItem = yourList[downloadItemPosition] targetItem.progress = progress yourAdapter.notifyItemChanged(downloadItemPosition) } DownloadState.Success -> { // 隐藏进度条,更新状态为已完成 topProgressBar.visibility = View.GONE val targetItem = yourList[downloadItemPosition] targetItem.progress = 100 targetItem.isDownloaded = true yourAdapter.notifyItemChanged(downloadItemPosition) Toast.makeText(context, "全部图片下载完成", Toast.LENGTH_SHORT).show() } is DownloadState.Failed -> { topProgressBar.visibility = View.GONE showErrorMessageDialog(R.string.failed, state.errorRes) } DownloadState.Idle -> { topProgressBar.visibility = View.GONE } } } } }
注意事项
- 如果需要同时支持多个并行下载任务,可给每个下载任务加唯一标识,
StateFlow改为存储Map<任务ID, DownloadState>,订阅时过滤对应ID的状态即可 - 建议将回调式的网络请求转为协程挂起函数,方便管理协程取消,避免不必要的资源浪费
- 可根据需求添加下载进度的本地缓存,进程被杀死后重启也能恢复未完成的下载
内容的提问来源于stack exchange,提问作者Manish
相关产品推荐
相关产品推荐

