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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.21 04:19:54