OkHttp网络框架源码分析

300 阅读4分钟

OkHttp同步使用方法

OkHttp异步使用方法

注意:

  • onResponse和onFailure都是在工作线程(子线程)中执行的。

同步和异步的区别

  • 发起请求的方法调用(步骤3)
    -- 同步调用call.execute;异步调用call.enqueue,并传入Callback对象。
  • 阻塞线程与否
    -- 同步会阻塞当前线程;异步不会阻塞当前线程,相反,会开启一个新的线程完成网络请求的操作。

异步/同步请求的流程

  • AsynCall可以理解为Runnable。
  • Dispatcher是分发器类,由该分发器类决定异步请求是执行还是就绪。
  • Call是接口,真正的操作在其实现类RealCall中。
  • 同步请求实际上就是通过executed方法把请求(RealCall对象)添加到同步请求队列(runningSyncCalls)当中。
    -- 进入call.execute方法
    • getResponseWithInterceptorChain方法:拦截器链,内部依次调用拦截器,详细见后。

      -- 进入Dispatcher的executed方法

      -- 同步请求队列runningSyncCallsDispatcher类中定义。
      Dispatcher类中定义了3种队列(异步就绪/缓存等待请求队列readyAsyncCalls, 异步执行队列runningAsyncCalls, 同步请求队列runningSyncCalls)。

      --进入finished方法

      --在进入调用的finished方法

    1. 首先执行call.remove(call),从当前队列中移除同步请求(promoteCalls=false),如果不能移除则抛出异常。
    2. 再执行runningCallsCount()计算目前还在运行的请求。
      -- 进入runningCallsCount方法
    • runningCallsCount方法返回了正在执行的异步请求和正在执行的同步请求数量之和。
    1. runningCallsCount == 0时表示Dispatch分发器类中没有可运行的请求。

同步请求步骤总结

异步请求

异步请求的关键方法enqueue

-- 异步请求调用的是Call的实现类RealCall中的enqueue

  • AsyncCall是Runnable的实现类。

    -- dispatcher().enqueue方法

    满足条件: 1. 异步执行runningAsyncCalls个数<最大请求数maxRequests; 2. 正在运行的每个主机的请求runningCallsForHost(call)<设置的主机请求数的最大值maxRequestsPerHost
    若满足条件,将AsyncCall添加到异步执行队列runningAsyncCalls中,再通过线程池执行该异步请求executorService().execute(call)
    若不满足条件,则将AsyncCall添加到等待队列readyAsyncCalls中。

    • 调用executorService().execute(call)时,由于executorService()返回的是一个线程池(见下),所以实际上是调用每个子线程(AsyncCall)的run方法。
      -- 进入executorService()

      • 使用synchronized关键字保证executorService线程池是单例。
      • 该方法返回的就是一个线程池对象executorService。
      • ThreadPoolExecutor的参数:
        参数1: 核心线程池数量,设为0表示空闲一段时间后,会把所有的线程全部销毁。
        参数2: 最大线程数,设为整型的最大值后理论上可以无限扩充线程最大值,但实际上受限于maxRequests(见上)。
        参数3: 当线程数大于核心线程数,多余的空闲线程最大的存活时间,这里设为60s。

      -- 进入AsyncCall的父类NamedRunnable

      -- NamedRunnable的run()

      • AsyncCall的execute()实现了这里的execute()
        -- 进入AsyncCall的execute() (关联1)
      • 所以为什么上面说onFailureonResponse是在子线程中执行的(这里的execute()在NamedRunnable的run()中)。

enqueue方法总结

  • 1中判断当前call是指判断当前call是否只执行了一次(if(executed))。

Dispatcher类

主要作用是维护请求队列。

Dispatcher类中定义了线程池(executorService),正在执行的异步请求队列(runningAsyncCalls)和就绪等待缓存的异步请求队列(readyAsyncCalls)。注意readyAsyncCalls包括已经取消但还未结束的异步请求。

总结 -异步请求流程

Dispatcher用于维护异步/同步请求队列。若是异步的情况,Dispatcher中定义了线程池ThreadPool用于执行异步的网络请求。
若满足条件 1. 异步执行runningAsyncCalls个数<最大请求数maxRequests; 2. 正在运行的每个主机的请求runningCallsForHost(call)<设置的主机请求数的最大值maxRequestsPerHost时,通过Dispatcher直接把异步请求放到正在执行的线程池队列(runningAsyncCalls.add(call))中,通过线程池ThreadPool开启任务(executorService().execute(call))。
若不满足条件,Dispatcher把异步请求放到就绪等待队列(readyAsyncCalls)中,当异步执行队列runningAsyncCalls有空闲位置时,则从readyAsyncCalls中取出优先级高的请求放到runningAsyncCalls请求队列中执行。

  • Q:readyAsyncCalls队列中的线程在什么时候才会被执行? 见AsyncCall的execute() (关联1),最后的finally中总会执行的dispatcher().finished(this)
    -- 进入dispatcher().finished(this)
    -- 进入3个参数的finished
    • 步骤1: 调用calls.remove(call),把异步请求从正在执行的异步请求队列中移除;
      步骤2: 调用promoteCalls(),调整任务队列,由于线程不安全,所以加上synchronized同步代码块;
      -- 进入promoteCalls()
      其中的操作就是遍历异步等待请求队列readyAsyncCalls.iterator(),移除最后一个请求任务(i.remove())并加至异步执行队列(runningAsyncCalls.add(call))并执行(execute(call))。
      步骤3: 调用runningCallsCount(),重新计算在执行的线程数量;
      -- 进入runningCallsCount()
      返回的就是正在执行的异步请求和正在执行的同步请求之和。