OkHttp 4源码(1)— OkHttp初始化和请求构造分析

本文基于OkHttp 4.3.1源码分析
OkHttp - 官方地址
OkHttp - GitHub代码地址

概括

本篇主要从OkHttp的两个请求示例开始,对Okhttp的初始化工作,和请求从构造、分发到执行的流程进行源码分析介绍

OkHttp整体流程(本文覆盖红色部分)

本文覆盖代码流程图

示例

使用OkHttp一般流程,初始化一个共享OkHttpClient,构建Request,然后OkHttpClient根据Request构建Call,接着执行call,最后进行Response处理

同步请求

public class GetExample {
  OkHttpClient client = new OkHttpClient(); // 构建共享的Client

  String run(String url) throws IOException {
    Request request = new Request.Builder() // 构建request
        .url(url)
        .build();
    // 构建Call,执行
    try (Response response = client.newCall(request).execute()) {
      return response.body().string();
    }
  }

  public static void main(String[] args) throws IOException {
    GetExample example = new GetExample();
    String response = example.run("https://raw.github.com/square/okhttp/master/README.md");
    System.out.println(response);
  }
}

异步请求

public final class AsynchronousGet {
  private final OkHttpClient client = new OkHttpClient();// 构建共享Client

  public void run() throws Exception {
    // 构建Request
    Request request = new Request.Builder()
        .url("http://publicobject.com/helloworld.txt")
        .build();
    // 构建Call,执行,回调接受处理
    client.newCall(request).enqueue(new Callback() {
      @Override public void onFailure(Call call, IOException e) {
        e.printStackTrace();
      }

      @Override public void onResponse(Call call, Response response) throws IOException {
        try (ResponseBody responseBody = response.body()) {
          if (!response.isSuccessful()) throw new IOException("Unexpected code " + response);

          Headers responseHeaders = response.headers();
          for (int i = 0, size = responseHeaders.size(); i < size; i++) {
            System.out.println(responseHeaders.name(i) + ": " + responseHeaders.value(i));
          }

          System.out.println(responseBody.string());
        }
      }
    });
  }

  public static void main(String... args) throws Exception {
    new AsynchronousGet().run();
  }
}

源码分析

构建OkHttpClient

  • OkHttpClient是Call的一个工厂类,OkHttpClient应该是共享的,或者说是单例
  • 可以通过newBuilder来自定义Client
  • 没有必要关心 关闭和资源释放
