Okhttp基本使用及源码分析

该文章所用的okhttp源码版本:4.2.0

基本使用

Okhttp的使用步骤分为三步 :
1.创建OkHttpClient,初始化一些连接参数。

OkHttpClient client = new OkHttpClient();

//如果需要添加拦截器,则需要用构建者模式
OkHttpClient client = new OkHttpClient.Builder()
		.addInterceptor(AppInterceptor())
		.addNetworkInterceptor(NetworkInterceptor())
		.build();

2.创建Request,初始化请求体,method,header,url等信息。

Request request = new Request.Builder()
      .url(url)
      .build();

3.同步&异步执行请求,获得响应Response。

Call call = client.newCall(request);

//同步
Response response = call.execute();

//异步
call.enqueue(new Callback() {
    @Override
    public void onFailure(Call call, IOException e) {
    }

    @Override
    public void onResponse(Call call, final Response response) throws IOException {
    }
});

源码流程

代码流程的前两步主要是初始化两个结构体,第三步开始创建了一个Call对象,该对象真正的实现类是RealCall。call.execute()和call.enqueue()就是同步,异步执行的两个入口,现在分别从这两个路径来分析。

  /** Prepares the [request] to be executed at some point in the future. */
  override fun newCall(request: Request): Call {
    return RealCall.newRealCall(this, request, forWebSocket = false)
  }
  companion object {
    fun newRealCall(
      client: OkHttpClient,
      originalRequest: Request,
      forWebSocket: Boolean
    ): RealCall {
      // Safely publish the Call instance to the EventListener.
      return RealCall(client, originalRequest, forWebSocket).apply {
        transmitter = Transmitter(client, this)
      }
    }
  }

同步执行call.execute()

同步执行代码比较简单,我们看RealCall.execute()

  override fun execute(): Response {
    synchronized(this) {
      //检查该任务是否执行,如已执行则抛出异常
      check(!executed) { "Already Executed" }
      executed = true
    }
    transmitter.timeoutEnter()
    transmitter.callStart()
    try {
      //把该任务加入Dispatcher.runningSyncCalls队列中,加入队列的目的主要用于查询,取消任务等操作
      client.dispatcher.executed(this)
      //真正的执行代码
      return getResponseWithInterceptorChain()
    } finally {
      //任务执行完毕,从队列中移除
      client.dispatcher.finished(this)
    }
  }

最终真正执行任务的是函数getResponseWithInterceptorChain(),该函数也是异步执行的入口,因此我们后面来具体分析。

异步执行call.enqueue()

异步执行我们来看RealCall.enqueue()

  override fun enqueue(responseCallback: Callback) {
    synchronized(this) {
      check(!executed) { "Already Executed" }
      executed = true
    }
    transmitter.callStart()
    client.dispatcher.enqueue(AsyncCall(responseCallback))
  }

主要是构造一个AsyncCall回调,传入Dispatcher.enqueue方法中,我们先来看AsyncCall这个类,

  internal inner class AsyncCall(
    private val responseCallback: Callback
  ) : Runnable {
	...

    fun executeOn(executorService: ExecutorService) {
      assert(!Thread.holdsLock(client.dispatcher))
      var success = false
      try {
        //使用传入的线程池执行run方法
        executorService.execute(this)
        success = true
      } catch (e: RejectedExecutionException) {
		...
        responseCallback.onFailure(this@RealCall, ioException)
      } finally {
        if (!success) {
          client.dispatcher.finished(this) // This call is no longer running!
        }
      }
    }

    //该方法通过上面的函数调用在线程中执行
    override fun run() {
      threadName("OkHttp ${redactedUrl()}") {
        var signalledCallback = false
        transmitter.timeoutEnter()
        try {
          //终止执行网络请求的函数
          val response = getResponseWithInterceptorChain()
          signalledCallback = true
          responseCallback.onResponse(this@RealCall, response)
        } catch (e: IOException) {
          if (signalledCallback) {
            // Do not signal the callback twice!
            Platform.get().log(INFO, "Callback failure for ${toLoggableString()}", e)
          } else {
            responseCallback.onFailure(this@RealCall, e)
          }
        } finally {
          client.dispatcher.finished(this)
        }
      }
    }
  }

AsyncCall实现了Runnable接口,通过executeOn传入的线程池执行run函数。那么这个executeOn方法在哪调用的呢?我们接着看Dispatcher类,它是一个任务执行类,里面初始化了一个线程池,异步任务都是通过该线程池执行。

