okHttp 源码解析

OkHttp 源码详解

OkHttp应该是目前Android平台上使用最为广泛的开源网络库了,Android 在6.0之后也将内部的HttpUrlConnection的默认实现替换成了OkHttp。

这篇文章的目的,了解okhttp的框架原理,以及责任链模式的使用。

1、发送请求

首先看一下,怎么发出一个同步/异步请求。

        /**
     * 同步发起请求
     *
     * @throws IOException
     */
    private void execute() throws IOException {
        OkHttpClient client = new OkHttpClient();
        Request request = new Request.Builder().url("your url").build();
        Response syncResponse = client.newCall(request).execute();
    }

    /**
     * 异步发起请求
     *
     * @throws IOException
     */
    private void enqueue() {
        OkHttpClient client = new OkHttpClient();
        Request request = new Request.Builder().url("your url").build();
        client.newCall(request).enqueue(new Callback() {
            @Override
            public void onFailure(@NotNull Call call, @NotNull IOException e) {
            }

            @Override
            public void onResponse(@NotNull Call call, @NotNull Response response) throws IOException {
            }
        });
    }

这一段代码就是日常使用OkHttp最常见的用法,跟进源码后,可以得到一张更为详细的流程图,通过这张图来看下内部的逻辑是如何流动的。

809143-20180816162138233-707076029.png

其实很简单,只有几个核心类,我们一个个来看一下。

  1. OkHttpClient
  2. Request 和 Response
  3. RealCall

OkHttpClient:这个是整个OkHttp的核心管理类,所有的内部逻辑和对象归OkHttpClient统一来管理,它通过Builder构造器生成,构造参数和类成员很多,这里先不做具体的分析。

Request 和Response:Request是我们发送请求封装类,内部有url, header , method,body等常见的参数,Response是请求的结果,包含code, message, header,body ;这两个类的定义是完全符合Http协议所定义的请求内容和响应内容。

RealCall:负责请求的调度(同步的话走当前线程发送请求,异步的话则使用OkHttp内部的线程池进行);同时负责构造内部逻辑责任链,并执行责任链相关的逻辑,直到获取结果。虽然OkHttpClient是整个OkHttp的核心管理类,但是真正发出请求并且组织逻辑的是RealCall类,它同时肩负了调度和责任链组织的两大重任,接下来我们来着重分析下RealCall类的逻辑。

大致总结就是,OkHttpClient 和 Request 都是使用了 Builder 设计模式,然后,Request 通过 OkHttpClient 把 同步/异步 请求发送出去.

2、同步/异步

我们跟进源码,看一下同步/异步的请求原理

public Response execute() throws IOException {
    synchronized (this) {
      if (executed) throw new IllegalStateException("Already Executed");
      executed = true;
    }
    captureCallStackTrace();
    try {
      client.dispatcher().executed(this);
      Response result = getResponseWithInterceptorChain();
      if (result == null) throw new IOException("Canceled");
      return result;
    } finally {
      client.dispatcher().finished(this);
    }
  }
public void enqueue(Callback responseCallback) {
    synchronized (this) {
      if (executed) throw new IllegalStateException("Already Executed");
      executed = true;
    }
    captureCallStackTrace();
    client.dispatcher().enqueue(new AsyncCall(responseCallback));
  }

可以看到最终的请求处理是 dispatcher 来完成的,接下来看下 dispatcher

 //最大并发请求书
 private int maxRequests = 64;
 //每个主机的最大请求数
 private int maxRequestsPerHost = 5;
 private Runnable idleCallback;

 /** 执行的线程池. Created lazily. */
 private ExecutorService executorService;

//将要运行的异步请求队列
 /** Ready async calls in the order they'll be run. */
 private final Deque readyAsyncCalls = new ArrayDeque<>();

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


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

 public Dispatcher() {
 }

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

Dispatcher 有两个构造方法,可以自己指定线程池, 如果没有指定, 则会默认创建默认线程池,可以看到核心数为0,缓存数可以是很大, 比较适合执行大量的耗时比较少的任务。

接着看 enqueue是如何实现的

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

当正在运行的异步请求队列中的数量小于64, 并且 正在运行的请求主机数小于5,把请求加载到runningAsyncCalls 中并在线程池中执行, 否则就加入到 readyAsyncCalls 进行缓存等待。

上面可以看到传递进来的是 AsyncCall 然后 execute 那我们看下 AsyncCall方法

