OKHttp源码解析(一)-执行流程

一、OKHTTP简介

1.支持HTTP2/SPDY
2.socket自动选择最好路线,并支持自动重连
3.拥有自动维护的socket连接池,减少握手次数
4.拥有队列线程池,轻松写并发
5.拥有Interceptors轻松处理请求与响应(比如透明GZIP压缩)基于Headers的缓存策略

二、OKHTTP代码使用

1.同步代码(GET请求)

  OkHttpClient client = new OkHttpClient();

  Request request = new Request.Builder()
      .url(url)
      .build();
  Call call =client.newCall(request);
  Response response = call.execute();
  return response.body().string();
}

在这里插入图片描述

2.异步代码(GET请求)

OkHttpClient okHttpClient = new OkHttpClient.Builder()
        .build();
Request request = new Request.Builder()
        .url(url)
        .build();
okHttpClient.newCall(request).enqueue(new Callback() {    //开启新的线程进行网络请求
    @Override
    public void onFailure(Call call, IOException e) {
    }

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

    }
});

在这里插入图片描述

在整个Okhttp的系统中,我们还要理解以下几个关键角色:

  • OkHttpClient:通信的客户端,用来统一管理发起请求与解析响应。
  • Call:Call是一个接口,它是HTTP请求的抽象描述,具体实现类是RealCall,它由CallFactory创建。
  • Request:请求,封装请求的具体信息,例如:url、header等。
  • RequestBody:请求体,用来提交流、表单等请求信息。
  • Response:HTTP请求的响应,获取响应信息,例如:响应header等。
  • ResponseBody:HTTP请求的响应体,被读取一次以后就会关闭,所以我们重复调用responseBody.string()获取请求结果是会报错的。
  • Interceptor:Interceptor是请求拦截器,负责拦截并处理请求,它将网络请求、缓存、透明压缩等功能都统一起来,每个功能都是一个Interceptor,所有的Interceptor最
    终连接成一个Interceptor.Chain。典型的责任链模式实现。
  • StreamAllocation:用来控制Connections与Streas的资源分配与释放。
  • RouteSelector:选择路线与自动重连。
  • RouteDatabase:记录连接失败的Route黑名单。

三、OKHTTP代码详解

1.OKHTTP 同步请求debug代码跟踪:

  OkHttpClient client = new OkHttpClient();
  Request request = new Request.Builder()
      .url(url)
      .build();
  Response response = client.newCall(request).execute();

从上面代码所示,先是new了一个OKHttpClient对象。

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

OkHttpClient(Builder builder) {
    this.dispatcher = builder.dispatcher;
    this.proxy = builder.proxy;
    //......
    this.connectTimeout = builder.connectTimeout;
    this.readTimeout = builder.readTimeout;
    this.writeTimeout = builder.writeTimeout;
    this.pingInterval = builder.pingInterval; 
  }

上面的代码就是OkHttpClient的构造方法。可以看到OkHttpClient有两个构造方法,在构造方法中我们可以看到会初始化一个Builder对象(OKHttp使用了建造者模式),根据构造方法的代码,很容易发现在构造方法中主要设置了一些OKHttp的属相。比如:超时设置、拦截器、HTTPS相关等。


接下来开始创建Request对象,Request描述了OkHttp将要发送的请求。比如:URL、HTTP header、请求类型(GET请求或者POST请求)等。

Request(Builder builder) {
    this.url = builder.url;
    this.method = builder.method;
    this.headers = builder.headers.build();
    this.body = builder.body;
    this.tag = builder.tag != null ? builder.tag : this;
  }

可以看到Request也是通过建造者模式创建的,在这里配置了url、请求头等信息。

在上面OKHttpClient和Request创建好之后,就开始发起HTTP请求了。OkHttp中请求方式分为同步请求(client.newCall(request).execute() )和异步请求(client.newCall(request).enqueue())两种,其中同步请求和异步请求的区别就是同步请求会阻塞当前线程,异步请求会放到线程池中执行。


OkHttpClient.newCall(Request) 开始。下面是这个方法的定义,它会创建一个 RealCall 对象,并把 OkHttpClient 对象和 Request 对象作为参数传入进去:

