OkHttp源码彻底解析(二)OkHttp架构及API源码

前言

OkHttp是一个处理网络请求的开源项目,是安卓端最火热的轻量级框架,由移动支付Square公司贡献(该公司还贡献了Picasso)

用于替代HttpUrlConnection和Apache HttpClient(android API23 6.0里已移除HttpClient,现在已经打不出来),这是现在非常主流的一个网络请求框架了。

可能有人会说Retrofit+RxJava才是最主流的,好吧,其实Retrofit的强大也是基于OkHttp,其是在OkHttp的基础上进一步封装,所以OkHttp也是切入Retrofit源码学习的入口。

博主对Retrofit2.x和OkHttp3.0也是比较熟悉的,刚好最近比较有空,接着周末时间总结了OkHttp。

本系列将带领大家从源码的出发,做到尽可能详细地剖析OkHttp的每一个知识点。
 

该系列的第一篇文章中我们已经了解了OkHttp从发起一个请求到收到网络回调数据的流程(Request——>Response)的过程。

本文的主要是从源码出发,带大家了解从发起一个Request请求到调用Dispatcher(分发器)调用线程池,来实现同/异步发起请求数据的流程,及内部涉及的设计模式和原理。

本系列文章:

OkHttp源码彻底解析(一)OkHttp请求流程

OkHttp源码彻底解析(二)OkHttp架构及API源码

OkHttp源码彻底解析(三)OkHttp3.0拦截器原理——责任链模式

OkHttp源码彻底解析(四)OkHttp拦截器的作用

OkHttp源码彻底解析(五)OkHttp连接池


目录

前言

客户端请求的数据Request、服务端返回的数据Response——Builder模式

Request:

Response:

okHttpClient——外观模式,组合模式

newCall

call

dispatcher与executorService

执行请求

Converter工厂模式


OkHttp整体流程

这是OkHttp请求的流程,也是本章讲解的流程,图中省略处为拦截器部分(责任链模式)本章涉及,将在下一篇博客提到

我们都知道,要是有网络请求的API之前,必须先有请求的信息,也就是request

客户端请求的数据Request、服务端返回的数据Response——Builder模式

首先要明白,Requset与Response为什么使用Builder模式

  • 因为它们需要的参数满足这两点中的一点:
    • 1.参数多且杂 ; 2.参数不是必须要传入的
  • 值得注意的一点是:数据请求类 的 url 是必须传的,会在 build() 方法里检查,如果 为空会报异常

Request:

这是一个 请求数据 的封装类(封装了请求头、请求地址等等)

public final class Request {
      //url字符串和端口号信息,默认端口号:http为80,https为443.其他自定义信息
      private final HttpUrl url;
      
      //"get","post","head","delete","put"....
      private final String method;
      
      //包含了请求的头部信息,name和value对。最后的形势为:$name1+":"+$value1+"\n"+ $name2+":"+$value2+$name3+":"+$value3...
      private final Headers headers;
      
      //请求的数据内容
      private final RequestBody body;
      
      //请求的附加字段。对资源文件的一种摘要。保存在头部信息中:ETag: "5694c7ef-24dc"。客户端可以在二次请求的时候,在requst的头部添加缓存的tag信息(如If-None-Match:"5694c7ef-24dc"),服务端用改信息来判断数据是否发生变化。
      private final Object tag;
      
      //各种附值函数和Builder类
      ...
        
     }

其中,ResponseBody是请求的具体内容,是抽象类

public abstract class RequestBody {
     
      ...
      
      //返回内容类型
      public abstract MediaType contentType();
    
      //返回内容长度
      public long contentLength() throws IOException {
        return -1;
      }
    
      //如何写入缓冲区。BufferedSink是第三方库okio对输入输出API的一个封装,不做详解。
      public abstract void writeTo(BufferedSink sink) throws IOException;

    }

OKHttp3中给出了两个requestBody的实现FormBody 和 MultipartBody,分别对应了两种不同的MIME类型:"application/x-www-form-urlencoded"和"multipart/"+xxx.作为的默认实现

其中,有一个重要的抽象方法writeTo

public abstract void writeTo(BufferedSink sink) throws IOException;

