Android OkHttp 源码解析 - 整体流程分析

一、前言


注:本文 OkHttp 源码解析基于 v3.14.2 ,下文中涉及到展示 OkHttp 源码的地方,都采用在 AS 里打开源码并以截图的方式展现出来,这样更加直观。

 

二、简单使用


请求分为同步和异步两种,同步请求通过 调用 Call.exectute() 方法直接返回当前请求的 Response,异步请求调用 Call.enqueue() 方法将请求(AsyncCall)添加到请求队列中去,并通过回调(Callback)获取服务器返回的结果,代码如下:

//1.新建OKHttpClient客户端
OkHttpClient client = new OkHttpClient();
//2.新建一个Request对象
Request request = new Request.Builder()
        .url(url)
        .build();
//3.Response为OKHttp中的响应
//(1)同步请求
Response response = client.newCall(request).execute();

//(2)异步请求
Response response = client.newCall(request).enqueue(new Callback() {
        @Override
        public void onFailure(Call call, IOException e) {
        }
        @Override
        public void onResponse(Call call, Response response) throws IOException {
        }
});

 

三、流程分析


从上面的代码中我们发现,同步和异步请求的流程中第一步和第二步操作都是一样的。下面我们根据请求流程来一步步分析:

第一步,先是 new 了一个 OKHttpClient 对象,这个  OKHttpClient 类就比较简单了,里面包含了很多对象,其实OKhttp的很多功能模块都包装进这个类,让这个类单独提供对外的 API,这种 外观模式 的设计十分的优雅。而内部模块比较多,就使用了 Builder 模式(建造者模式)。我们看下 OKHttpClient 的构造方法,代码如下:

图 3-1

通过上述代码我们可以看到,OKhttpClient 有两个构造方法,可以通过传入一个构建好的 Builder 来设置相关参数,也可以像我们的样例代码中那样采用默认的构造方法。默认的构造方法中会调用 OkHttpClient 内部类 Builder 的默认构造方法来设置默认模块参数。Builder 的默认构造方法如下:

图 3-2

OkHttpClient 中除了这些获取模块参数的方法外,只有一个 newCall(Request request) 方法。这个我们下面第三步的时候说。

第二步,构建一个 Request 对象,Request 和 第三步中的 Response 分别抽象成请求和响应。其中 Request 包括 Headers 和 RequestBody,而 RequestBody 是 abstract 的,他的子类有 FormBody (表单提交的)和 MultipartBody(文件上传),分别对应了两种不同的 MIME 类型,FormBody :"application/x-www-form-urlencoded",MultipartBody:"multipart/"+xxx。Response 包括 Headers 和 RequestBody,而 ResponseBody 是 abstract 的,所以他的子类也是有两个,RealResponseBody 和 CacheResponseBody,分别代表真实响应和缓存响应。

第三步,同步和异步都是先传入第二步中的 Request 对象来调用第一步中的 OkHttpClient 对象的 newCall(Request request) 方法。我们看看这个 newCall() 方法里面做了什么,代码如下:

图 3-3

调用了 RealCall 类的 newRealCall() 方法,而 RealCall 类实现了 Call 接口,并且是 Call 接口的唯一实现类,所以这个方法返回 Call 对象其实也就是返回 RealCall 对象了,下面我们先看看 Call 接口,代码结构如下:

图 3-4

可以说我们能用到的操操作基本上都定义在这个接口里面了,所以也可以说这个类是 OKHttp 的核心类了。我们可以通过 Call 对象来操作请求了。而 Call 接口内部提供了内部接口 Factory,用于将对象的创建延迟到该工厂类的子类中进行,从而实现动态的配置。这种方式是比较常见的 工厂方法模式

我们接着第三步来说,调用了 newCall 方法后同步和异步就有区别了,同步调用的是 RealCall 的 execute() 方法,异步调用的是 RealCall 的 enqueue 方法,并传入一个 Callback 对象来接收回调信息。代码如下:

