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);
答案 0 :(得分:20)
下面的解决方案可能对您有所帮助,但可能为时已晚。 :P
删除Api客户端中不会执行writeTo()函数两次的HttpLoggingInterceptor
对象。基本上,HttpLoggingInterceptor
通过调用writeTo()然后再次加载数据缓冲区(用于内部日志记录)调用writeTo()将数据上传到服务器。
HttpLoggingInterceptor logging = new HttpLoggingInterceptor();
logging.setLevel(HttpLoggingInterceptor.Level.BODY);
httpClient.addInterceptor(logging);
答案 1 :(得分:3)
将日志级别从BODY降低到HEADERS,BASIC或NONE为我解决了这个问题
答案 2 :(得分:2)
我想出了两次调用writeTo()方法的另一种情况。 我使用没有Retrofit和HttpLoggingInterceptor的OkHttpClient,我有两次调用问题。
解决方案:将Android Studio升级到3.1.1后出现问题,并在运行项目配置中启用高级配置文件。因此,禁用高级分析。
答案 3 :(得分:0)
将级别从 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);
}
}
}
答案 4 :(得分:0)
如果您使用 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
}
是链中的最后一个拦截器(根据文档)。您可以在进一步处理之前检查堆栈跟踪。是的,这很丑陋。但是这样您就不必修改拦截器或在其他人添加另一个拦截器时为细微的错误留出空间。
const { name, age, setName, setAge } = useContext(MyContext);
<TextInput placeholder={"Enter Name"} onChangeText={(text) => setName(text)} />
<TextInput placeholder={"Enter Age"} onChangeText={(text) => setAge(text)} />