Retrofit 2 RequestBody writeTo() 方法调用了两次

Retrofit 2 RequestBody writeTo() method called twice

Retrofit 2 RequestBody writeTo()方法调用了两次,我使用的代码如下:

ProgressRequestBody requestVideoFile = new ProgressRequestBody(videoFile, new ProgressRequestBody.UploadCallbacks() {

    VideoUploadStore store = new VideoUploadStore();

    @Override
    public void onProgressUpdate(int percentage) {
        if (!mIsCancelled) {
            Log.i("UploadServiceManager", "Read Percentage : " + percentage);
            data.setUploadPercentage(percentage);
            store.updateUploadData(data);
        }
    }

    @Override
    public void onError() {
        if(!mIsCancelled) {
            data.setUploadPercentage(0);
            store.updateUploadData(data);
        }
    }

    @Override
    public void onFinish() {

    }
});
MultipartBody.Part multipartVideo = MultipartBody.Part.createFormData("File", videoFile.getName(), requestVideoFile);

下面的解决方案可能对您有所帮助,尽管可能为时已晚。 :p

删除 Api 客户端中不会执行 writeTo() 函数的 HttpLoggingInterceptor 对象 twice.Basically,HttpLoggingInterceptor 首先加载数据缓冲区(用于内部日志记录目的)通过调用 writeTo() 然后再次调用 writeTo() 将数据上传到服务器。

  HttpLoggingInterceptor logging = new HttpLoggingInterceptor();
  logging.setLevel(HttpLoggingInterceptor.Level.BODY);
  httpClient.addInterceptor(logging);

将日志级别从 BODY 降低到 HEADERS、BASIC 或 NONE 为我解决了这个问题

我发现了另一种情况,两次调用 writeTo() 方法。 我使用没有 Retrofit 和 HttpLoggingInterceptor 的 OkHttpClient,我有 两次调用 问题。

解决方法:将AndroidStudio升级到3.1.1并在运行项目配置中启用Advanced Profiling后出现该问题。所以禁用高级分析。

将级别从 BODY 降低到 HEADERS 并删除 HttpLoggingInterceptor 对我来说不是解决方案,因为它解释了调用 api 的情况。你可以像这样启动计数器变量, private int firstTimeCounter = 0; 然后

@Override
public void writeTo(BufferedSink sink) throws IOException {
    firstTimeCounter += 1;

    .......
   .......

    if(firstTimeCounter==2){
        try {
            while (total != file.length()) {
                read = source.read(sink.buffer(), SEGMENT_SIZE);
                total += read;
                Log.e("progress ", total + " %");
                sink.flush();
            }
        } finally {
            okhttp3.internal.Util.closeQuietly(source);
        }
    }
}  

如果您使用 writeTo() 来跟踪文件上传进度,则需要区分 writeTo() 的调用者。基本上 writeTo() 可以被链中的任何拦截器调用,例如任何日志记录拦截器,例如 HttpLoggingInterceptor/OkHttpProfilerInterceptor/StethoInterceptor,此方法不提供任何上下文。

最简单的方法(正如其他答案所指出的)是摆脱那些需要访问请求主体的拦截器。但这可能并不总是可行的。

另一种解决方案是利用服务器调用由 CallServerInterceptor 执行的事实,它是链中的最后一个拦截器(根据文档)。您可以在进一步处理之前检查堆栈跟踪。是的,这很丑陋。但这样一来,当其他人出现并添加另一个拦截器时,您不必修改拦截器或为细微错误留出空间。

override fun writeTo(sink: BufferedSink) {
    val isCalledByCallServerInterceptor = Thread.currentThread().stackTrace.any { stackTraceElement ->
        stackTraceElement.className == CallServerInterceptor::class.java.canonicalName
    }

    // TODO
}