图 3-5

从上面实现可以看出,不管是同步请求还是异步请求都是 Dispatcher 在处理,区别在于同步请求是直接执行,并返回请求结果。而异步请求是构造了一个 AsyncCall,并将自己加入处理队列中。AsyncCall 是 RealCall 的内部类,继承于 NamedRunnable 类,而 NamedRunnable 又实现了 Runnable 接口,所以 AsyncCall 本质上是一个 Runable,Dispatcher 会调度 ExecutorService (线程池)来执行这些 Runable。我们来看一下 AsyncCall 的代码,如下:

图 3-6

从上面代码可以看出,不管是同步请求还是异步请求最后都会通过 getResponseWithInterceptorChain() 来获取 Response,只不过异步请求多了个线程调度,异步执行的过程。我们先来看看 Dispatcher 里的实现。代码结构如下:

图 3-7

注意红框中的变量,在后面的代码中会用到,下面我们看看 Dispatcher 的 execute() 和 enqueue() 方法:

图 3-8
图 3-9
图3-10

Dispatcher 是一个任务调度器,它内部维护了三个双端队列,readyAsyncCalls:准备运行的异步请求,runningAsyncCalls:正在运行的异步请求,runningSyncCalls:正在运行的同步请求。

同步请求就直接把请求添加到正在运行的同步请求队列 runningSyncCalls 中。而异步请求这里是先直接添加到 readyAsyncCalls 中,然后调用 promoteAndExecute() 方法用 Iterator 遍历整个 readyAsyncCalls。接着会做个判断,如果正在运行的异步请求数不超过 maxRequests(值为 64)而且同一个 host 下的异步请求数不超过 maxRequestsPerHost(值为 5)个,则将请求(AysncCall)从 readyAsyncCalls 中移除并添加到正在运行的异步请求队列 runningAsyncCalls 中,接着调用 AysncCall 类(代码上面图 3-6 已经贴出)的 executeOn() 方法来利用 ExecutorService 来调度执行这些请求(AsyncCall),如果不满足请求数的挑键就让请求在 readyAsyncCalls 队列中继续等待。

在调用 executeOn() 方法时,调用了 executorService.execute(this) 方法,这里的 this 代表的是 AsyncCall。而  AsyncCall 继承于 NamedRunnable,我们来看看 NamedRunnable 的代码:

NamedRunnable 是个 Runnable,也就是说 executorService.execute(this) 执行的是  NamedRunnable 的 run() 方法,而 run() 方法里又回去执行 execute() 这个抽象方法,所以最终执行的其实就是 AsyncCall 的 execute() 方法。

异步请求在 AsyncCall 的 executeOn() 和 execute() 方法的 finally 执行体中最终都会去调用 client.dispatcher().finished(this) 这个方法,而同步请求在 RealCall 的 execute() 方法的 finally 执行体中最终也会去调用 client.dispatcher().finished(this),注意这里的两个 this 不同,分别代表着 AsyncCall 和 RealCall,也就是说同步请求和异步请求,不管请求失败还是成功,最终都会去调用 Dispatcher 的 finish() 方法。finish() 这个步骤非常重要,下面我们看下 Dispatcher 中这两个 finish 的代码:

之前调用的两个 finish() 方法,最终都会去调用 Dispathcer 类的一个 finished() 私有方法。在这个 finished() 方法中,首先会去 runningAsyncCalls 和 runningSyncCalls 中移除当前 call,接着又会去执行 promoteAndExecute() 方法(从 readyAsyncCalls 中往 runningAsyncCalls 中迁移 AsyncCall),这样就达到了一个请求调度的过程,所以说这个 finish() 方法真的是非常重要的一个步骤。

为了讲解队列中请求调度的连续性,我们跳过了一个 getResponseWithInterceptorChain() 方法,这个方法是整个 OkHttp 中的核心,下面我们看下它的代码:

