okhttp添加日志拦截器,上传文件RequestBody.writeTo调用两次

Posted 威威dett

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了okhttp添加日志拦截器,上传文件RequestBody.writeTo调用两次相关的知识,希望对你有一定的参考价值。

okhttp添加日志拦截器,上传文件RequestBody.writeTo调用两次


在使用okhttp上传文件监听进度时,由于OkHttpClient添加了日志拦截器,会导致RequestBody.writeTo调用2次的问题,网上找了好多,浪费好多时间,也没有找到合适的…

废话不多说,下面介绍2种解决方法,最后面分析调用2次的原因和解决思路

方法一:

在添加日志拦截器的时候,控制是否打印日志,如:在上传文件时设置

HttpLoggingInterceptor.Level.NONE

再或者,粗暴点,就直接在上传文件的时候,重新创建一个OkHttpClient实体删掉HttpLoggingInterceptor就行了

 OkHttpClient.Builder builder = new OkHttpClient.Builder().
        connectTimeout(CONNECT_TIME_OUT, TimeUnit.MILLISECONDS)
        .readTimeout(READ_TIME_OUT, TimeUnit.MILLISECONDS)
        .writeTimeout(WRITE_TIME_OUT, TimeUnit.MILLISECONDS)
        .addInterceptor(new HttpLoggingInterceptor()
        .setLevel(HttpLoggingInterceptor.Level.NONE));

这样就可以调用一次了,但是这样就需要在上传文件的时候单独配置一个OkHttpClient实体,无法公用同一个OkHttpClient,有没有什么更优雅的方法呢,下面介绍第二种

方法二:

在监听进度的的RequestBody中重写isOneShot函数法返回值为true就行了(默认false

如:我定义的一个监听进度的类

public class ProgressRequestBody<T> extends RequestBody 
	// 省略代码....
    @Override
    public boolean isOneShot() 
        // 此处返回true,防止添加了日志拦截器导致,
        // 上传文件时writeTo函数2次调用(因为日志拦截器里面也调用了一次)
        return true;
    
 	// 省略代码....

isOneShot函数的官方解释:https://square.github.io/okhttp/4.x/okhttp/okhttp3/-request-body/is-one-shot/

好了,到这里2中方式都介绍完了

下面介绍一下原因,下面是我贴的HttpLoggingInterceptor 4.0的部分源码,主要由于打印日志的时候里面有调用requestBody.writeTo(buffer),解决思路我都写在下面代码的注释里面了

// 官方源码
class HttpLoggingInterceptor @JvmOverloads constructor(
  private val logger: Logger = Logger.DEFAULT
) : Interceptor 

  
  @Throws(IOException::class)
  override fun intercept(chain: Interceptor.Chain): Response 
    val level = this.level

	// 第一种
	// 这里,就是我们介绍的第一种方法的思路,设置Level.NONE
	// 直接return掉了,不会走下面的 requestBody.writeTo(buffer)
	// 也就避免了2次调用的情况
	val request = chain.request()
    if (level == Level.NONE) 
      return chain.proceed(request)
    

    val logBody = level == Level.BODY
    val logHeaders = logBody || level == Level.HEADERS

    val requestBody = request.body

	  if (!logBody || requestBody == null) 
        logger.log("--> END $request.method")
       else if (bodyHasUnknownEncoding(request.headers)) 
        logger.log("--> END $request.method (encoded body omitted)")
       else if (requestBody.isDuplex()) 
        logger.log("--> END $request.method (duplex request body omitted)")
       else if (requestBody.isOneShot()) 
      	// 第二种,根据官方注释,此处 isOneShot返回true时,就可走到这里,防止进入到下面的
      	// else分支里面执行requestBody.writeTo(buffer)		       			
        logger.log("--> END $request.method (one-shot body omitted)")
       else 
      	// 根源:由于我们自定义监听进度的时候回调用一次requestBody.writeTo,
      	// 这里再次调用了,导致调用2次
        val buffer = Buffer()
        requestBody.writeTo(buffer)       
      
    

好了,到此RequestBody.writeTo调用两次的问题和原因都分析完了,如果对你有用,欢迎给个免费的赞

以上是关于okhttp添加日志拦截器,上传文件RequestBody.writeTo调用两次的主要内容,如果未能解决你的问题,请参考以下文章

Retrofit2.0+OkHttp打印Request URL(请求地址参数)

Retrofit2.0+OkHttp打印Request URL(请求地址参数)

Retrofit2.0+OkHttp打印Request URL(请求地址参数)

多部分中的okhttp登录拦截器

java 用于OkHttp和Retrofit的GZip日志拦截器

Retrofit全攻略——进阶篇