class Dispatcher constructor() {
...

  /** Ready async calls in the order they'll be run. */
  private val readyAsyncCalls = ArrayDeque<AsyncCall>()

  private var executorServiceOrNull: ExecutorService? = null

  @get:Synchronized
  @get:JvmName("executorService") val executorService: ExecutorService
    get() {
      if (executorServiceOrNull == null) {
        executorServiceOrNull = ThreadPoolExecutor(0, Int.MAX_VALUE, 60, TimeUnit.SECONDS,
            SynchronousQueue(), threadFactory("OkHttp Dispatcher", false))
      }
      return executorServiceOrNull!!
    }
    
  	constructor(executorService: ExecutorService) : this() {
    this.executorServiceOrNull = executorService
  }
...
}

该线程池行为类似于CachedThreadPool,核心线程为0,最大线程很大,队列使用的是同步阻塞队列。正因为队列阻塞,任何新加入的任务都会被立即执行,任务完成后过了超时时间会自动回收。同时,该线程池可以通过构造函数实现自定义替换。下来我们来看enqueue方法。

  internal fun enqueue(call: AsyncCall) {
    synchronized(this) {
      readyAsyncCalls.add(call)

      // Mutate the AsyncCall so that it shares the AtomicInteger of an existing running call to
      // the same host.
      if (!call.get().forWebSocket) {
        val existingCall = findExistingCallWithHost(call.host())
        if (existingCall != null) call.reuseCallsPerHostFrom(existingCall)
      }
    }
    promoteAndExecute()
  }

  private fun promoteAndExecute(): Boolean {
    assert(!Thread.holdsLock(this))

    val executableCalls = mutableListOf<AsyncCall>()
    val isRunning: Boolean
    synchronized(this) {
      val i = readyAsyncCalls.iterator()
      while (i.hasNext()) {
        val asyncCall = i.next()

        if (runningAsyncCalls.size >= this.maxRequests) break // Max capacity.
        if (asyncCall.callsPerHost().get() >= this.maxRequestsPerHost) continue // Host max capacity.

        i.remove()
        asyncCall.callsPerHost().incrementAndGet()
        executableCalls.add(asyncCall)
        runningAsyncCalls.add(asyncCall)
      }
      isRunning = runningCallsCount() > 0
    }

    for (i in 0 until executableCalls.size) {
      val asyncCall = executableCalls[i]
      //在这里回调了AsyncCall的执行方法
      asyncCall.executeOn(executorService)
    }

    return isRunning
  }

enqueue方法最终回调了AsyncCall的run方法,这样就开启了一个新线程来执行任务。因此不论是同步还是异步,执行任务的函数都是getResponseWithInterceptorChain(),他们的差别只是在于是否使用了线程池来执行。

核心流程-拦截器

到这里任务还没有真正执行,上述代码只是决定是否需要新开线程来执行任务。要了解核心代码我们先来了解一下拦截器机制。Okhttp拦截器共分为七层,采用的是责任连模式,及一级一级的向上执行,如果中途有拦截器处理了请求,那么后续拦截器将不再执行。我们可以自定义第一次层应用拦截器和第六层网络拦截器,可以在拦截器里面添加请求头,重定向URL等操作,而无需关注具体拦截的是哪个请求,这种方式可以理解为面向切面(AOP)的编程方式。为了更好的理解拦截器机制,我们来看下面的代码。

  fun getResponseWithInterceptorChain(): Response {
    // Build a full stack of interceptors.
    val interceptors = mutableListOf<Interceptor>()
    interceptors += client.interceptors
    interceptors += RetryAndFollowUpInterceptor(client)
    interceptors += BridgeInterceptor(client.cookieJar)
    interceptors += CacheInterceptor(client.cache)
    interceptors += ConnectInterceptor
    if (!forWebSocket) {
      interceptors += client.networkInterceptors
    }
    interceptors += CallServerInterceptor(forWebSocket)

    val chain = RealInterceptorChain(interceptors, transmitter, null, 0, originalRequest, this,
        client.connectTimeoutMillis, client.readTimeoutMillis, client.writeTimeoutMillis)

    var calledNoMoreExchanges = false
    try {
      val response = chain.proceed(originalRequest)
      if (transmitter.isCanceled) {
        response.closeQuietly()
        throw IOException("Canceled")
      }
      return response
    } catch (e: IOException) {
      calledNoMoreExchanges = true
      throw transmitter.noMoreExchanges(e) as Throwable
    } finally {
      if (!calledNoMoreExchanges) {
        transmitter.noMoreExchanges(null)
      }
    }
  }

