面试题网络

OkHttp3源码解析

2018-06-19  本文已影响0人  luweicheng24

引用

okhttp问世以来,以其高度封装、定制、简洁的api调用获得广大使用者的喜爱,目前最流行的网络请求框架莫过于rxjava+retrofit+okhttp,如果你一直停留在使用的地步,那你永远可能只是大自然的搬运工了,为了了解这些架构设计的巧妙以及为何会如此受欢迎,只有通过源码来了解设计精髓,学习square出品,必属精品的代码设计思路,本篇先来了解一下okhttp3,本片所有源码是基于okhttp3.8.1,为了了解设计思路,所贴源码可能会有部分省略。

简单的使用

  1. 同步请求
OkHttpClient client = new OkHttpClient();  // 1.1 构建HttpClient对象,okhttp的门面或者外观对象
                Request request = new Request.Builder().url("http://www.baidu.com")  
                        .build();  //1.2 使用构建者模式创建一个包含请求参数的Request对象
                try {  
                    Call call = client.newCall(request);//1.3 call对象表示一个执行请求的实体对象,一个call代表一个请求
                    Response response = call.execute();  //1.4 执行同步请求
                     
                    if (response.isSuccessful()) { // 根据服务器返回数据封装的Response对象,包含响应码、响应体等
                        System.out.println("成功");  
                    }  
                } catch (IOException e) {  
                    e.printStackTrace();  
                }  

接下来对上面的步骤,一步一步解释然后跟踪源码:

*1.1 okhttpclient采用外观者模式、构建者模式,创建一个http请求的client,该对象包含一个Build对象,用来定制化创建你所需要的client对象;

  public OkHttpClient() {
    this(new Builder());
  }  

      public Builder() {
      dispatcher = new Dispatcher(); // 由call代表的请求的分发器
      protocols = DEFAULT_PROTOCOLS; // 默认的协议 http2 http1.1
      connectionSpecs = DEFAULT_CONNECTION_SPECS; // 设置连接时支持的tls层协议以及不进行数据加密
      eventListenerFactory = EventListener.factory(EventListener.NONE);
      proxySelector = ProxySelector.getDefault();
      cookieJar = CookieJar.NO_COOKIES;
      socketFactory = SocketFactory.getDefault(); // socket生产工厂
      hostnameVerifier = OkHostnameVerifier.INSTANCE;
      certificatePinner = CertificatePinner.DEFAULT;
      proxyAuthenticator = Authenticator.NONE;
      authenticator = Authenticator.NONE;
      connectionPool = new ConnectionPool(); //连接池 支持多路复用
      dns = Dns.SYSTEM;
      followSslRedirects = true;
      followRedirects = true;
      retryOnConnectionFailure = true;
      connectTimeout = 10_000;
      readTimeout = 10_000;
      writeTimeout = 10_000;
      pingInterval = 0;
    }

通过观察okhttpclient其实就是在配置全局发送请求中所需要的各种定制化的参数,并且持有各个参数引用对象。
1.2 根据Http协议创建的Request请求,Request这个类总共还没300行代码,内部使用好了嵌套的构建者模式来对请求参数进行设置,主要就是Header、url、method、requestbody等一些在进行http请求中符合http协议的参数:

Request类结构图
1.3 client.newCall(request);client根据request中的参数创建一个执行该请求的执行体call对象,
一个call就代表一个请求** Call是一个接口规定了需要执行的几个行为,具体的实现类有RealCall和AyncCall:
public interface Call<T> extends Cloneable {
  // 同步执行网络请求
  Response<T> execute() throws IOException;

  // 异步执行网络请求
  void enqueue(Callback<T> callback);

 // 判断该请求一否已经执行完成
  boolean isExecuted();

 // 取消该请求
  void cancel();

  // 克隆一个一模一样的call对象也就是一个请求
  Call<T> clone();

  // 获取封装该请求参数的request对象
  Request request();
}

既然Call只是规定了这些执行的行为,那一个请求的执行必然是由其由其实现类来执行,这就是面向接口编程。client.newCall(request) 这一步就是创建一个call对象:

 /**
   * Prepares the {@code request} to be executed at some point in the future.
   */
  @Override public Call newCall(Request request) {
     // 包含三个参数 1 当前client 2 request对象 3 是不是web socket http支持使用socket实现长连接
    return new RealCall(this, request, false /* for web socket */);
  }

可以看到,这一步是创建了RealCall对象,该对象就是执行同步请求的真正对象。
*1.4 Response response = call.execute() 这一步就是由请求的执行体realCall来发起同步请求:

@Override public Response execute() throws IOException {
    synchronized (this) {// 添加同步锁,判断该RealCall是否已经执行
      if (executed) throw new IllegalStateException("Already Executed");
      executed = true;
    }
    captureCallStackTrace(); 
    try {
      client.dispatcher().executed(this);//1.5
      Response result = getResponseWithInterceptorChain();//1.6
      if (result == null) throw new IOException("Canceled");
      return result;
    } finally {
      client.dispatcher().finished(this);
    }
  }

*1.5 调用在创建client的时候就初始化的Dispatcher,Dispatcher是一个请求分发器,内部包含了三个队列数组和一个线程池:


