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

如何解决Retrofit单文件上传时进度条重复显示的问题?

解决Retrofit上传文件时进度条重复显示的问题

我懂你遇到的糟心问题——传单个文件进度条居然跑了两次全程,这体验确实够别扭的。你猜的方向没错,核心原因是你的ProgressRequestBody里的writeTo方法被调用了两次,大概率是OkHttp的自动重试/重定向机制搞的鬼(比如遇到临时重定向,OkHttp会默默重试一次请求)。

下面是调整后的ProgressRequestBody代码,通过添加状态标记来彻底避免重复的进度回调:

public class ProgressRequestBody extends RequestBody {
    private File mFile;
    private UploadCallbacks mListener;
    private static final int DEFAULT_BUFFER_SIZE = 2048;
    // 标记是否已经完成过一次完整的上传流程
    private boolean mIsUploadCompleted = false;
    // 记录已回调过的最高进度,避免重复推送相同/更低的进度值
    private int mReportedMaxProgress = 0;

    public interface UploadCallbacks {
        void onProgressUpdate(int percentage);
        void onError();
        void onFinish();
    }

    public ProgressRequestBody(File file, UploadCallbacks listener) {
        mFile = file;
        mListener = listener;
    }

    @Override
    public MediaType contentType() {
        return MediaType.parse("image/*");
    }

    @Override
    public long contentLength() throws IOException {
        return mFile.length();
    }

    @Override
    public void writeTo(BufferedSink sink) throws IOException {
        Source source = null;
        try {
            source = Okio.source(mFile);
            long totalWritten = 0;
            long read;

            while ((read = source.read(sink.buffer(), DEFAULT_BUFFER_SIZE)) != -1) {
                totalWritten += read;
                sink.flush();
                int currentPercentage = (int) (100 * totalWritten / mFile.length());

                // 只在进度有提升且未完成上传时,才推送进度更新
                if (currentPercentage > mReportedMaxProgress && !mIsUploadCompleted) {
                    mListener.onProgressUpdate(currentPercentage);
                    mReportedMaxProgress = currentPercentage;
                }
            }

            // 确保完成回调只触发一次
            if (!mIsUploadCompleted) {
                // 兜底设置进度为100,避免字节计算误差导致进度停在99%
                mListener.onProgressUpdate(100);
                mListener.onFinish();
                mIsUploadCompleted = true;
            }
        } catch (IOException e) {
            // 错误回调同样只触发一次
            if (!mIsUploadCompleted) {
                mListener.onError();
                mIsUploadCompleted = true;
            }
            throw e;
        } finally {
            Util.closeQuietly(source);
        }
    }
}

关键优化点:

  1. mIsUploadCompleted标记:确保完成、错误回调只会触发一次,彻底杜绝重复结束提示
  2. mReportedMaxProgress变量:避免重复推送相同或更低的进度值,防止进度条出现"回退"或重复跳动
  3. 兜底100%进度:避免因字节计算的微小误差,导致进度条卡在99%无法完成

这样修改后,哪怕writeTo被调用多次,进度条也只会完整走一次从0到100的流程,不会再出现重复显示的问题。

内容的提问来源于stack exchange,提问作者saneesh

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.28 06:23:52