这段代码可以看出添加了七个拦截器,并构建了一个RealInterceptorChain,然后调用了它的proceed方法。我们来看RealInterceptorChain.proceed

  //RealInterceptorChain.proceed
  fun proceed(request: Request, transmitter: Transmitter, exchange: Exchange?): Response {
    if (index >= interceptors.size) throw AssertionError()

    calls++
	...
    // Call the next interceptor in the chain.
    //下一个拦截器,index每次会加一指向当前链的下一条
    val next = RealInterceptorChain(interceptors, transmitter, exchange,
        index + 1, request, call, connectTimeout, readTimeout, writeTimeout)
    val interceptor = interceptors[index]

    //注意这行代码,这里实现了责任连模式
    //调用当前拦截器的intercept方法,并在参数中传入下一条链。
    //在intercept方法的实现中会继续调next.proceed()实现链式调用。
    val response = interceptor.intercept(next) ?: throw NullPointerException(
        "interceptor $interceptor returned null")

    // Confirm that the next interceptor made its required call to chain.proceed().
    check(exchange == null || index + 1 >= interceptors.size || next.calls == 1) {
      "network interceptor $interceptor must call proceed() exactly once"
    }

    check(response.body != null) { "interceptor $interceptor returned a response with no body" }

    return response
  }

这段代码是整个链式调用代码的精髓,注意理解是怎样进行链式传递的。为了便于理解,构造了下面的伪代码:

   internal inner class 拦截器1 : Interceptor {
        override fun intercept(拦截器2): Response {
            val response = 拦截器2.proceed(request)
            return response
        }
    }
	
	internal inner class 拦截器2 : Interceptor {
        override fun intercept(拦截器3): Response {
            val response = 拦截器3.proceed(request)
            return response
        }
    }
    
    //整个代码的调用顺序就类似于此
    拦截器1.intercept(拦截器2)
    拦截器2.intercept(拦截器3)
    ...
    ...
    拦截器6.intercept(拦截器7)

那么看到这里,最终执行网络请求的拦截器到底是哪个呢?这样看来肯定是最后一个咯。如果是从缓存中获取,那么最后一个就是CacheInterceptor。如果是正常获取那么最后一个就是CallServerInterceptor,我们来看它的代码。

/** This is the last interceptor in the chain. It makes a network call to the server. */
class CallServerInterceptor(private val forWebSocket: Boolean) : Interceptor {
	val realChain = chain as RealInterceptorChain
	//exchange 真正执行网络请求的类
    val exchange = realChain.exchange()
    val request = realChain.request()
    val requestBody = request.body
    val sentRequestMillis = System.currentTimeMillis()

	//请求头
    exchange.writeRequestHeaders(request)
	...

	//写入请求
	if (requestBody.isDuplex()) {
          // Prepare a duplex body so that the application can send a request body later.
          exchange.flushRequest()
          val bufferedRequestBody = exchange.createRequestBody(request, true).buffer()
          requestBody.writeTo(bufferedRequestBody)
        } else {
          // Write the request body if the "Expect: 100-continue" expectation was met.
          val bufferedRequestBody = exchange.createRequestBody(request, false).buffer()
          requestBody.writeTo(bufferedRequestBody)
          bufferedRequestBody.close()
    }

	//得到响应头
	exchange.responseHeadersEnd(response)
	...
	
	
	//构造响应response
    response = if (forWebSocket && code == 101) {
      // Connection is upgrading, but we need to ensure interceptors see a non-null response body.
      response.newBuilder()
          .body(EMPTY_RESPONSE)
          .build()
    } else {
      response.newBuilder()
          .body(exchange.openResponseBody(response))
          .build()
    }
}

可见真正执行网络请求的类是Exchange,在该类中所有IO请求都和一个叫ExchangeCodec的类相关。这个类是上一条链中构建传递下来的。继续跟代码,

/** Opens a connection to the target server and proceeds to the next interceptor. */
object ConnectInterceptor : Interceptor {