根据上述的代码我们发现,先是创建了一个 Interceptor 的 List,然后不断的往里面添加 interceptor,Interceptor 称为拦截器,是个接口,所有的拦截器都要实现 Interceptor 接口。短短几行代码,完成了对请求的所有处理过程,Interceptor 将网络请求、缓存、透明压缩等功能统一了起来,它的实现采用责任链模式,各司其职, 每个功能都是一个 Interceptor,上一级处理完成以后传递给下一级,它们最后连接成了一个 Interceptor.Chain。它们的功能如下:

  1. client.interceptors():用户自定义的 Interceptor。(在图 3-1 的 210 行设置)
  2. RetryAndFollowUpInterceptor:负责失败重试以及重定向。
  3. BridgeInterceptor:负责把用户构造的请求转换为发送给服务器的请求,把服务器返回的响应转换为对用户友好的响应。
  4. CacheInterceptor:负责读取缓存以及更新缓存。
  5. ConnectInterceptor:负责与服务器建立连接。
  6. client.newworkInterceptors():用户自定义的网络层 Interceptor(在图 3-1 的 211 行设置)
  7. CallServerInterceptor:负责从服务器读取响应的数据。

位置决定功能,位置靠前的先执行(按以上顺序),最后一个则负责与服务器通讯,请求从 RetryAndFollowUpInterceptor (如果没有用户自定义的 Interceptor)开始层层传递到 CallServerInterceptor,每一层都对请求做相应的处理,处理的结果再从 CallServerInterceptor 层层返回给 RetryAndFollowUpInterceptor,最后请求的发起者获得了服务器返回的结果。

我们看下 Interceptor 的代码:

添加完 interceptor 后创建了一个 Interceptor.Chain,这个 Chain 是 Interceptor 接口的内部接口,被称为拦截器链,它的唯一实现类是 RealInterceptorChain。我们看一下 RealInterceptorChain 的构造方法,代码如下:

 从上述代码可以发现,index 的初始值是 0,对应的是 interceptors 中当前执行的索引。创建完 RealInterceptorChain 紧接着就会去调用这个 chain 的 proceed 方法,代码如下:

上述代码中,122 行是为了避免 List 索引越界。138 - 142 行主要做的工作是用当前的参数调用 RealInterceptorChain  的构造方法来再创建一个新的 RealInterceptorChain,其中传给下个 RealInterceptorChain  的 index 在当前基础上加 1 了,接着获取 interceptors 中当前 index 下的 Interceptor,然后调用这个 Interceptor 的 intercept(Chain chain) 方法,并将刚才构建的 RealInterceptorChain 作为参数传递。直到所有 interceptor 都处理完毕,然后将得到的 response 返回。

每个拦截器的方法都遵循这样的规则:

@Override public Response intercept(Chain chain) throws IOException {
    //1 Request阶段,该拦截器在Request阶段负责做的事情    
    Request request = chain.request();

    //2 调用RealInterceptorChain.proceed(),其实是在递归调用下一个拦截器的intercept()方法
    response = ((RealInterceptorChain) chain).proceed(request, transmitter, null);

    //3 Response阶段,完成了该拦截器在Response阶段负责做的事情,然后返回到上一层的拦截器。
    return response;     
  }

从上面的描述可知,Request 是按照 interpretors 的顺序正向处理,而 Response 是逆向处理的。它的实现采用责任链模式,这参考了OSI七层模型的原理。上面我们也提到过。CallServerInterceptor 相当于最底层的物理层, 请求从上到逐层包装下发,响应从下到上再逐层包装返回。很漂亮的设计。 可以用一个更加生动的环形流水线生产的例子来帮助大家在概念上完全理解 Interceptor:

具体流程图如下:

OkHttp 中这个 Interceptor 的设计十分精彩,不仅分解了问题,降低了复杂度,还提高了拓展性和可维护性,非常值得大家学习。我们将在下一篇文章中学习 《Android OkHttp 源码解析 - 拦截器》

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值