该方法的参数BufferedSink是Okio的封装,就是一个sink就是从本地写出的特殊的IO流。

这个抽象方法会在最后一个拦截器CallServerInterceptor里面,也就是最终发起网络请求的部分被调用

request.body().writeTo(bufferedRequestBody);

把request装换成bufferedRequestBody,并作为IO流通过Socket写到目标网络中,当然,在这一步之前还有好多好多好多操作,这里简单先提一下。

Response:

public final class Response implements Closeable {
      //网络请求的信息
      private final Request request;
      
      //网路协议,OkHttp3支持"http/1.0","http/1.1","h2"和"spdy/3.1"
      private final Protocol protocol;
      
      //返回状态码,包括404(Not found),200(OK),504(Gateway timeout)...
      private final int code;
      
      //状态信息,与状态码对应
      private final String message;
      
      //TLS(传输层安全协议)的握手信息(包含协议版本,密码套件(https://en.wikipedia.org/wiki/Cipher_suite),证书列表
      private final Handshake handshake;
      
      //相应的头信息,格式与请求的头信息相同。
      private final Headers headers;
      
      //数据内容在ResponseBody中
      private final ResponseBody body;
      
      //网络返回的原声数据(如果未使用网络,则为null)
      private final Response networkResponse;
      
      //从cache中读取的网络原生数据
      private final Response cacheResponse;
      
      //网络重定向后的,存储的上一次网络请求返回的数据。
      private final Response priorResponse;
      
      //发起请求的时间轴
      private final long sentRequestAtMillis;
      
      //收到返回数据时的时间轴
      private final long receivedResponseAtMillis;
    
      //缓存控制指令,由服务端返回数据的中的Header信息指定,或者客户端发器请求的Header信息指定。key:"Cache-Control"
      //详见<a href="http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.9">RFC 2616,14.9</a>
      private volatile CacheControl cacheControl; // Lazily initialized.
        
      //各种附值函数和Builder类型          ...
    }

其中,上面比较重要的是:1.ResponseBody是获取的数据内容,2.三个Response:网络返回的、从cache中读取的、重定向后保存的之前的网络请求返回数据,3.Requset网络请求信息,4.Headers响应头:可以知道缓存指令,5.code状态码:404就是错误,6.CacheControl 缓存控制指令,由服务器返回的Header或客户端Header指定

介绍了Request和Response之后,我们来了解如何通过这个Requset来得到Pesronse,已经这里面API的内部逻辑

首先,OKHttp3在项目中发起网络请求的API如下:

okHttpClient.newCall(request).execute();

我们按顺序来了解这个API涉及的类的源码:

okHttpClient——外观模式,组合模式

OkHttp是一个比较庞大的网络请求框架(Retrofit内部也是使用OkHttp这个框架),为了方便地和这个框架内部复杂的子模块进行交互,OkHttpClient使用了外观模式来实现。将OKHttp的很多功能模块,全部包装进这个类中,让这个类单独提供对外的API,这种设计叫做外观模式。将操作都隐藏起来,减少用户的交互成本。

由于内部功能模块太多,使用了Builder模式(生成器模式)来构造。

它的方法只有一个:newCall.返回一个Call对象(一个准备好了的可以执行和取消的请求)。

newCall

先来看源码:

@Override 
public Call newCall(Request request) {
    return new RealCall(this, request);
}

我们可以看到,newCall其实是返回一个RealCall类,也就是说我们的同/异步请求网络数据,实际上都是调用这个RealCall的execute/enqueue方法。这是一个Call接口的实现类

call

public interface Call {
  
  Request request();
 
  //同步的方法,直接返回Response
  Response execute() throws IOException;
  
  //异步的,传入回调CallBack即可(接口,提供onFailure和onResponse方法)
  void enqueue(Callback responseCallback);
  
  void cancel();

  boolean isExecuted();

  boolean isCanceled();

  interface Factory {
    Call newCall(Request request);
  }
}

Call接口提供了内部接口Factory(用于将对象的创建延迟到该工厂类的子类中进行,从而实现动态的配置,工厂方法模式)。

dispatcher与executorService

dispatcher与executorService分别是分发器与线程池,承接上面的RealCall,