  @Throws(IOException::class)
  override fun intercept(chain: Interceptor.Chain): Response {
    val realChain = chain as RealInterceptorChain
    val request = realChain.request()
    val transmitter = realChain.transmitter()

    // We need the network to satisfy this request. Possibly for validating a conditional GET.
    val doExtensiveHealthChecks = request.method != "GET"
	//这里创建的exchange
    val exchange = transmitter.newExchange(chain, doExtensiveHealthChecks)

    return realChain.proceed(request, transmitter, exchange)
  }
}

  //transmitter.newExchange
  /** Returns a new exchange to carry a new request and response. */
  internal fun newExchange(chain: Interceptor.Chain, doExtensiveHealthChecks: Boolean): Exchange {
	...
	//哈哈,发现codec创建的代码在这里
    val codec = exchangeFinder!!.find(client, chain, doExtensiveHealthChecks)
    val result = Exchange(this, call, eventListener, exchangeFinder!!, codec)
	
	...
  }

exchangeFinder!!.find里面调用了resultConnection.newCodec,这个地方真正创建了socket

  @Throws(SocketException::class)
  internal fun newCodec(client: OkHttpClient, chain: Interceptor.Chain): ExchangeCodec {
    val socket = this.socket!!
    val source = this.source!!
    val sink = this.sink!!
    val http2Connection = this.http2Connection

    return if (http2Connection != null) {
      //http2请求
      Http2ExchangeCodec(client, this, chain, http2Connection)
    } else {
      //http1请求
      socket.soTimeout = chain.readTimeoutMillis()
      source.timeout().timeout(chain.readTimeoutMillis().toLong(), MILLISECONDS)
      sink.timeout().timeout(chain.writeTimeoutMillis().toLong(), MILLISECONDS)
      Http1ExchangeCodec(client, this, source, sink)
    }
  }

以请求头exchange.writeRequestHeaders(request)函数为例,最终调用的是Http2ExchangeCodec.writeRequestHeaders

  //Http2ExchangeCodec.writeRequestHeaders
  override fun writeRequestHeaders(request: Request) {
    if (stream != null) return

    val hasRequestBody = request.body != null
    val requestHeaders = http2HeadersList(request)
    //建立IO流
    stream = connection.newStream(requestHeaders, hasRequestBody)
    // We may have been asked to cancel while creating the new stream and sending the request
    // headers, but there was still no stream to close.
    if (canceled) {
      stream!!.closeLater(ErrorCode.CANCEL)
      throw IOException("Canceled")
    }
    stream!!.readTimeout().timeout(chain.readTimeoutMillis().toLong(), TimeUnit.MILLISECONDS)
    stream!!.writeTimeout().timeout(chain.writeTimeoutMillis().toLong(), TimeUnit.MILLISECONDS)
  }

通过进一步分析connection.newStream方法,发现ohttp最终使用了Okio来封装网络请求。Okio调用的代码还有很大一部分,这里不做具体分析了,有兴趣的读者可以继续往下看源码。

通过分析我们知道了Okhttp有七大拦截器,那么它们的作用又分别是什么呢?

interceptors
应用拦截器,可实现连接日志添加,添加请求头,重定向等自定义操作。

RetryAndFollowUpInterceptor
重试和重定向拦截器,它负责连接失败时重试和重定向操作。

BridgeInterceptor
桥拦截器,连接用户请求信息 和 HTTP 请求的桥梁。负责把用户请求转换为发送到服务器的请求,并把服务器的响应转化为用户需要的响应

CacheInterceptor
缓存拦截器,负责读取缓存、更新缓存,判断请求是否直接从缓存中获取

ConnectInterceptor - Opens a connection to the target server and proceeds to the next interceptor
连接拦截器,负责和服务器建立连接

networkInterceptors -
网络拦截器,可自定义

CallServerInterceptor -This is the last interceptor in the chain. It makes a network call to the server
负责向服务器发送数据,从服务器读取响应数据

Okhttp中的每一个拦截器都负责不能的职能,并且值得深入去分析,这也是Okhttp的精华所在。

总结

Okhttp异步调用采用了线程池来实现,该线程池类似CachedThreadPool,有任务来时直接执行无需等待。

Okhttp采用责任链模式工作模式,按照顺序依次调用拦截器的intercept方法把任务往后传递,如果中途有拦截器处理了事件,那么不在传递下去,整个事件结束,如果没有人处理该事件那么传递给最后一个拦截器处理。

Okhttp可通过自定义应用拦截器和网络拦截器来帮助我们实现对网络请求的灵活控制。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章