Okhttp源码学习之总体流程

本文详细探讨了Okhttp的使用实例和整体流程,包括创建OkhttpClient、构造Request对象、同步与异步模式。重点解析了OkhttpClient的创建方式、涉及的设计模式,以及Request的构建。此外,还深入介绍了异步模式下的enqueue方法和Dispatcher调度器的工作原理,以及同步模式的execute方法。通过对Okhttp源码的分析,展示了网络请求的完整流程。
摘要由CSDN通过智能技术生成

这个库一直想要去学习,不过一直没有时间去做,打算一点点梳理Okhttp。本文将按照发起网络请求的顺序来详细描述下okhttp是如何发起网络请求的,其中包含一些优秀文章可供大家参考。

使用实例

先放一段Okhttp的使用代码。

       //1
        OkHttpClient client=new OkHttpClient();
        //2
        Request request = new Request.Builder()
        .url("url)
        .build();
        //3
		client.newCall(request).enqueue(new Callback() {
    		@Override
    		public void onFailure(Call call, IOException e) {
        		e.printStackTrace();
    		}
    		@Override
    		public void onResponse(Call call, Response response) throws IOException {
    			String results = response.body().string(); 
    		}
		}); 

我们阅读源码就要从暴露给用户使用的那个类先去入手,对应Okhttp就是**(创建)OkhttpClient**,以及RequestCallback,在新写Callback的时候要重写onFailure(Call call, IOException e)OnResponse(Call call, Response response)两个函数,于是另一个重要的类Response类简单来说,使用的时候,只需要传入网络的url地址,然后加上新创建的Callback对象(包括重写两个函数)就完成了对url地址的访问(获取数据或上传数据)。

整体流程

1.创建OkhttpClient实例

在读源码之前,每个类都会有一些注释,先阅读下注释。

1.1 用户使用时,创建全局(单例)实例

这里的意思就是说,最好在整个应用中只包含一个(全局)OkhttpClient实例,在每一个发起Http请求的时候重用该实例即可(单例),同时复用的还有Response缓存、共用线程池以及共用连接池。

 * <h3>OkHttpClients should be shared</h3>
 *
 * <p>OkHttp performs best when you create a single {@code OkHttpClient} instance and reuse it for
 * all of your HTTP calls. This is because each client holds its own connection pool and thread
 * pools. Reusing connections and threads reduces latency and saves memory. Conversely, creating a
 * client for each request wastes resources on idle pools.

1.2 构造方式

可以使用两种方式创建OkHttpClient的实例(构造函数和使用Builder),以及一种特殊的构造方式。
还是先看注释。

<p>Use {@code new OkHttpClient()} to create a shared instance with the default settings:
 * <pre>   {@code
 *
 *   // The singleton HTTP client.
 *   public final OkHttpClient client = new OkHttpClient();
 * }</pre>
 *
 * <p>Or use {@code new OkHttpClient.Builder()} to create a shared instance with custom settings:
 * <pre>   {@code
 *
 *   // The singleton HTTP client.
 *   public final OkHttpClient client = new OkHttpClient.Builder()
 *       .addInterceptor(new HttpLoggingInterceptor())
 *       .cache(new Cache(cacheDir, cacheSize))
 *       .build();
 * }</pre>

我们来读源码。
这是第一种默认的方式

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

最后会调用该函数,使用的就是默认的Builder

private OkHttpClient(Builder builder) {
	this.dispatcher = builder.dispatcher;
    this.proxy = builder.proxy;
    this.protocols = builder.protocols;
    this.connectionSpecs = builder.connectionSpecs;
    ...
}

第二种方式:想要配置OkhttpClient的一些参数,就需要使用Builder。

public final OkHttpClient client = new OkHttpClient.Builder()
        .addInterceptor(new HttpLoggingInterceptor())
        .cache(new Cache(cacheDir, cacheSize))
        ...//其他设置
        .build();

最后调用Builder类内的build函数,这里的this就是Builder,所以会调用OkhttpClient(Builder builder)来更改client的数据域。

 public OkHttpClient build() {
      return new OkHttpClient(this);
    }

总结:OkHttpClient本身不能设置参数,需要借助于其内部类Builder设置参数,参数设置完成后,调用Builder的build方法得到一个配置好参数的OkHttpClient对象。这些配置的参数会对该OkHttpClient对象所生成的所有HTTP请求都有影响。
第三种方式:我们先看注释中的文字。
您可以使用{@link#NewBuilder()}自定义共享的OkHttpClient实例。 这将构建一个共享相同连接池,线程池和配置的客户端。 使用生成器方法为特定目的配置派生客户端。
共享相同连接池,线程池和配置的客户端,共享谁的呢?当然是共享那个client实例的。有时候我们想单独给某个网络请求设置特别的几个参数,比如只想设置某个请求的超时时间,但是还想保持OkHttpClient对象(client)中的其他的参数设置,那么可以调用OkHttpClient对象的newBuilder()方法。

* <h3>Customize your client with newBuilder()</h3>
 *
 * <p>You can customize a shared OkHttpClient instance with {@link #newBuilder()}. This builds a
 * client that shares the same connection pool, thread pools, and configuration. Use the builder
 * methods to configure the derived client for a specific purpose.
 *
 * <p>This example shows a call with a short 500 millisecond timeout: <pre>   {@code
 *
 *   OkHttpClient eagerClient = client.newBuilder()
 *       .readTimeout(500, TimeUnit.MILLISECONDS)
 *       .build();
 *   Response response = eagerClient.newCall(request).execute();
 * }</pre>

这里的this是Okhttp的实例

public Builder newBuilder() {
    return new Builder(this);
  }

之后会调用Builder类中的这个构造函数,这样就在builder中保存了OkHttpClient中的参数。

 Builder(OkHttpClient okHttpClient) {
      this.dispatcher = okHttpClient.dispatcher;
      this.proxy = okHttpClient.proxy;
      this.protocols = okHttpClient.protocols;
      this.connectionSpecs = okHttpClient.connectionSpecs;
      ...
    }

1.3 优点

不需要将其杀掉。
保持空闲的线程和连接将自动释放。

<h3>Shutdown isn't necessary</h3>

1.4 涉及的模式

1.构造者模式

不用说了,都有builder了,肯定就是构造者模式了。

2.单例模式

有点单例模式的意思,但这里没有,就是我们构建的OkhttpCilent最好要单例,只构造一个来复用。

3.外观模式

用户可以使用这个类来进行一些操作来实现内部的功能。

2.构造Request对象

构造方法

Request也是需要通过构造者模式来构造。

private 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;
  }

下面是Builder中的部分代码,可以看到无参构造方法时Builder会默认method为get,其实我们可以简单理解为就是需要将url传给Request,以便于它发送网络请求。也可以看到我们如果想要构建Request对象就一定要构建一个Builder对象因为Builder类中的每个设置参数的函数都是返回this,所以可以使用链式结构赋值,最后使用build构建时,直接调用new Request(this),这里的this是Builder对象。

//使用代码
Request request=new Request.Builder().url(address).build();
//源码
 public static class Builder {
    private HttpUrl url;
    private String method;
    private Headers.Builder headers;
    private RequestBody body;
    private Object tag;

    public Builder() {
      this.method = "GET";
      this.headers = new Headers.Builder();
    }

    private Builder(Request request) {
      this.url = request.url;
      this.method = request.method;
      this.body = request.body;
      this.tag = request.tag;
      this.headers = request.headers.newBuilder();
    }

    public Builder url(HttpUrl url) {
      if (url == null) throw new NullPointerException("url == null");
      this.url = url;
      return this;
    }
    ...
    public Request build() {
      if (url == null) throw new IllegalStateException("url == null");
      return new Request(this);
    }
get与post

get方式很简单,因为我们RequestBuilder的无参构造函数默认将method设置为GET。

Request request = new Request.Builder()
        .url("http://www.baidu.com")
        .build();

而post方式则不同,我们需要借助RequestBody类与post方法。因为post方法会将所上传的信息封装在请求数据中并发给服务器,这里请求数据就是RequestBody,具体代码如下。

首先要创建一个RequestBody(传入当前MediaType类型和json数据),同时要调用Builder的post方法,并传入RequestBody实例。在post方法中会将method置为post,对body赋值。

public static final MediaType JSON = MediaType.parse("application/json; charset=utf-8");
OkHttpClient client = new OkHttpClient();
String post(String url, String json) throws IOException {
    RequestBody body = RequestBody.create(JSON, json);
    Request request = new Request.Builder()
      .url(url)
      .post(body)
      .build();
    Response response = client.newCall(request).execute();
    if (response.isSuccessful()) {
        return response.body().string();
    } else {
        throw new IOException("Unexpected code " + response);
    }
}

3.同步模式与异步模式

3.1 简介

当构建完Request之后,我们就需要构建一个Call,Okhttp中使用异步模式就是使用enqueue方法,而同步模式就是使用execute方法。同步方式没有传入Callback对象,我们可以通过判断返回的response的isSuccessfull字段来判断是否请求成功。
同步方式:

Request request = new Request.Builder()
        .url(url)
        .build();
Response response = client.newCall(request).execute();
 if (response.isSuccessful()) {
        return response.body().string();
    } else {
        throw new IOException("Unexpected code " + response);
    }

异步方式:enqueue()方法的内部已经开好子线程了,在子线程中去执行HTTP请求,并将最终的请求结果回调OKhttp.Callback当中,在调用它的时候要重写onResponse(),得到服务器返回的具体内容,重写onFailure(),在这里对异常情况进行处理。不可以再这里进行UI操作。如果要进行UI操作一定要通过runOnUiThread(new Runnable(){});切换回UI线程

Request request = new Request.Builder()
        .url(url)
        .build();
client.newCall(request).enqueue(new Callback() {
    @Override
    public void onFailure(Call call, IOException e) {
        e.printStackTrace();
         runOnUiThread(new Runnable() {
                    @Override
                    public void run() {
                        Toast.makeText(WeatherActivity.this, "获取天气信息失败",
                                Toast.LENGTH_SHORT).show();
                        //刷新事件结束,隐藏刷新进度条
                        swipeRefresh.setRefreshing(false);
                    }
                });
    }
    @Override
    public void onResponse(Call call, Response response) throws IOException {
    	String results = response.body().string(); 
    	runOnUiThread(new Runnable() {
                    @Override
                    public void run() {
                    	...
                    	}
                   });
    }
});

3.2 异步方式

3.2.1 newcall()方法

这里我们是使用异步方式,所以先看异步的代码。

client.newCall(request).enqueue(callback);

我们回到OkhttpClient,去看newCall方法,发现返回了的其实是 RealCall对象

/**
   * Prepares the {@code request} to be executed at some point in the future.
   */
  @Override public Call newCall(Request request) {
    return new RealCall(this, request, false /* for web socket */);
  }
  

能看到OkHttpClient是实现了接口Call.Factory,很明显这里是工厂模式将(Call)构建的细节交给实现它的类实现,顶层只需要返回Call对象即可,我们不管是怎么构建的call。

public class OkHttpClient implements Cloneable, Call.Factory, WebSocket.Factory {...}
interface Factory {
    Call newCall(Request request);
  }

继续回到RealCall方法,能看到刚刚调用的构造函数,基本赋值包括OkHttpClient,Request,以及一个boolean变量,这里默认是给false。

 RealCall(OkHttpClient client, Request originalRequest, boolean forWebSocket) {
    this.client = client;
    this.originalRequest = originalRequest;
    this.forWebSocket = forWebSocket;
    this.retryAndFollowUpInterceptor = new RetryAndFollowUpInterceptor(client, forWebSocket);
  }

涉及到知识盲点了,默认创建一个RetryAndFollowUpInterceptor对象,这个类被叫做过滤器/拦截器

3.2.2 enqueue()方法

现在Call创建完成(RealCall),接下来将请求加入调度,代码如下:

client.newCall(request).enqueue(new Callback() {
    @Override
    public void onFailure(Call call, IOException e) {
        ...
    }
    @Override
    public void onResponse(Call call, Response response) throws IOException {
    	...
    }
});

这里是调用了Call对象的enqueue方法,但其实是RealCall对象的enqueue方法,继续回到RealCall类。可以看到这里使用了synchronized加入了对象锁,防止多线程同时调用,如果用过就会抛出异常,没有用过就会将boolean变量赋值为true。
百度百科:synchronized
synchronized方法后面的括号中是对象,就是对象锁,如果线程进入,则得到当前对象锁,那么别的线程在该类所有对象上的任何操作都不能进行

之后使用了captureCallStackTrace()方法。

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

captureCallStackTrace()方法中相当于为RetryAndFollowUpInterceptor对象(拦截器)加入了一个用于追踪堆栈信息的callStackTrace。

 private void captureCallStackTrace() {
    Object callStackTrace = Platform.get().getStackTraceForCloseable("response.body().close()");
    retryAndFollowUpInterceptor.setCallStackTrace(callStackTrace);
  }

之后使用了 client.dispatcher().enqueue(new AsyncCall(responseCallback));创建了一个AsyncCall的实例

看下 AsyncCall类,这里可以简单先理解为把Callback对象先存储在该类中,之后会叙述该类的作用。

final class AsyncCall extends NamedRunnable {
    private final Callback responseCallback;

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

之后看下client.dispatcher()方法返回了OkHttpClient类中的Dispatcher对象(调度器)。该Dispatcher对象是在OkHttpClient对象构造时,获取Bulider对象中的Dispatcher对象。(总结一下:所有OkHttpClient中的数据域对象,全是Builder类中的对象,在Builder类构造函数中被赋值

public Dispatcher dispatcher() {
    return dispatcher;
  }
3.2.3 Dispatcher

进入Dispathcer类,首先看该类的成员变量(数据域)。

  //最大异步请求数为64
  private int maxRequests = 64;
  //每个主机的最大请求数为5
  private int maxRequestsPerHost = 5;
  private Runnable idleCallback;
	
	//线程池
  /** Executes calls. Created lazily. */
  private 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<>();

我好菜,连Deque是双端队列都不知道。简单来说双端队列就是可以对队首和队尾插入和移除元素。这里稍微补充一下Deque的方法。
在这里插入图片描述
看起来与LinkedList有点相似。都是即可作为栈也可以作为队列
在这里插入图片描述
之后去看该类的enqueue方法。该方法进行了上锁,线程获得了成员锁,即一次只可以有一个线程访问该方法,其他线程想要访问的时候就会被阻塞,需要等到该线程用完才可以访问。

synchronized void enqueue(AsyncCall call) {
	//判断正在执行的异步请求数没有达到阈值,并且该对象需要的主机请求数也没有达到阈值
    if (runningAsyncCalls.size() < maxRequests && runningCallsForHost(call) < maxRequestsPerHost) {
    //加入到执行队列,并立即执行。
      runningAsyncCalls.add(call);
      executorService().execute(call);
    } else {
    //加入等待队列
      readyAsyncCalls.add(call);
    }
  }

runningCallsForHost函数 返回该请求(AsyncCall对象)需要的主机请求数。

/** Returns the number of running calls that share a host with {@code call}. */
  private int runningCallsForHost(AsyncCall call) {
    int result = 0;
    for (AsyncCall c : runningAsyncCalls) {
      if (c.host().equals(call.host())) result++;
    }
    return result;
  }

加入到执行队列后,还有一行代码。可以理解为使用线程池创建一个线程去执行该请求(任务)
这里可以看到放入线程池中,线程池会创建一个线程去执行该请求,因此前面说的enqueue中会为任务自己开启线程去执行。

 executorService().execute(call);

executorService()方法是通过懒汉单例模式创建了一个线程池。各个参数的含义如下

  • 0(corePoolSize):核心线程的数量为 0,空闲一段时间后所有线程将全部被销毁。
  • Integer.MAX_VALUE(maximumPoolSize): 最大线程数,当任务进来时可以扩充的线程最大值,相当于无限大。
  • 60(keepAliveTime): 非核心线程的处于空闲后的最大存活时间。
  • TimeUnit.SECONDS:存活时间的单位是秒。
  • new SynchronousQueue():工作队列,先进先出。
  • Util.threadFactory(“OkHttp Dispatcher”, false):单个线程的工厂 ,通过线程工厂创建线程。

简单来说,在实际运行中,当收到10个并发请求时,线程池会创建十个线程,当工作完成后,线程池会在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;
  }

execute()方法可以理解为将任务添加到线程去执行(当将一个任务添加到线程池中的时候,线程池会为每个任务创建一个线程,该线程会在之后的某个时刻自动执行。)。
什么样的类叫做任务呢。任务就是一个实现了Runnable接口的类。线程就会在接下来自动执行(可以理解为执行该任务类的run()方法)。
综上所述,可以总结下enqueue方法。
该方法的核心思想如下:

首先做出判断:
1执行队列的大小(队列中AsyncCall的数量也相当于正在执行的异步请求数)是否小于阈值
2.执行该请求(AsyncCall对象)的主机请求数是否小于主机的最大请求数。
满足1和2就将该请求(任务)添加到执行队列,立刻执行任务(将任务添加到线程池之后,线程池会为任务创建一个线程),否则进入等待队列
接下来要看一下我们的从后台获取的数据是从哪里返回的。

3.2.4 AsyncCall

前面说到简单先理解AsyncCall存储了我们的Callback对象,而且使用过Okhttp的人都知道,返回数据的操作都是在Callback对象的两个函数(onFailure()与OnResponse()方法)中。

client.newCall(request).enqueue(new Callback() {
    @Override
    public void onFailure(Call call, IOException e) {
        e.printStackTrace();
    }
    @Override
    public void onResponse(Call call, Response response) throws IOException {
    	String results = response.body().string(); 
    }
});

那么这两个方法是在哪里调用的呢,别跟我说是请求成功和请求失败的时候调用的。我说的是具体位置好吧。

我们把目光放到AsyncCall类去,AsyncCall是RealCall的内部类
为什么要看该类,因为线程执行的是该任务,我们去找下他是怎么实现的Runnable接口,也可以想到数据应该是在run()函数中获取到的。

final class AsyncCall extends NamedRunnable

可以看到他继承自NamedRunnable,我们进入该抽象类。没的说,该类实现了Runnable接口。可以看到run函数中执行的是execute()函数execute函数是一个抽象的函数。那么一目了然,AsyncCall类继承该抽象类就一定会实现execute()函数。同时,将一个AsyncCall任务添加到线程池中,线程在执行的就是所传入任务(AsyncCall实例)的execute()函数。

/**
 * 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类的execute()方法。果不其然,首先通过**getResponseWithInterceptorChain()**方法获得Response,之后根据判断过滤器retryAndFollowUpInterceptor是否被取消了,来判断请求成功还是失败进而回调Callback的onFailure还是onResponse。在回调函数中传递RealCall与response/IOException。那么核心就是getResponseWithInterceptorChain()方法了。

final class AsyncCall extends NamedRunnable {
    private final Callback responseCallback;

    AsyncCall(Callback responseCallback) {
      super("OkHttp %s", redactedUrl());
      this.responseCallback = responseCallback;
    }
    ...
     @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);
      }
    }
    }
3.2.5 getResponseWithInterceptorChain

接着看getResponseWithInterceptorChain的代码,简单来看,首先创建了一个List用来存储过滤器,其实在创建Okhttp对象的时候,都帮我们默认实现了这些过滤器。这里涉及了每个过滤器完成自己的任务,互不耦合。

Response getResponseWithInterceptorChain() throws IOException {
    // Build a full stack of interceptors.
    List<Interceptor> interceptors = new ArrayList<>();
    interceptors.addAll(client.interceptors());
    //失败和重定向过滤器
    interceptors.add(retryAndFollowUpInterceptor);
    //封装request和response过滤器
    interceptors.add(new BridgeInterceptor(client.cookieJar()));
    //缓存相关的过滤器,负责读取缓存直接返回、更新缓存
    interceptors.add(new CacheInterceptor(client.internalCache()));
     //负责和服务器建立连接
    interceptors.add(new ConnectInterceptor(client));
    if (!forWebSocket) {
    //配置 OkHttpClient 时设置的 networkInterceptors
      interceptors.addAll(client.networkInterceptors());
    }
     //负责向服务器发送请求数据、从服务器读取响应数据(实际网络请求)
    interceptors.add(new CallServerInterceptor(forWebSocket));

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

之后执行过滤器,其他可以先不考虑,单单看index,这个index是干嘛用的呢,我们继续向下看代码。

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

创建RealInterceptorChain后,会调用该对象的proceed方法,直接去看下面的第二个方法。

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

  public Response proceed(Request request, StreamAllocation streamAllocation, HttpCodec httpCodec,
      Connection 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 && !sameConnection(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;
  }

首先就有下面的代码,interceptors是我们之前创建RealInterceptorChain对象时传入的list。

if (index >= interceptors.size()) throw new AssertionError();

省略两个判断,直接到这里,注释写着调用链中的下一个拦截器。而且创建了新的RealInterceptorChain也使用了下个的index。根据我的理解就是首先取出当前index的拦截器(interceptors.get(index)),然后将其与下一个连接起来(或者可以理解为去处理下一个index的拦截器)。这里就有了递归调用的感觉

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

我们去看一下Interceptor的intercept方法。可以看到他继续调用了RealInterceptorChain的proceed方法,这就是递归调用了。最后返回经过所有拦截器之后的Response,这就是getResponseWithInterceptorChain() 方法返回的结果。

@Override public Response intercept(Chain chain) throws IOException {
    Request request = chain.request();

    streamAllocation = new StreamAllocation(
        client.connectionPool(), createAddress(request.url()), callStackTrace);

    int followUpCount = 0;
    Response priorResponse = null;
    while (true) {
      if (canceled) {
        streamAllocation.release();
        throw new IOException("Canceled");
      }

      Response response = null;
      boolean releaseConnection = true;
      try {
        response = ((RealInterceptorChain) chain).proceed(request, streamAllocation, null, null);
        releaseConnection = false;
      }
      ...
      }
}

这里应该是使用了责任链模式。
总结下getResonseWithInterceptorChain方法()做的事情。

  1. 创建一个List(interceptors)用来存储拦截器
  2. 创建RealInterceptorChain对象,其中传入的包括interceptorsindex(为0,从第一个开始)
  3. 调用RealInterceptorChain对象的proceed方法,在该方法中创建新的RealInterceptorChain对象(next),传入list与index(1)。取出lsit中当前index的拦截器(interceptor),声明Response对象,调用该拦截器的intercept方法,并传入新创建的RealInterceptorChain,并将其返回值付给新创建的Response对象。(Response response = interceptor.intercept(next)),在proceed方法的结束会返回Response对象。
  4. 拦截器的intercept方法会继续调用传入的RealInterceptorChain对象的proceed方法,这样就会递归处理拦截器链中的下一个拦截器。
  5. 递归结束后会返回一个Response对象。

3.3同步方式

先回顾一下同步方式是如何请求数据的。

Request request = new Request.Builder()
        .url("http://www.baidu.com")
        .build();
Response response = client.newCall(request).execute();

我们看到了熟悉的身影。

 client.newCall(request)

和异步相同都是先创建一个RealCall对象(实现了接口的newCall方法,运用了工厂模式,不管具体怎么制作,只要一个Call对象。)

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

继续看execute方法。首先调用了Dispatcher对象的executed方法。

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

Dispatcher的executed方法很简单,就是将请求加入同步进行队列中。

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

之后还是通过getResponseWithInterceptorChain()方法获得Response对象。
到这里,同步方法和异步方法就全部讲解完毕。

4.总结

最后以文字形式总结下总体请求流程,几乎可以当做面试回答去回答面试官了有木有。
首先通过OkHttpClientBuilder创建OkHttpClient实例,之后通过OkHttpClient对象的newCall方法,(传入创建好的Request对象),去创建RealCall对象。

异步方式调用RealCall对象的enqueue方法并传入创建好的Callback对象(创建Callback对象需要重写其OnResponse方法与OnFailure方法分别对应网络请求成功与失败),在该方法下,会继续调用Dispatcher调度器的enqueue方法并传入通过Callback对象创建的AsynCall对象,在该方法中会判断是否立即执行该请求,如果可以就加入异步执行队列,并使用线程池创建一个线程来执行该请求(AsynCall)对象(会调用AsynCall对象的execute方法(不返回Response对象,会根据重定向重试拦截器的isCanceled函数来判断在其中调用OnResponse方法或OnFailure方法))。否则进入等待队列

同步方式则直接调用RealCall对象的execute方法,在该方法中调用Dispatcher调度器的executed方法(返回Response对象),直接进入同步执行队列。

之后两个execute方法都会调用getResponseWithInterceptorChain方法去获取Response。在getResponseWithInterceptorChain方法中会递归的去处理下一个Interceptor(拦截器),最终返回Response。

本文精髓

1.OkHttpClient实现Call.Factory,负责为Request创建Call(RealCall)。

2.异步方式中加入到执行队列后线程池创建线程并执行该请求会调用AsynCall对象的executed方法,这是因为抽象类NamedRunnable实现了Runnable接口并在run函数中执行execute方法(在类中为抽象方法)而AsynCall类又继承了NamedRunnable类,因此执行请求相当于调用AsynCall对象的execute方法。

3.同步方式调用的是RealCall对象的execute方法,在该方法中首先调用Dispatcher的executed方法,将RealCall对象加入同步执行队列,之后会调用getResponseWithInterceptorChain方法。也就是说同步方式和异步方式调用的最终调用的虽然都是executed方法,但并不是同一个executed方法。网上有的图片直接会都指向同一个executed,难免会让读者疑惑。

4.总结下getResonseWithInterceptorChain方法()做的事情。

  1. 创建一个List(interceptors)用来存储拦截器
  2. 创建RealInterceptorChain对象,其中传入的包括interceptorsindex(为0,从第一个开始)
  3. 调用RealInterceptorChain对象的proceed方法,在该方法中创建新的RealInterceptorChain对象(next),传入list与index(1)。取出lsit中当前index的拦截器(interceptor),声明Response对象,调用该拦截器的intercept方法,并传入新创建的RealInterceptorChain,并将其返回值付给新创建的Response对象。(Response response = interceptor.intercept(next)),在proceed方法的结束会返回Response对象。
  4. 拦截器的intercept方法会继续调用传入的RealInterceptorChain对象的proceed方法,这样就会递归处理拦截器链中的下一个拦截器。
  5. 递归结束后会返回一个Response对象。

流程图

下图是我总结的总体请求流程,可以说是非常详细了。
在这里插入图片描述

参考链接(优秀文章汇总)

1.okhttp源码分析(一)——基本流程(超详细)
2.OkHttp-Request-请求执行流程
3.Okhttp使用详解
4.Okhttp官方网站
5.拆轮子系列:拆 OkHttp
6.拆轮子:OkHttp 的源码解析(三):任务分发器(Dispatcher)
7.OkHttp Dispatcher的调度过程分析

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值