Dispatcher结构
  /** Executes calls. Created lazily. */
  private @Nullable ExecutorService executorService; // 线程池

  /** Ready async calls in the order they'll be run. */ 正在等待的异步任务队列
  private final Deque<AsyncCall> readyAsyncCalls = new ArrayDeque<>();

  /** Running asynchronous calls. Includes canceled calls that haven't finished yet. */   正在执行的异步任务队列
  private final Deque<AsyncCall> runningAsyncCalls = new ArrayDeque<>();

  /** Running synchronous calls. Includes canceled calls that haven't finished yet. */ 正在执行的同步任务队列
  private final Deque<RealCall> runningSyncCalls = new ArrayDeque<>();

  public Dispatcher(ExecutorService executorService) {
    this.executorService = executorService;
  }

  public Dispatcher() {
  }
  // 创建一个线程池,核心线程为0,最大为Integer的最大值,空闲线程60s没任务线程自动销毁
  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;
  }

而Dispatcher的execute的的方法是将realCall对象加入到runningSyncCalls的队列中,

 /** Used by {@code Call#execute} to signal it is in-flight. */
  synchronized void executed(RealCall call) {
    runningSyncCalls.add(call);
  }

*1.6 这一步执行前,先来说一下拦截器,okhttp对于网络请求采用用了一个类似AOP的的拦截器链,链式调用所有拦截器,最后执行请求返回response,而okhttp内置了5个拦截器。

拦截器的总体执行流程如下:

拦截器执行流程
这一步Response result = getResponseWithInterceptorChain(); 就是执行拦截器链,直到返回Response:
Response getResponseWithInterceptorChain() throws IOException {
    // Build a full stack of interceptors.
    List<Interceptor> interceptors = new ArrayList<>();
    interceptors.addAll(client.interceptors());
    interceptors.add(retryAndFollowUpInterceptor);
    interceptors.add(new BridgeInterceptor(client.cookieJar()));
    interceptors.add(new CacheInterceptor(client.internalCache()));
    interceptors.add(new ConnectInterceptor(client));
    if (!forWebSocket) {
      interceptors.addAll(client.networkInterceptors());
    }
    interceptors.add(new CallServerInterceptor(forWebSocket));

    Interceptor.Chain chain = new RealInterceptorChain(
        interceptors, null, null, null, 0, originalRequest);
    return chain.proceed(originalRequest);
  }

通过责任链模式循环调用所有拦截器,每个拦截器可以根据Request和Reponse前后执行相应的逻辑。
以上分析的是同步请求,异步请求也是大同小异:

  @Override public void enqueue(Callback responseCallback) {
    synchronized (this) {
      if (executed) throw new IllegalStateException("Already Executed");
      executed = true;
    }
    captureCallStackTrace();
    client.dispatcher().enqueue(new AsyncCall(responseCallback));
  }

跟踪到Dispatcher中:

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

以上就是Dispatcher的enqueue函数,先判断是否异步请求队列长度大于线程池最大请求数,以及当前主机的请求数超过5个。如果没有将给异步call加入到异步线程队列,调用线程池执行该call,如果超了,将该异步call加入到异步等待队列,

AsynCall是RealCall内部类,继承于NameRunnable,NameRunable其实就是Runnable的子类,定义了一个execute方法,执行在run()方法中:

/**
 * Runnable implementation which always sets its thread name.
 */
public abstract class NamedRunnable implements Runnable {
  protected final String name;

  public NamedRunnable(String format, Object... args) {
    this.name = Util.format(format, args);
  }

  @Override public final void run() {
    String oldName = Thread.currentThread().getName();
    Thread.currentThread().setName(name);
    try {
      execute();
    } finally {
      Thread.currentThread().setName(oldName);
    }
  }

  protected abstract void execute();
}

将AsyncCall加入到线程池,既然AsyncCall是一个Runnnable,那么就是执行Async的execute方法:

   @Override protected void execute() {
      boolean signalledCallback = false;
      try {
        Response response = getResponseWithInterceptorChain();  // 调用拦截器链,执行请求 返回response
        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) {
          // Do not signal the callback twice!
          Platform.get().log(INFO, "Callback failure for " + toLoggableString(), e);
        } else {
          responseCallback.onFailure(RealCall.this, e);// 失败回调
        }
      } finally {
        client.dispatcher().finished(this); // 完成了请求
      }
    }

在一次网络请求不管成功失败,都会调用finally中的这行代码client.dispatcher().finished(this); 别问我为啥?跟踪一下这行代码:

/** Used by {@code AsyncCall#run} to signal completion. */
  void finished(AsyncCall call) {
    finished(runningAsyncCalls, call, true);
  }
  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!");
      if (promoteCalls) promoteCalls();
      runningCallsCount = runningCallsCount();
      idleCallback = this.idleCallback;
    }

    if (runningCallsCount == 0 && idleCallback != null) {
      idleCallback.run();
    }
  }

将异步call熊runningAsyncCalls队列中移除,然后 如果是异步请求就会执行promoteCalls() 这个方法:

  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; // Reached max capacity.
    }
  }

其实就是查看线程池情况,然后从readAsyncCall是中获取等待的异步call执行,如此循环,直到所有的异步call执行完成,大体流程如下:


网络同步与异步请求

结语
本篇只是从一次简单的网络请求来跟踪源码,梳理的一个大致流程,其中对于每个拦截器只是说明作用,其实每个拦截器设计也很巧妙,比如CacheInterceptor采用了策略模式来对网络缓存和本地缓存进行相应的处理,缓存采用DiskLruCache来缓存。ConnnectInteceptor内置连接池采用多路复用技术减少了创建connection的花销等等。

上一篇下一篇

猜你喜欢

热点阅读