/*
 * Factory for [calls][Call], which can be used to send HTTP requests and read their responses.
 * ## OkHttpClients Should Be Shared
 * ## Customize Your Client With newBuilder()
 * ## Shutdown Isn't Necessary
 * /
open class OkHttpClient internal constructor(
  builder: Builder
) : Cloneable, Call.Factory, WebSocket.Factory {
    // 3. 成员变量初始化
    ... 
    
    // 1. 内部无参数构造函数
    constructor() : this(Builder())
    
    // 4. OkHttpClient函数初始化 
    init {
        // 初始化证书和拦截器等判断逻辑
        ...
    }
    // 2. Builder构造函数
    class Builder constructor() {
        ...
    } 
}

OkHttpClient.Builder

Builder模式,提供自定义配置化能力,同时有一份无需关心的默认配置

  class Builder constructor() {
        internal var dispatcher: Dispatcher = Dispatcher()
        internal var connectionPool: ConnectionPool = ConnectionPool()
        internal val interceptors: MutableList<Interceptor> = mutableListOf()
        internal val networkInterceptors: MutableList<Interceptor> = mutableListOf()
        internal var eventListenerFactory: EventListener.Factory = EventListener.NONE.asFactory()
        internal var retryOnConnectionFailure = true
        internal var authenticator: Authenticator = Authenticator.NONE
        internal var followRedirects = true
        internal var followSslRedirects = true
        internal var cookieJar: CookieJar = CookieJar.NO_COOKIES
        internal var cache: Cache? = null
        internal var dns: Dns = Dns.SYSTEM
        internal var proxy: Proxy? = null
        internal var proxySelector: ProxySelector? = null
        internal var proxyAuthenticator: Authenticator = Authenticator.NONE
        internal var socketFactory: SocketFactory = SocketFactory.getDefault()
        internal var sslSocketFactoryOrNull: SSLSocketFactory? = null
        internal var x509TrustManagerOrNull: X509TrustManager? = null
        internal var connectionSpecs: List<ConnectionSpec> = DEFAULT_CONNECTION_SPECS
        internal var protocols: List<Protocol> = DEFAULT_PROTOCOLS
        internal var hostnameVerifier: HostnameVerifier = OkHostnameVerifier
        internal var certificatePinner: CertificatePinner = CertificatePinner.DEFAULT
        internal var certificateChainCleaner: CertificateChainCleaner? = null
        internal var callTimeout = 0
        internal var connectTimeout = 10_000
        internal var readTimeout = 10_000
        internal var writeTimeout = 10_000
        internal var pingInterval = 0
    }   

OkHttpClient成员变量初始化

  • 初始化成员变量
  • JvmName是为了支持兼容3.x
  @get:JvmName("dispatcher") val dispatcher: Dispatcher = builder.dispatcher

  @get:JvmName("connectionPool") val connectionPool: ConnectionPool = builder.connectionPool

  @get:JvmName("interceptors") val interceptors: List<Interceptor> =
      builder.interceptors.toImmutableList()

  @get:JvmName("networkInterceptors") val networkInterceptors: List<Interceptor> =
      builder.networkInterceptors.toImmutableList()

  @get:JvmName("eventListenerFactory") val eventListenerFactory: EventListener.Factory =
      builder.eventListenerFactory

  @get:JvmName("retryOnConnectionFailure") val retryOnConnectionFailure: Boolean =
      builder.retryOnConnectionFailure

  @get:JvmName("cookieJar") val cookieJar: CookieJar = builder.cookieJar

  @get:JvmName("cache") val cache: Cache? = builder.cache

  ....

构建Request

Request 对应HTTP请求中的Request,OkHttp依旧是Builder构建模式构建Request

Request.Builder

支持url、method、headers、body的配置

 open class Builder {
    internal var url: HttpUrl? = null 
    internal var method: String
    internal var headers: Headers.Builder
    internal var body: RequestBody? = null
    // 构造Request
    open fun build(): Request {
      return Request(
          checkNotNull(url) { "url == null" },
          method,
          headers.build(),
          body,
          tags.toImmutableMap()
      )
    }
}

Request

通过Request.Builder构造Request实例

class Request internal constructor(
  @get:JvmName("url") val url: HttpUrl,
  @get:JvmName("method") val method: String,
  @get:JvmName("headers") val headers: Headers,
  @get:JvmName("body") val body: RequestBody?,
  internal val tags: Map<Class<*>, Any>
) {
    ...
}

构建Call

OkHttpClient.newCall

OkHttpClient 实现了Call.Factory,作为Call的构造工厂类

  override fun newCall(request: Request): Call {
    // 执行 RealCall的构造call方法
    return RealCall.newRealCall(this, request, forWebSocket = false)
  }

RealCall.newRealCall

构造Call真正方法,另外创建了一个 发射器,接下来先了解下Call

companion object {
    fun newRealCall(
      client: OkHttpClient,
      originalRequest: Request,
      forWebSocket: Boolean
    ): RealCall {
      
      return RealCall(client, originalRequest, forWebSocket).apply {
       // 构造了一个 发射器,它是应用层和网络层交互的桥梁,后面会着重介绍
        transmitter = Transmitter(client, this) 
      }
    }
  }

RealCall

Call定义为一个准备好执行的请求,它是能被取消的,且它只能被执行一次(http请求也是一次执行)
包括一个核心成员变量 Transmitter ,两个重要方法 execute(同步) 和 enqueue(异步)


internal class RealCall private constructor(
  val client: OkHttpClient,
  /** The application's original request unadulterated by redirects or auth headers. */
  val originalRequest: Request,
  val forWebSocket: Boolean
) : Call {
 // 发射机
 private lateinit var transmitter: Transmitter
 
 // 同步请求执行方法
 override fun execute(): Response {
  ...
 }
  // 异步请求执行方法 
  override fun enqueue(responseCallback: Callback) {
    ...
  }}
  
  // 取消请求
  override fun cancel() {
    transmitter.cancel()
  }

同步请求