final class AsyncCall extends NamedRunnable {
    private final Callback responseCallback;

    AsyncCall(Callback responseCallback) {
      super("OkHttp %s", redactedUrl());
      this.responseCallback = responseCallback;
    }

    String host() {
      return originalRequest.url().host();
    }

    Request request() {
      return originalRequest;
    }

    RealCall get() {
      return RealCall.this;
    }

    @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) {
          // 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);
      }
    }
  }

看到 NamedRunnable 实现了 Runnable,AsyncCall 中的 execute 是对网络请求的具体处理。

Response response = getResponseWithInterceptorChain();

能明显看出这就是对请求的处理,在看它的具体实现之前先看下 client.dispatcher().finished 的方法实现。

  /** Used by {@code AsyncCall#run} to signal completion. */
  void finished(AsyncCall call) {
    finished(runningAsyncCalls, call, true);
  }

  /** Used by {@code Call#execute} to signal completion. */
  void finished(RealCall call) {
    finished(runningSyncCalls, call, false);
  }

// 最后调用这个
  private  void finished(Deque 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();
    }
  }

由于 promoteCalls 是true 我们看下 promoteCalls 的方法实现

private void promoteCalls() {
  if (runningAsyncCalls.size() >= maxRequests) return; // Already running max capacity.
  if (readyAsyncCalls.isEmpty()) return; // No ready calls to promote.

  for (Iterator 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.
  }
}

根据代码可以明显看出 , 当一个请求结束了调用 finished 方法,最终到promoteCalls就是把 异步等待队列中的请求,取出放到 异步执行队列中。

  • 如果异步执行队列已经是满的状态就不加了,return
  • 如果 异步等待队列中 没有需要执行的网络请求 也就没有必要进行下一步了 return
  • 上面的两条都没遇到,遍历 异步等待队列,取出队首的请求,如果这个请求的 host 符合 (正在执行的网络请求中 同一个host最多只能是5个)的这个条件, 把 等待队列的这个请求移除, 加入到 正在执行的队列中, 线程开始执行。 如果不符合继续 遍历操作。

3、interceptors 拦截器

接着看 RealCall 的 getResponseWithInterceptorChain 方法

Response getResponseWithInterceptorChain() throws IOException {
    // Build a full stack of interceptors.
    List interceptors = new ArrayList<>();
    //用户自己定义的拦截器
    interceptors.addAll(client.interceptors());
    //系统提供的重试拦截器,失败后的重试和重定向
    interceptors.add(retryAndFollowUpInterceptor);
    //负责把用户构造的请求转换为发送到服务器的请求 、把服务器返回的响应转换为用户友好的响应 处理 配置请求头等信息
    //从应用程序代码到网络代码的桥梁。首先,它根据用户请求构建网络请求。然后它继续呼叫网络。最后,它根据网络响应构建用户响应。
    interceptors.add(new BridgeInterceptor(client.cookieJar()));
    //处理 缓存配置 根据条件(存在响应缓存并被设置为不变的或者响应在有效期内)返回缓存响应
    //设置请求头(If-None-Match、If-Modified-Since等) 服务器可能返回304(未修改)
    //可配置用户自己设置的缓存拦截器
    interceptors.add(new CacheInterceptor(client.internalCache()));
    //连接拦截器 这里才是真正的请求网络
    interceptors.add(new ConnectInterceptor(client));
    if (!forWebSocket) {
        //配置okhttpClient 时设置的networkInterceptors
       //返回观察单个网络请求和响应的不可变拦截器列表。
      interceptors.addAll(client.networkInterceptors());
    }
    //执行流操作(写出请求体、获得响应数据) 负责向服务器发送请求数据、从服务器读取响应数据
    //进行http请求报文的封装与请求报文的解析
    interceptors.add(new CallServerInterceptor(forWebSocket));
    //创建责任链
    Interceptor.Chain chain = new RealInterceptorChain(
        interceptors, null, null, null, 0, originalRequest);
        //执行 责任链
    return chain.proceed(originalRequest);
  }

看下 RealInterceptorChain 的实现