OKHttpClient类中有个成员变量dispatcher负责请求的分发。既在真正的请求RealCall的execute方法中,使用dispatcher来执行任务:

  • RealCall的execute方法:

      @Override 
      public Response execute() throws IOException {
      synchronized (this) {
        if (executed) throw new IllegalStateException("Already Executed");
        executed = true;
      }
      try {
        //使用dispatcher 来分发任务
        client.dispatcher().executed(this);
        Response result = getResponseWithInterceptorChain();
        if (result == null) throw new IOException("Canceled");
        return result;
      } finally {
        client.dispatcher().finished(this);
      }
      }
    
  • RealCall的enqueue方法:

      @Override public void enqueue(Callback responseCallback) {
          synchronized (this) {
            if (executed) throw new IllegalStateException("Already Executed");
            executed = true;
          }
          //使用dispatcher来将人物加入队列
          client.dispatcher().enqueue(new AsyncCall(responseCallback));
        }
    

OKHttp3中分发器只有一个类 ——Dispathcer.

也就是说,enqueue/execute(同/异步)都是内部调用了dispatcher来执行任务,

同步操作:dispathcer调用自己的execute方法

流程:

1.在RealCall中client.dispatcher().execute(this); 其中this就是RealCall

2.executorService()就是获取一个线程池

3.RealCall的execute内部是executorService().execute(this);线程池executorService调用他的execute(call)   ( call也就是上面的new AsyncCall(responseCallback))

异步操作:dispathcer调用自己的enqueue方法

流程:

 1.在RealCall中client.dispatcher().enqueue(new AsyncCall(responseCallback));其中,AsyncCall与上面同步时的RealCall形对比,同步调用RealCall,异步调用RealCall的内部类AsyncCall的enqueue

2.executorService()方法获取一个线程池,而

3.AsyncCall的enqueue内部是executorService().execute(call);线程池executorService调用他的execute(call)   ( call也就是上面的new AsyncCall(responseCallback))

下面我们可以通过源码来看看Dispatcher

线程池executorService:

 public synchronized ExecutorService executorService() {
    if (executorService == null) {
      executorService = new ThreadPoolExecutor(0, Integer.MAX_VALUE, 60, TimeUnit.SECONDS, new SynchronousQueue<Runnable>(), Util.threadFactory("OkHttp Dispatcher", false));
    }
    return executorService;
  }

参数:

  • 0:核心线程数量。保持在线程池中的线程数量(即使已经空闲),为0代表线程空闲后不会保留,等待一段时间后停止。
  • Integer.MAX_VALUE: 线程池可容纳线程数量。
  • 60,TimeUnit.SECONDS: 当线程池中的线程数大于核心线程数时,空闲的线程会等待60s后才会终止。如果小于,则会立刻停止。
  • new SynchronousQueue<Runnable>():线程的等待队列。同步队列,按序排队,先来先服务。
    Util.threadFactory("OkHttp Dispatcher", false): 线程工厂,直接创建一个名为 “OkHttp Dispathcer”的非守护线程。

(2) 执行同步的Call:直接加入runningSyncCalls队列中,实际上并没有执行该Call,交给外部执行。

  synchronized void executed(RealCall call) {
    runningSyncCalls.add(call);
  }

(3) 将Call加入队列:如果当前正在执行的call数量大于maxRequests,64,或者该call的Host上的call超过maxRequestsPerHost,5,则加入readyAsyncCalls排队等待。否则加入runningAsyncCalls,并执行。

  synchronized void enqueue(AsyncCall call) {
    if (runningAsyncCalls.size() < maxRequests && runningCallsForHost(call) < maxRequestsPerHost) {
      runningAsyncCalls.add(call);
      executorService().execute(call);
    } else {
      readyAsyncCalls.add(call);
    }
  }

(4) 从ready到running的轮转,在每个call 结束的时候调用finished,并:

    private <T> void finished(Deque<T> calls, T call, boolean promoteCalls) {
        int runningCallsCount;
        Runnable idleCallback;
        synchronized (this) {
          if (!calls.remove(call)) throw new AssertionError("Call wasn't in-flight!");
          //每次remove完后,执行promoteCalls来轮转。
          if (promoteCalls) promoteCalls();
          runningCallsCount = runningCallsCount();
          idleCallback = this.idleCallback;
        }
        //线程池为空时,执行回调
        if (runningCallsCount == 0 && idleCallback != null) {
          idleCallback.run();
        }
      }