RealCall.execute

  • 请求前校验逻辑,仅能执行一次,和过期时间逻辑判断逻辑
  • 通知请求start事件,便于metrics 指标数据收集
  • 将call加入分发器的同步请求队列中
  • 通过拦截器责任链模式进行请求和返回一系列逻辑处理
  override fun execute(): Response {
    // 检查是否已经执行
    synchronized(this) {
      check(!executed) { "Already Executed" }
      executed = true
    }
    // 过期时间逻辑,如果配置了会有WatchDog线程进行Watch然后执行退出逻辑
    transmitter.timeoutEnter()
    // 通知start,最后会通过 EventListener 发出时间,主要目的是收集 metrics events
    transmitter.callStart()
    try {
      // 调用client的dispatcher分发器执行call(将call加入同步call队列)
      client.dispatcher.executed(this)
      // 通过拦截器责任链模式进行请求和返回处理等一系类逻辑
      return getResponseWithInterceptorChain()
    } finally {
      client.dispatcher.finished(this)
    }
  }

RealCall.getResponseWithInterceptorChain

  • 配置拦截器,所有请求和响应处理逻辑解耦到各个拦截器负责模块
  • 构造拦截器链式调用处理类RealInterceptorChain实例
  • chain.proceed 进行拦截器的链式调用
  fun getResponseWithInterceptorChain(): Response {
    // 构建所有的拦截器
    val interceptors = mutableListOf<Interceptor>()
    interceptors += client.interceptors // client配置的拦截器
    interceptors += RetryAndFollowUpInterceptor(client) // 重试机制拦截器
    interceptors += BridgeInterceptor(client.cookieJar) // 请求和返回桥(http信息配置和解析)拦截器
    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)
      }
    }
  }

异步请求

RealCall.enqueue

  • 构造一个异步call
  • 调用dispatcher 入队AsyncCall
  override fun enqueue(responseCallback: Callback) {
    synchronized(this) { 
      check(!executed) { "Already Executed" }
      executed = true
    }
    transmitter.callStart()
    // 构造 AsyncCall ,接着分发器入队操作
    client.dispatcher.enqueue(AsyncCall(responseCallback)) 
  }

Dispatcher 分发器队Call进行分发执行

参考:线程池理解

  • 初始化时,构造分发器的线程池,及对应执行参数
  • 主要管理异步请求的处理(异步Call入队、并发执行)
class Dispatcher constructor() {
  // 默认最大请求数 64
  @get:Synchronized var maxRequests = 64
  // 默认最大并发Host 5
  @get:Synchronized var maxRequestsPerHost = 5
  // 线程池执行器 默认创建可缓存线程池 
  @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!!
    }
    
  /** 准备好的异步Call对列 */
  private val readyAsyncCalls = ArrayDeque<AsyncCall>()

  /** 执行中异步call对列 */
  private val runningAsyncCalls = ArrayDeque<AsyncCall>()

  /** 同步call对列 */
  private val runningSyncCalls = ArrayDeque<RealCall>()
  
  // 异步Call 入队操作  
  internal fun enqueue(call: AsyncCall) {
    synchronized(this) {
      readyAsyncCalls.add(call)
      // same host情况下的复用逻辑处理
      if (!call.get().forWebSocket) {
        val existingCall = findExistingCallWithHost(call.host())
        if (existingCall != null) call.reuseCallsPerHostFrom(existingCall)
      }
    }
    promoteAndExecute() // 准备线程池执行器,及执行
  }

// 执行Call
private fun promoteAndExecute(): Boolean {
    this.assertThreadDoesntHoldLock()

    val executableCalls = mutableListOf<AsyncCall>()
    val isRunning: Boolean
    synchronized(this) {
      // 遍历所有的ready 异步 call
      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.executeOn(executorService) // 将线程执行器传入call,在Call中进行执行
    }

    return isRunning
  }
  
  /** 同步call,添加到队列 */
  @Synchronized internal fun executed(call: RealCall) {
    runningSyncCalls.add(call)
  }

}

AsyncCall执行

  • executeOn ,线程池执行器执行Runnable
  • run,通过拦截器进行请求和获取响应结果
fun executeOn(executorService: ExecutorService) {
    client.dispatcher.assertThreadDoesntHoldLock()
    
    var success = false
    try {
        // 线程池 执行器执行
        executorService.execute(this)
        success = true
    }
}
// 执行方法
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) {
            ...
        } finally {
            client.dispatcher.finished(this)
        }
    }
}

下一篇 OkHttp 4源码(2)— 拦截器机制分析

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,293评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,604评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,958评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,729评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,719评论 5 366
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,630评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,000评论 3 397
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,665评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,909评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,646评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,726评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,400评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,986评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,959评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,197评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 44,996评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,481评论 2 342

推荐阅读更多精彩内容