OkHttp—流程全解析

作者: JackDaddy | 来源:发表于2021-03-04 22:02 被阅读0次

    想必一提到 Android 网络库 就会想到 OkHttp,今天就来分析一下 OkHttp 里面的整个整个流程

    前言

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

    一、简单使用

    请求分为同步和异步两种:

    • 同步请求通过 调用 Call.exectute() 方法直接返回当前请求的 Response;
    • 异步请求调用 Call.enqueue() 方法将请求(AsyncCall)添加到请求队列中去,并通过回调(Callback)获取服务器返回的结果。
      代码如下:
    //1.新建OKHttpClient客户端
    OkHttpClient mClient = new OkHttpClient();
    //2.新建一个Request对象
    Request mRequest = new Request.Builder()
                    .url(URL)
                    .build();
    
    //3.Response为OKHttp中的响应
    //(1)同步请求
    Response response = mClient.newCall(mRequest).execute();
    
    //(2)异步请求
    Response response = mClient.newCall(mRequest).enqueue(new Callback() {
        @Override
        public void onFailure(Call call, IOException e) {
        }
       @Override
       public void onResponse(Call call, Response response) throws IOException {
      }
    });
    

    二、流程分析

        从上面可以看到无论是 同步请求 还是 异步 请求,前两个步骤都是一致的,接下来一步一步来分析流程。
        第一步,先是 实例化 了一个 OKHttpClient 对象,这个 OKHttpClient 类就比较简单了,里面包含了很多对象,其实OKhttp的很多功能模块都包装进这个类,让这个类单独提供对外的 API,这种 外观模式的设计十分的优雅。而内部模块比较多,就使用了Builder 模式(建造者模式)。我们看下 OKHttpClient 的构造方法,代码如下:

    OKHttpClient     通过上述代码我们可以看到,OKhttpClient 有两个构造方法,可以通过传入一个构建好的 Builder 来设置相关参数,也可以像我们的样例代码中那样采用默认的构造方法。默认的构造方法中会调用 OkHttpClient 内部类 Builder 的默认构造方法来设置默认模块参数。Builder 的默认构造方法如下:
    OkHttpClient # Builder
    第二步,构建一个 Request 对象,Request 和 第三步中的 Response 分别抽象成 请求响应
    其中 Request 包括 HeadersRequestBody,而 RequestBodyabstract 的,他的子类有 FormBody (表单提交的)MultipartBody(文件上传),分别对应了两种不同的 MIME 类型,FormBody :"application/x-www-form-urlencoded"MultipartBody:"multipart/"+xxxResponse 包括 HeadersResponseBody,而 ResponseBodyabstract 的,所以他的子类也是有两个,RealResponseBodyCacheResponseBody,分别代表真实响应和缓存响应。
    Reques & Respone

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

    OkHttpClient#newCall client调用了 RealCall 的 newRealCall() 方法,而 RealCall 类实现了 Call 接口,并且是 Call 接口的唯一实现类,所以这个方法返回 Call 对象其实也就是返回 RealCall 对象了: RealCall 接着来看一下Call 这个接口: Call     可以说我们能用到的操操作基本上都定义在这个接口里面了,所以也可以说这个类是 OKHttp 的核心类了。我们可以通过 Call 对象来操作请求了。而 Call 接口内部提供了内部接口 Factory,用于将对象的创建延迟到该工厂类的子类中进行,从而实现动态的配置。这种方式是比较常见的 工厂方法模式
        前面第三步说到有两种请求方式:
    • 同步请求
    • 异步请求

      刚才说过一切方法都是通过 Call 这个接口里的方法来实现的,而 RealCall 又是 Call 的唯一实现类,因此同步和异步方法分别执行的是RealCall 里的方法: 同步 & 异步     从上面实现可以看出,不管是同步请求还是异步请求都是 Dispatcher(分发器) 在处理,区别在于同步请求是直接执行,并返回请求结果。而异步请求是构造了一个 AsyncCall,并将自己加入处理队列中。接下来先来看下 Dispatcher(分发器) ,首先注意一下几个变量: Dispatcher

      分别指的是:

    • 最大连接数:64
    • 同一端口允许最大连接数:5
    • 等待队列
    • 正在执行异步队列
    • 正在执行同步队列
      带着这几个变量来看 enqueue 方法: Dispatcher#enqueue 可以看到把新构造的 AsyncCall 加入了 ready 队列,同时调用了 promoteAndExecute 方法: promoteAndExecute 看到第191、192 行,这里的意思是当正在执行的请求数不超过64 个且同一个端口请求数不超过 5 个时执行这个请求。
      整个方法的作用就是:从等待队列中取出一个请求,判断此时正在执行的队列数是否超过64,以及同一端口的请求数是否超过5,如果都不超过的话就将这个请求加入到执行队列中。

    而在第202-205行则是循环从执行队列中取出请求,在204行通过AsyncCall 执行 executeOn 方法执行请求。注意这里传入了 executorService() :

    executorService 可以看到这里是一个线程池,这里有个很有趣的点:
    • 这里的核心线程数为0
    • 同时这里传入的队列 SynchronousQueue 有个特点,容量为0
      这两个特点结合 线程池的特点 可以得到一个结论就是新加入的请求不会被堵塞住而会马上执行。
      接下来看到 AnsyncCall 方法: AnsyncCall 可以看到AnsyncCall 继承自NameRunable: NameRunable
      AsyncCall 是 RealCall 的内部类,继承于 NamedRunnable 类,而 NamedRunnable 又实现了 Runnable 接口,所以 AsyncCall 本质上是一个 Runable。在 NameRunnable 中执行了 run方法,而 run方法 执行了 execute 方法,也就是执行了 AsyncCall 中的 execute 方法: AsyncCall # execute 看到这里,发现其实发现获取请求是通过 getResponseWithInterceptorChain(); 这个方法获取响应的,这里放到最后讲。从刚才的 Dispatcher(分发器)无论是 同步请求 / 异步请求 方法最后都会去调用 finish 方法:
      finish 看到这里,应该都很熟悉,这里的意思就是当每次执行完请求后都会去等待队列取出一个请求,当满足上面所说的那两个条件时,就从等待队列移除,同时加到执行队列里。
      最后回过头来说一下刚才的 getResponseWithInterceptorChain() 方法,也就是真正获取响应的方法: getResponseWithInterceptorChain 根据上述的代码我们发现,先是创建了一个 Interceptor 的 List,然后不断的往里面添加 interceptor,Interceptor 称为拦截器,是个接口,所有的拦截器都要实现 Interceptor 接口。短短几行代码,完成了对请求的所有处理过程,Interceptor 将网络请求、缓存、透明压缩等功能统一了起来,它的实现采用责任链模式,各司其职, 每个功能都是一个 Interceptor,上一级处理完成以后传递给下一级,它们最后连接成了一个 Interceptor.Chain。它们的功能如下:
    1. client.interceptors():用户自定义的 Interceptor。(第205行设置)
    2. RetryAndFollowUpInterceptor:负责失败重试以及重定向。
    3. BridgeInterceptor:负责把用户构造的请求转换为发送给服务器的请求,把服务器返回的响应转换为对用户友好的响应。
    4. CacheInterceptor:负责读取缓存以及更新缓存。
    5. ConnectInterceptor:负责与服务器建立连接。
    6. client.newworkInterceptors():用户自定义的网络层 Interceptor(在图 3-1 的 211 行设置)
    7. CallServerInterceptor:负责从服务器读取响应的数据。

    位置决定功能,位置靠前的先执行(按以上顺序),最后一个则负责与服务器通讯,请求从 RetryAndFollowUpInterceptor (如果没有用户自定义的 Interceptor)开始层层传递到 CallServerInterceptor,每一层都对请求做相应的处理,处理的结果再从 CallServerInterceptor 层层返回给 RetryAndFollowUpInterceptor,最后请求的发起者获得了服务器返回的结果。注意这里第215行 的传入的index 值为 0 先来看下 Interceptor 这个接口的代码:

    Interceptor 添加完 interceptor 后创建了一个 Interceptor.Chain,这个 Chain 是 Interceptor 接口的内部接口,被称为拦截器链,它的唯一实现类是 RealInterceptorChain。我们看一下 RealInterceptorChain 的构造方法,代码如下: RealInterceptorChain 前面说过传入的index为 0,因此,这里的 index 为 0,对应的是 interceptors 中当前执行的索引。创建完 RealInterceptorChain 紧接着就会去调用这个 chain 的 proceed 方法,代码如下: RealInterceptorChain#proceed 上述代码中,122 行是为了避免 List 索引越界。139 - 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 相当于最底层的物理层, 请求从上到逐层包装下发,响应从下到上再逐层包装返回。


    责任链
    流程如下: 流程

    相关文章

      网友评论

        本文标题:OkHttp—流程全解析

        本文链接:https://www.haomeiwen.com/subject/gvedfltx.html