(5) 线程轮转:遍历readyAsyncCalls,将其中的calls添加到runningAysncCalls,直到后者满。

    private void promoteCalls() {
        if (runningAsyncCalls.size() >= maxRequests) return; // Already running max capacity.
        if (readyAsyncCalls.isEmpty()) return; // No ready calls to promote.
    
        for (Iterator<AsyncCall> i = readyAsyncCalls.iterator(); i.hasNext(); ) {
          AsyncCall call = i.next();
            
          if (runningCallsForHost(call) < maxRequestsPerHost) {             i.remove();
            runningAsyncCalls.add(call);
            executorService().execute(call);
          }
    
          if (runningAsyncCalls.size() >= maxRequests) return; 
        }
      }  

执行请求

同步的请求RealCall 实现了Call接口:
可以execute,enqueue和cancle。
异步的请求AsyncCall(RealCall的内部类)实现了Runnable接口:
只能run(调用了自定义函数execute).

execute 对比:

  • RealCall:

    @Override public Response execute() throws IOException {
      synchronized (this) {
        if (executed) throw new IllegalStateException("Already Executed");
        executed = true;
      }
      try {
        //分发。实际上只是假如了队列,并没有执行
        client.dispatcher().executed(this);
        //实际上的执行。
        Response result = getResponseWithInterceptorChain();
        //返回结果
        if (result == null) throw new IOException("Canceled");
        return result;
      } finally {
        //执行完毕,finish
        client.dispatcher().finished(this);
      }
    }
    
  • AsyncCall:

    @Override protected void execute() {
          boolean signalledCallback = false;
          try {
            //实际执行。
            Response response = getResponseWithInterceptorChain();
            //执行回调
            if (retryAndFollowUpInterceptor.isCanceled()) {
              signalledCallback = true;
              responseCallback.onFailure(RealCall.this, new IOException("Canceled"));
            } else {
              signalledCallback = true;
              responseCallback.onResponse(RealCall.this, response);
            }
          } catch (IOException e) {
            if (signalledCallback) {
              Platform.get().log(INFO, "Callback failure for " + toLoggableString(), e);
            } else {
              responseCallback.onFailure(RealCall.this, e);
            }
          } finally {
            //执行完毕,finish
            client.dispatcher().finished(this);
          }
        }

实际上的执行函数都是getResponseWithInterceptorChain():

    private Response getResponseWithInterceptorChain() throws IOException {
        //创建一个拦截器列表
        List<Interceptor> interceptors = new ArrayList<>();
        //优先处理自定义拦截器
        interceptors.addAll(client.interceptors());
        //失败重连拦截器
        interceptors.add(retryAndFollowUpInterceptor);
        //接口桥接拦截器(同时处理cookie逻辑)
        interceptors.add(new BridgeInterceptor(client.cookieJar()));
        //缓存拦截器
        interceptors.add(new CacheInterceptor(client.internalCache()));
        //分配连接拦截器
        interceptors.add(new ConnectInterceptor(client));
        //web的socket连接的网络配置拦截器
        if (!retryAndFollowUpInterceptor.isForWebSocket()) {
          interceptors.addAll(client.networkInterceptors());
        }
        //最后是连接服务器发起真正的网络请求的拦截器
        interceptors.add(new CallServerInterceptor(
            retryAndFollowUpInterceptor.isForWebSocket())); 
        Interceptor.Chain chain = new RealInterceptorChain(
            interceptors, null, null, null, 0, originalRequest);
        //流式执行并返回response
        return chain.proceed(originalRequest);
      }

这里额外提一下converter

Converter工厂模式

converter:序列化,反序列化的工具(对应Requset和Response),实现数据类型的转换,例Gson解析等

converterFactory是converter的工厂模式,用来构建各种converter,

可以添加converterFactory由retrofit完成requestBody和responseBody的构造。