@Override public Call newCall(Request request) {
    return RealCall.newRealCall(this, request, false /* for web socket */);
}

然后,RealCall 调用内部的静态方法 newRealCall 在其中创建一个 RealCall 实例并将其返回:

static RealCall newRealCall(OkHttpClient client, Request originalRequest, boolean forWebSocket) {
    RealCall call = new RealCall(client, originalRequest, forWebSocket);
    call.eventListener = client.eventListenerFactory().create(call);
    return call;
}

然后,当返回了 RealCall 之后,我们又会调用它的 execute() 方法来获取响应结果,下面是这个方法的定义:

    @Override public Response execute() throws IOException {
        synchronized (this) {
            if (executed) throw new IllegalStateException("Already Executed");
            executed = true;
        }
        captureCallStackTrace();
        eventListener.callStart(this);
        try {
            // 加入到一个双端队列中
            client.dispatcher().executed(this);
            // 从这里拿的响应Response
            Response result = getResponseWithInterceptorChain();
            if (result == null) throw new IOException("Canceled");
            return result;
        } catch (IOException e) {
            eventListener.callFailed(this, e);
            throw e;
        } finally {
            client.dispatcher().finished(this);
        }
    }

首先是:

        synchronized (this) {
            if (executed) throw new IllegalStateException("Already Executed");
            executed = true;
        }

判断call是否执行过,可以看出每个Call对象只能使用一次原则。然后调用了captureCallStackTrace()方法。

captureCallStackTrace()这个方法其实是捕获了这个请求的StackTrace。

然后进入了第一个核心类—Dispatcher的的execute方法了,由于下面是进入了关键部分,所以重点讲解下,代码如何:

    try {
     client.dispatcher().executed(this);
     Response result = getResponseWithInterceptorChain();
     if (result == null) throw new IOException("Canceled");
     return result;
   } finally {
     client.dispatcher().finished(this);
   }

看下OKHttpClient的dispatcher()方法的具体内容如下图

 //OKHttpClient.java
  public Dispatcher dispatcher() {
    return dispatcher;
  }

大家发现client.dispatcher()返回的是Dispatcher对象,那么这个Dispatcher对象是何时创建的那?在OkHttpClient.java里面Build类里面的构造函数里面,如下图

//OkHttpClient.java
public static final class Builder {
   //其它代码先忽略掉
    public Builder() {
      dispatcher = new Dispatcher();
      //其它代码先忽略掉
    }
}

所以默认执行Builder()放到时候就创建了一个Dispatcher。那么咱们看下dispatcher里面的execute()是如何处理的

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