public Response proceed(Request request, StreamAllocation streamAllocation, HttpCodec httpCodec,
    Connection connection) throws IOException {
 
  if (index >= interceptors.size()) throw new AssertionError();

  calls++;
  //创建新的拦截链,链中的拦截器集合index+1
  RealInterceptorChain next = new RealInterceptorChain(
      interceptors, streamAllocation, httpCodec, connection, index + 1, request);
    //  执行当前的拦截器
  Interceptor interceptor = interceptors.get(index);
  // 执行拦截器
  Response response = interceptor.intercept(next);

     if (httpCodec != null && index + 1 < interceptors.size() && next.calls != 1) {
    throw new IllegalStateException("network interceptor " + interceptor
        + " must call proceed() exactly once");
  }

  // Confirm that the intercepted response isn't null.
  if (response == null) {
    throw new NullPointerException("interceptor " + interceptor + " returned null");
  }
  
  return response;
}

根据上面的代码 我们可以看出,新建了一个RealInterceptorChain 责任链 并且 index+1,然后 执行interceptors.get(index); 返回Response。

责任链中每个拦截器都会执行chain.proceed()方法之前的代码,等责任链最后一个拦截器执行完毕后会返回最终的响应数据,而chain.proceed() 方法会得到最终的响应数据,这时就会执行每个拦截器的chain.proceed()方法之后的代码,其实就是对响应数据的一些操作。

结合源码,可以得到如下结论:

拦截器按照添加顺序依次执行
拦截器的执行从RealInterceptorChain.proceed()开始,进入到第一个拦截器的执行逻辑
每个拦截器在执行之前,会将剩余尚未执行的拦截器组成新的RealInterceptorChain
拦截器的逻辑被新的责任链调用next.proceed()切分为start、next.proceed、end这三个部分依次执行
next.proceed() 所代表的其实就是剩余所有拦截器的执行逻辑
所有拦截器最终形成一个层层内嵌的嵌套结构

了解了上面拦截器的构造过程,我们再来一个个的分析每个拦截器的功能和作用。
从代码来看,总共添加了五个拦截器(不包含自定义的拦截器如client.interceptors和client.networkInterceptors,这两个后面再解释)。
我们本次分享,只做整体框架的解读,具体每个拦截器的处理不在此做出赘述。

  • retryAndFollowUpInterceptor——失败和重定向拦截器
  • BridgeInterceptor——封装request和response拦截器
  • CacheInterceptor——缓存相关的过滤器,负责读取缓存直接返回、更新缓存
  • ConnectInterceptor——连接服务,负责和服务器建立连接 这里才是真正的请求网络
  • CallServerInterceptor——执行流操作(写出请求体、获得响应数据) 负责向服务器发送请求数据、从服务器读取响应数据 进行http请求报文的封装与请求报文的解析

4、拦截器demo

个人觉得,okhttp的精髓之一,就是拦截器的理念,针对本理念,写了个简单易懂的demo,请笑纳。

Interceptor拦截器接口

public interface Interceptor {
    String intercept(OkHttpChain chain);
}

Interceptor拦截器链

public class OkHttpChain {
    private int index = 0;
    private List interceptors;

    public OkHttpChain(List interceptors, int index) {
        this.interceptors = interceptors;
        this.index = index;
    }

    public String process() {
        if (index >= interceptors.size()) {
            return "";
        }
        OkHttpChain next = new OkHttpChain(interceptors, index + 1);
        Interceptor interceptor = interceptors.get(index);
        return interceptor.intercept(next);
    }

    public void add(Interceptor interceptor) {
        interceptors.add(interceptor);
    }
}

三个Interceptor拦截器

public class AInterceptor implements Interceptor {
    @Override
    public String intercept(OkHttpChain chain) {
        System.out.println("Interceptor_A");
        return "A" + chain.process();
    }
}
public class BInterceptor implements Interceptor {
    @Override
    public String intercept(OkHttpChain chain) {
        System.out.println("Interceptor_B");
        return "_B" + chain.process();
    }
}
public class CInterceptor implements Interceptor {
    @Override
    public String intercept(OkHttpChain chain) {
        System.out.println("Interceptor_C");
        return "_C" + chain.process();
    }
}

执行

    public static void main(String[] args) {
        List interceptors = new ArrayList<>();
        OkHttpChain chain = new OkHttpChain(interceptors, 0);
        interceptors.add(new AInterceptor());
        interceptors.add(new BInterceptor());
        interceptors.add(new CInterceptor());
        String result = chain.process();
        System.out.println("result = " + result);
    }

执行结果为:

Interceptor_A
Interceptor_B
Interceptor_C
result = A_B_C

参考博客

  • OkHttp 源码解析

你可能感兴趣的:(okHttp 源码解析)