这里对retrofit2不展开讨论,后续会出新的文章来详细讨论。仅仅介绍一下converterFacotry,以及它是如何构建OkHttp3中的RequestBody和ResponseBody的。

  • Note: retrofit2中的Response与okhttp3中的response不同,前者是包含了后者。既retrofit2中的response是一层封装,内部才是真正的okhttp3种的response。

我们项目中的一个converterFacotry代码如下:

    public class RsaGsonConverterFactory extends Converter.Factory {
   
    //省略部分代码
    ...
    
    private final Gson gson;

    private RsaGsonConverterFactory(Gson gson) {
        if (gson == null) throw new NullPointerException("gson == null");
        this.gson = gson;
    } 
    //将返回的response的Type,注释,和retrofit的传进来,返回response的转换器。Gson只需要type就可以将responseBody转换为需要的类型。
    @Override
    public Converter<ResponseBody, ?> responseBodyConverter(Type type, Annotation[] annotations, Retrofit retrofit) {
        TypeAdapter<?> adapter = gson.getAdapter(TypeToken.get(type));
        return new RsaGsonResponseBodyConverter<>(gson, adapter);
    }
    //将request的参数类型,参数注释,方法注释和retrofit传进来,返回request的转换器。Gson只需要type就可以将request对象转换为OKHttp3的reqeustBody类型。
    @Override
    public Converter<?, RequestBody> requestBodyConverter(Type type, Annotation[] parameterAnnotations, Annotation[] methodAnnotations, Retrofit retrofit) {
        TypeAdapter<?> adapter = gson.getAdapter(TypeToken.get(type));
        return new RsaGsonRequestBodyConverter<>(gson, adapter);
    }
    }

该Factory(工厂方法模式,用于动态的创建对象)主要是用来生产response的converter和request的converter。显然我们使用了Gson作为数据转换的桥梁。分别对应如下两个类:

  • response的converter(之所以命名为Rsa,是做了一层加解密):

      public class RsaGsonResponseBodyConverter<T> implements Converter<ResponseBody, T> {
          private final Gson gson;
          private final TypeAdapter<T> adapter;
      
          RsaGsonResponseBodyConverter(Gson gson, TypeAdapter<T> adapter) {
              this.gson = gson;
              this.adapter = adapter;
          }
      
          @Override public T convert(ResponseBody value) throws IOException {
              JsonReader jsonReader = gson.newJsonReader(value.charStream());
              try {
                  return adapter.read(jsonReader);
              } finally {
                  value.close();
              }
          }
      }
    

直接将value中的值封装为JsonReader供Gson的TypeAdapter读取,获取转换后的对象。

  • request的converter:

      final class RsaGsonRequestBodyConverter<T> implements Converter<T, RequestBody> {
          private static final MediaType MEDIA_TYPE = MediaType.parse("application/json; charset=UTF-8");
          private static final Charset UTF_8 = Charset.forName("UTF-8");
      
          private final Gson gson;
          private final TypeAdapter<T> adapter;
      
          RsaGsonRequestBodyConverter(Gson gson, TypeAdapter<T> adapter) {
              this.gson = gson;
              this.adapter = adapter;
          }
      
          @Override public RequestBody convert(T value) throws IOException {
      
              Buffer buffer = new Buffer();
              Writer writer = new OutputStreamWriter(buffer.outputStream(), UTF_8);
              JsonWriter jsonWriter = gson.newJsonWriter(writer);
      
              adapter.write(jsonWriter, value);
              jsonWriter.close();
              //如果是RsaReq的子类,则进行一层加密。
              if(value instanceof RsaReq){
                 //加密过程
              }
              //不需要加密,则直接读取byte值,用来创建requestBody
              else {
                  //这个构造方法是okhttp专门为okio服务的构造方法。
                  return RequestBody.create(MEDIA_TYPE, buffer.readByteString());
              }
          }
      }   
    

上面的流操作使用的是第三方库okio。可以看到,retrofitokhttp,okio这三个库是完全相互兼容并互相提供了专有的API。



 

好了,这里就是本章内容,本章介绍了OkHttp的架构,API内部的源码,我们可以看到里面包含了许多值得学习的设计模式,

Requset和Response的builder模式

converter的工厂模式

OkHttpClient的外观模式,组合模式

猜你喜欢

转载自blog.csdn.net/qq_39037047/article/details/84674653