里面发现是runningSyncCalls执行了add方法莫非runningSyncCalls是个list,咱们查看dispatcher里面怎么定义runningSyncCalls的。

  /** 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<>();

原来runningSyncCalls是双向队列啊,突然发现Dispatcher里面定义了三个双向队列,看下注释,我们大概能明白readyAsyncCalls 是一个存放了等待执行任务Call的双向队列,runningAsyncCalls是一个存放异步正在请求任务Call的双向任务队列,runningSyncCalls是一个存放同步正在请求的双向队列。关于队列咱们在下篇文章里面详细介绍

执行完client.dispatcher().executed(this);要走到getResponseWithInterceptorChain();方法了里面了,看下这个方法是具体做什么的?

  Response getResponseWithInterceptorChain() throws IOException {
    // Build a full stack of interceptors.
    List<Interceptor> interceptors = new ArrayList<>();
   
    //添加开发者应用层自定义的Interceptor
    interceptors.addAll(client.interceptors());


    //这个Interceptor是处理请求失败的重试,重定向    
    interceptors.add(retryAndFollowUpInterceptor);

	// 桥拦截器
    //这个Interceptor工作是添加一些请求的头部或其他信息
    //并对返回的Response做一些友好的处理(有一些信息你可能并不需要)
    interceptors.add(new BridgeInterceptor(client.cookieJar()));
    
	// 缓存拦截器:从缓存中拿数据
    //这个Interceptor的职责是判断缓存是否存在,读取缓存,更新缓存等等
    interceptors.add(new CacheInterceptor(client.internalCache()));
    
	// 网络连接拦截器:建立网络连接
	//这个Interceptor的职责是建立客户端和服务器的连接
    interceptors.add(new ConnectInterceptor(client));
  
    if (!forWebSocket) {
      //添加开发者自定义的网络层拦截器
      interceptors.addAll(client.networkInterceptors());
    }

    // 服务器请求拦截器:向服务器发起请求获取数据
    interceptors.add(new CallServerInterceptor(forWebSocket));
    //一个包裹这request的chain
    Interceptor.Chain chain = new RealInterceptorChain(interceptors, null, null, null, 0,
            originalRequest, this, eventListener, client.connectTimeoutMillis(),
            client.readTimeoutMillis(), client.writeTimeoutMillis());

    //把chain传递到第一个Interceptor手中
    return chain.proceed(originalRequest);
  }

发现 new了一个ArrayList,然后就是不断的add,后面 new了 RealInterceptorChain对象,最后调用了chain.proceed()方法。先看下RealInterceptorChain的构造函数。

 public RealInterceptorChain(List<Interceptor> interceptors, StreamAllocation streamAllocation,
      HttpCodec httpCodec, RealConnection connection, int index, Request request) {
    this.interceptors = interceptors;
    this.connection = connection;
    this.streamAllocation = streamAllocation;
    this.httpCodec = httpCodec;
    this.index = index;
    this.request = request;
  }

发现什么都没做就是做了赋值操作,后面跟踪下chain.proceed()方法
由于Interceptor是个接口,所以应该是具体实现类RealInterceptorChain的proceed实现

public interface Interceptor {
 Response intercept(Chain chain) throws IOException;

 interface Chain {
   Request request();

   Response proceed(Request request) throws IOException;

   Connection connection();
 }
}
public final class RealInterceptorChain implements Interceptor.Chain{
  Response intercept(Chain chain) throws IOException;

      @Override 
      public Response proceed(Request request) throws IOException {
        return proceed(request, streamAllocation, httpCodec, connection);
      }

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

        calls++;

        // If we already have a stream, confirm that the incoming request will use it.
        if (this.httpCodec != null && !this.connection.supportsUrl(request.url())) {
            throw new IllegalStateException("network interceptor " + interceptors.get(index - 1)
                    + " must retain the same host and port");
        }

        // If we already have a stream, confirm that this is the only call to chain.proceed().
        if (this.httpCodec != null && calls > 1) {
            throw new IllegalStateException("network interceptor " + interceptors.get(index - 1)
                    + " must call proceed() exactly once");
        }

        // Call the next interceptor in the chain.
        RealInterceptorChain next = new RealInterceptorChain(
                interceptors, streamAllocation, httpCodec, connection, index + 1, request);
        Interceptor interceptor = interceptors.get(index);
        Response response = interceptor.intercept(next);

        // Confirm that the next interceptor made its required call to chain.proceed().
        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对象时候httpCodec直接赋予了null,所以下面代码直接略过。

   // If we already have a stream, confirm that the incoming request will use it.
    if (this.httpCodec != null && !this.connection.supportsUrl(request.url())) {
      throw new IllegalStateException("network interceptor " + interceptors.get(index - 1)
          + " must retain the same host and port");
    }

    // If we already have a stream, confirm that this is the only call to chain.proceed().
    if (this.httpCodec != null && calls > 1) {
      throw new IllegalStateException("network interceptor " + interceptors.get(index - 1)
          + " must call proceed() exactly once");
    }

然后看到在proceed方面里面又new了一个RealInterceptorChain类的next对象,温馨提示下,里面的streamAllocation, httpCodec, connection都是null,所以这个next对象和chain最大的区别就是index属性值不同chain是0.而next是1,然后取interceptors下标为1的对象的interceptor。由从上文可知,如果没有开发者自定义的Interceptor时,首先调用的RetryAndFollowUpInterceptor,如果有开发者自己定义的interceptor则调用开发者interceptor。

这里重点说一下,由于后面的interceptor比较多,且涉及的也是重要的部分,而咱们这里主要是讲流程,所以这里就不详细和大家说了,由后面再详细讲解,后面的流程是在每一个interceptor的intercept方法里面都会调用chain.proceed()从而调用下一个interceptor的intercept(next)方法,这样就可以实现遍历getResponseWithInterceptorChain里面interceptors的item,实现遍历循环,缩减后的代码如下:

  //RetryAndFollowUpInterceptor.java
public Response intercept(Chain chain) throws IOException {
 //忽略部分代码
 response = ((RealInterceptorChain) chain).proceed(request, streamAllocation, null, null);
 //忽略部分代码
}

//BridgeInterceptor.java
public Response intercept(Chain chain) throws IOException {
  //忽略部分代码
  Response networkResponse = chain.proceed(requestBuilder.build());
  //忽略部分代码
}

//CacheInterceptor.java
public Response intercept(Chain chain) throws IOException {
   //忽略部分代码
   networkResponse = chain.proceed(networkRequest);
   //忽略部分代码
}

//ConnectInterceptor.java
public Response intercept(Chain chain) throws IOException {
     //忽略部分代码
     return realChain.proceed(request, streamAllocation, httpCodec, connection);
}

读过源码我们知道getResponseWithInterceptorChain里面interceptors的最后一个item是CallServerInterceptor.java,最后一个Interceptor(即CallServerInterceptor)里面是直接返回了response 而不是进行继续递归,具体里面是通过OKio实现的,具体代码,等后面再详细说明,CallServerInterceptor返回response后返回给上一个interceptor,一般是开发者自己定义的networkInterceptor,然后开发者自己的networkInterceptor把他的response返回给前一个interceptor,依次以此类推返回给第一个interceptor,这时候又回到了realCall里面的execute()里面了,代码如下:

  @Override 
  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);
    }
  }
最后把response返回给get请求的返回值。至此整体GET请求的大体流程就已经结束了。(PS:最后别忘记走client.dispatcher().finished(this))

大体流程如下图:
在这里插入图片描述
在这里插入图片描述

2.OKHTTP 异步请求debug代码跟踪:

OkHttpClient client = new OkHttpClient();

String run(String url) throws IOException {
  Request request = new Request.Builder()
      .url(url)
      .build();

  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了一个OKHttp和Request。这块和同步一样就不说了,那么说说和同步不一样的地方,后面异步进入enqueue()方法

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

由于executed默认为false,所以先进行判断是否为true,为true则直接跑异常,没有则设置为true,可以看出executed这个是一个标志,标志这个请求是否已经正在请求中,合同步一样先调用了captureCallStackTrace();然后调用 client.dispatcher().enqueue(new AsyncCall(responseCallback));client.dispatcher()返回的是Dispatcher对象所以实际调用的是Dispatcher的enqueue(),那么咱们进入源码看下

  //Dispatcher.java
  private int maxRequests = 64;
  private int maxRequestsPerHost = 5;

  synchronized void enqueue(AsyncCall call) {
  //如果正在执行的请求小于设定值即64,并且请求同一个主机的request小于设定值即5
    if (runningAsyncCalls.size() < maxRequests && runningCallsForHost(call) < maxRequestsPerHost) {
      //添加到执行队列,开始执行请求
      runningAsyncCalls.add(call);
      //获得当前线程池,没有则创建一个
      executorService().execute(call);
    } else {
      //添加到等待队列中
      readyAsyncCalls.add(call);
    }
  }

根据源码和注释大家可以看到如果正在执行的异步请求小于64,并且请求同一个主机小于5的时候就先往正在运行的队列里面添加这个call,然后用线程池去执行这个call,否则就把他放到等待队列里面。执行这个call的时候,自然会去走到这个call的run方法,那么咱们看下AsyncCall.java这个类,而AsyncCall.java又继承自NamedRunnable.java咱们就一起看下他们的源码

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();
}

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的构造方法设置了name在的run方法里面设定为当前线程的name,而NamedRunnable的run方法里面调用了它自己的抽象方法execute,由此可见NamedRunnable的作用就是设置了线程的name,然后回调子类的execute方法,那么我们来看下AsyncCall的execute方法。貌似好像又回到了之前同步的getResponseWithInterceptorChain()里面,根据返回的response来这只callback回调。所以我们得到了OKHTTP的大体流程,如下图:
OKHTTP大体流程

四、OKHTTP类详解

大体核心类主要下图:
在这里插入图片描述
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值