Android中返回Activity后如何恢复显示下载进度条?
解决Adapter下载进度在界面重建后恢复显示的方案
这个问题我之前帮不少开发者解决过,核心就是要把下载状态和进度"存住",同时让界面重建后能重新跟上后台任务的进度更新。下面一步步给你讲具体实现思路和代码:
1. 用全局状态容器记录下载任务信息
首先得创建一个单例类,用来保存所有下载任务的进度和运行状态——不管界面怎么销毁重建,这个全局容器里的状态都不会丢:
public class DownloadTaskManager { private static DownloadTaskManager instance; // 用任务唯一ID做key,存储进度和运行状态 private HashMap<String, DownloadStatus> taskStatusMap = new HashMap<>(); // 保存正在运行的任务引用(用弱引用避免内存泄漏) private HashMap<String, WeakReference<DownloadTask>> runningTasks = new HashMap<>(); public static DownloadTaskManager getInstance() { if (instance == null) { instance = new DownloadTaskManager(); } return instance; } // 更新任务进度 public void updateTaskProgress(String taskId, int progress) { DownloadStatus status = taskStatusMap.getOrDefault(taskId, new DownloadStatus()); status.progress = progress; status.isRunning = true; taskStatusMap.put(taskId, status); } // 获取任务当前状态 public DownloadStatus getTaskStatus(String taskId) { return taskStatusMap.getOrDefault(taskId, new DownloadStatus()); } // 保存正在运行的任务 public void addRunningTask(String taskId, DownloadTask task) { runningTasks.put(taskId, new WeakReference<>(task)); } // 获取正在运行的任务 public DownloadTask getRunningTask(String taskId) { WeakReference<DownloadTask> taskRef = runningTasks.get(taskId); return taskRef != null ? taskRef.get() : null; } // 任务完成后清理记录 public void finishTask(String taskId) { taskStatusMap.remove(taskId); runningTasks.remove(taskId); } // 封装进度和状态的内部类 public static class DownloadStatus { public int progress; public boolean isRunning; } }
2. 修改AsyncTask,绑定全局状态与界面回调
让你的AsyncTask每次更新进度时,同步更新全局容器的状态,同时用弱引用持有界面回调,避免内存泄漏:
public class DownloadTask extends AsyncTask<Void, Integer, Void> { private String taskId; // 任务唯一标识,比如用文件URL的哈希值生成 private WeakReference<DownloadProgressCallback> callbackRef; private DownloadTaskManager taskManager; public DownloadTask(String taskId, DownloadProgressCallback callback) { this.taskId = taskId; this.callbackRef = new WeakReference<>(callback); this.taskManager = DownloadTaskManager.getInstance(); } @Override protected Void doInBackground(Void... params) { final int TIMEOUT_CONNECTION = 5000; // 这里写你的下载逻辑,比如网络请求、文件写入等 int currentProgress = 0; while (currentProgress < 100 && !isCancelled()) { // 模拟下载进度更新(替换成你的真实进度计算) currentProgress += 1; // 更新全局状态 taskManager.updateTaskProgress(taskId, currentProgress); // 发布进度到主线程 publishProgress(currentProgress); try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } } // 任务完成后清理全局记录 taskManager.finishTask(taskId); return null; } @Override protected void onProgressUpdate(Integer... values) { super.onProgressUpdate(values); DownloadProgressCallback callback = callbackRef.get(); if (callback != null) { callback.onProgressUpdated(taskId, values[0]); } } // 定义进度更新的回调接口 public interface DownloadProgressCallback { void onProgressUpdated(String taskId, int progress); } }
3. 在Adapter中绑定进度,重建界面时自动恢复
在Adapter的onBindViewHolder方法里,先从全局容器获取任务状态,如果任务正在运行,就直接恢复进度条的显示和当前进度,同时重新绑定回调监听后续更新:
@Override public void onBindViewHolder(@NonNull MyViewHolder holder, int position) { DownloadItem item = mDataList.get(position); // 生成任务唯一ID(比如用item的下载URL) String taskId = generateTaskId(item.getDownloadUrl()); // 从全局管理器获取任务当前状态 DownloadTaskManager.DownloadStatus status = DownloadTaskManager.getInstance().getTaskStatus(taskId); if (status.isRunning) { // 恢复进度条显示和当前进度 holder.progressBar.setVisibility(View.VISIBLE); holder.progressBar.setProgress(status.progress); // 绑定正在运行的任务的回调 DownloadTask runningTask = DownloadTaskManager.getInstance().getRunningTask(taskId); if (runningTask != null) { runningTask.setCallback(new DownloadTask.DownloadProgressCallback() { @Override public void onProgressUpdated(String taskId, int progress) { // 检查ViewHolder是否还对应当前位置,避免复用错误 if (holder.getAdapterPosition() == position) { holder.progressBar.setProgress(progress); } } }); } } else { holder.progressBar.setVisibility(View.GONE); } // 下载按钮点击事件 holder.downloadBtn.setOnClickListener(v -> { DownloadTask newTask = new DownloadTask(taskId, new DownloadTask.DownloadProgressCallback() { @Override public void onProgressUpdated(String taskId, int progress) { if (holder.getAdapterPosition() == position) { holder.progressBar.setVisibility(View.VISIBLE); holder.progressBar.setProgress(progress); } } }); newTask.execute(); // 将任务保存到全局管理器 DownloadTaskManager.getInstance().addRunningTask(taskId, newTask); }); } // 生成唯一任务ID的工具方法 private String generateTaskId(String url) { return String.valueOf(url.hashCode()); }
4. 更可靠的进阶方案:用ViewModel + Coroutines
AsyncTask本身在配置变更(比如界面重建)时的稳定性一般,更推荐用ViewModel管理下载任务(ViewModel在配置变更时不会被销毁),配合Coroutines实现异步下载,再用LiveData推送进度:
public class DownloadViewModel extends ViewModel { private MutableLiveData<Map<String, Integer>> progressLiveData = new MutableLiveData<>(); private CoroutineScope downloadScope = CoroutineScope(Dispatchers.IO + SupervisorJob()); public LiveData<Map<String, Integer>> getProgressLiveData() { return progressLiveData; } public void startDownload(String taskId, String downloadUrl) { downloadScope.launch { int progress = 0; while (progress < 100 && !isActive()) { // 真实下载逻辑替换这里的模拟代码 progress += 1; // 更新进度到LiveData Map<String, Integer> progressMap = progressLiveData.getValue(); if (progressMap == null) progressMap = new HashMap<>(); progressMap.put(taskId, progress); progressLiveData.postValue(progressMap); delay(100); } // 完成后移除进度记录 Map<String, Integer> progressMap = progressLiveData.getValue(); if (progressMap != null) { progressMap.remove(taskId); progressLiveData.postValue(progressMap); } } } @Override protected void onCleared() { super.onCleared(); downloadScope.cancel(); } }
然后在你的Activity/Fragment中观察LiveData,通知Adapter更新:
DownloadViewModel viewModel = new ViewModelProvider(this).get(DownloadViewModel.class); viewModel.getProgressLiveData().observe(this, progressMap -> { mAdapter.notifyDataSetChanged(); });
这样界面重建后,ViewModel依然保留着下载任务的状态,LiveData会自动把最新进度推送给新的界面,Adapter就能直接刷新显示了。
内容的提问来源于stack exchange,提问作者Latha
相关产品推荐
相关产品推荐

