OKHttp原码分析(一)

一,概述

okhttp并不是对某个网络请求类的封装,它是偏底层的网络请求类库,封装的是传输层的socket,与httpURLconnection是同一级别的。OKHttp比起httpURLconnection做了大量的性能优化和在使用上的优化,因此OKHttp的源码也比较复杂,需要连续多篇blog进行讲解分析。

这篇blog以get请求为例主要分析以下几点:
1,OkHttpClient对象的创建。
2,Request 对象的创建。
3,Call对象的创建
4,Call的execute方法实现同步请求。
5,Call的enqueue方法实现异步请求,异步请求是怎么开启的子线程。
6,同步请求与异步请求是怎么殊途同归到一条路上的。

二,OkHttpClient对象的创建

OKHttpClient对象的创建方式有两种。
第一种是使用构造方法创建对象:

OkHttpClient client = new OkHttpClient();//使用构造方法直接创建OkHttpClient对象。

第二种是使用Builder模式创建对象:

OkHttpClient.Builder builder = new OkHttpClient.Builder();//先创建构建者对象
 builder.connectTimeout(3*1000, TimeUnit.MILLISECONDS);//设置超时时间
 OkHttpClient client = builder.build();//使用构建者创建OkHttpClient对象。

1,使用构造方法创建OkHttpClient对象

OkHttpClient的构造方法原码是:

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

本质调用的是OkHttpClient的有参构造,我们先看看这个构造方法的源码,再看Builder类的无参构造方法。

OkHttpClient的有参构造方法的部分原码是

private OkHttpClient(Builder builder) {
    this.dispatcher = builder.dispatcher;
    this.protocols = builder.protocols;
    this.interceptors = Util.immutableList(builder.interceptors);
    this.networkInterceptors = Util.immutableList(builder.networkInterceptors);
    this.cookieJar = builder.cookieJar;
    this.cache = builder.cache;
    this.internalCache = builder.internalCache;
    this.socketFactory = builder.socketFactory;
    。。。略。。。
}

以上是部分代码,从代码中我们可以看出这个构造方法主要是给OkHttpClient的字段进行初始化,OkHttpClient字段的值来自于builder对象。

下面看Builder类的无参构造方法的源码:

public Builder() {
  dispatcher = new Dispatcher();
  socketFactory = SocketFactory.getDefault();//得到SocketFactory对象,这个工程类可以创建Socket对象。
  connectionPool = new ConnectionPool();//链接池
  connectTimeout = 10_000;//设置连接超时时间,默认为10秒
  readTimeout = 10_000;//设置读取超时时间
  writeTimeout = 10_000;//设置写入超时时间
    。。。略。。。
}

Builder的无参构造中主要是对Builder的字段进行初始化,这些初始化值也是OkHttpClient的初始化值,也就是对网络请求参数的初始化。

2,使用Builder模式创建OkHttpClient对象

首先看创建Builder对象使用的代码:

OkHttpClient.Builder builder = new OkHttpClient.Builder();

Builder是OkHttpClient的内部类,创建Builder对象使用的是builder的无参构造方法。在上面我们已经看过Builder的无参构造方法,主要功能就是进行一些字段的初始化。

创建OkHttpClient对象使用的代码是:

 OkHttpClient client = builder.build();

这是一种build设计模式,下面看Builder类的build方法的源码:

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

看到这儿会觉得好熟悉啊,build()方法中调用的也是OkHttpClient的有参构造,参数也是Builder对象。通过上面的分析我们知道OkHttpClient的有参构造主要作用就是给OkHttpClient的字段进行初始化。

3,两种创建OkHttpClient对象方式的区别与应用场景

通过上述分析可知,创建OkHttpClient对象最终都是使用的OkHttpClient的有参构造,且都是通过Builder对象给OkHttpClient的字段进行初始化。

区别是:
1,使用构造方法创建OkHttpClient对象:使用builder的无参构造方法创建builder对象后直接给OkHttpClient的字段进行初始化,即这是一步操作,中间不能做任何设置。
2,使用Builder模式创建OkHttpClient对象:先得到Builder对象,然后再通过代码创建OkHttpClient对象对象。这样我们就可以根据需要修改Builder对象的一些字段值。

应用场景:
在Builder的无参构造方法中我们看到系统对连接超时时间,缓存等字段初始化了一些默认值,如果这些值不是我们想要的,我们要修改某些值,此时就需要使用第二种方法创建OkHttpClient对象。如果系统的默认值满足我们的要求,直接使用第一种方法创建OkHttpClient对象即可。

三,Request 对象的创建

get请求时创建Request 对象的代码如下:

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

很明显这是一种Builder设计模式,首先创建Builder对象,然后给builder对象设置属性,然后再通过Builder对象的build方法创建Request 对象,并把builder的值赋给Request 对象。

注意:这个Builder类是Request 的内部类,与OkHttpClient的内部类Builder不是同一个。

下面看Request 的内部类Builder的无参构造原码:

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

此时发现在创建Builder对象时默认把请求方式设置为GET,所以使用get请求不需要再次设置请求方式。
此外还有一行代码是创建headers 。这是headers 的使用细节,这儿不做讲解。

下面看Builder的url方法的原码。
url方法有三个重载方法,最终调用的都是下面这个方法:

    public Builder url(HttpUrl url) {
      if (url == null) throw new NullPointerException("url == null");
      this.url = url;
      return this;
    }

此时注意两点:
1,url方法就是给builder对象的url属性赋值。
2,此时返回this,这是一种典型的链式编程思想。

下面看Builder的build方法的原码:

    public Request build() {
      if (url == null) throw new IllegalStateException("url == null");
      return new Request(this);
    }

作用就是调用Request的有参构造创建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的属性值赋值给Request的属性。

看到这儿我们应该对builder设计模式有个认识了。首先创建builder对象,然后给builder对象的属性赋值,然后将builder对象的属性值赋值给实际要创建的对象。

四,Call对象的创建

创建call对象使用的代码是:

client.newCall(request)

下面看一下OKHttpClient类的newCall方法的原码:

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

这个方法的作用是创建RealCall对象,并把client对象和request对象都传递过去了。

注意:
1,RealCall是Call接口的实现类,Call调用的方法的实现都在RealCall类中,这个类是关键类。
2,将client对象和request对象传递给了RealCall对象,这样等于RealCall对象拥有了client的属性值和request对象的属性值。即我们在前面对client设置的属性值和对request设置的属性值都传递到了这个对象中。

五,RealCall的execute方法实现同步请求。

通过上面分析我们知道Call是一个接口,他的实现类是RealCall,所以下面看RealCall类的execute方法的源码:

  @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 {
      client.dispatcher().finished(this);
    }
  }

这个方法中的核心代码是:Response result = getResponseWithInterceptorChain()。这个方法中返回的result 即是我们需要的响应数据。所以getResponseWithInterceptorChain是网络请求的核心方法。现在先记住这个方法,这个方法的源码下篇blog再惊喜讲解。

六,RealCall的enqueue方法实现异步请求

首先上RealCall类的enqueue方法的源码:

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

分析:
1,这里的client就是前面创建的OKHttpClient对象。
2,client.dispatcher()方法返回Dispatcher对象,这个类成为调度着,这个对象的创建在OKHttpClient的内部类Builder的构造方法中。
3,重点有两个,一个是Dispatcher的enqueue方法,一个是AsyncCall对象。

下面先看Dispatcher的enqueue方法的源码:

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

这是一个同步方法,核心代码是:executorService().execute(call);

下面首先看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;
  }

这个方法创建了一个线程池对象,这个线程池的特点是:
1,核心线程数为0。
2,最大线程数是Integer的最大值,这里可以理解为无限多。
3,非核心线程存活时间为60秒。

分析:这个线程池没有核心线程,非核心线程的存活时间只有60秒,所以这个线程池的优点不是特别突出。

下面继续看:executorService().execute(call);
executorService()返回一个线程池对象,我们知道execute方法接收Runnable对象,Runnable的run方法执行在子线程。下面开下这个call对象。

这个call对象来源于:client.dispatcher().enqueue(new AsyncCall(responseCallback));

下面看AsyncCall类。
AsyncCall类是RealCall的内部类,AsyncCall继承NamedRunnable类,NamedRunnable又继承Runnable类。在NamedRunnable类中声明了一个抽象方法execute(),且在run方法中调用了。我们由代码executorService().execute(call)知道此时会调用run方法,从而会调用execute方法。
下面看AsyncCall的execute方法的源码(注意:这个方法执行在子线程):

@Override protected void execute() {
      boolean signalledCallback = false;
      try {
        Response response = getResponseWithInterceptorChain();
        if (retryAndFollowUpInterceptor.isCanceled()) {
          responseCallback.onFailure(RealCall.this, new IOException("Canceled"));
        } else {
          responseCallback.onResponse(RealCall.this, response);
        }
      }
    }

在这个方法中我们看到了熟悉的代码:
Response response = getResponseWithInterceptorChain();
这行代码在同步请求中已经看到过,它是网络请求的核心。到此同步请求和异步请求都殊途同归到了getResponseWithInterceptorChain方法上。不同的是:异步请求是在子线程中调用的getResponseWithInterceptorChain方法。

此外在这儿我们看到了CallBack的onFailure方法和onResponse方法被调用。由于execute方法执行在子线程,所以onFailure方法和onResponse方法都是执行在子线程。

注意事项:
1,在RealCAll的enqueue方法中通过线程池的方式开启了子线程。
2,CallBack的onFailure方法和onResponse方法都执行在子线程中,所以在更新UI时还需要跳转到UI线程。

七,同步请求与异步请求是怎么殊途同归到一条路上的

由上面的分析可知无论是同步请求还是异步请求,最后调用的代码都是:
Response response = getResponseWithInterceptorChain();
这行代码是网络请求的核心,以上的代码都是辅助作用。

这行代码返回了Response 对象,所以表层的原码分析到此也算是一个结点。
getResponseWithInterceptorChain内部到底是怎么执行的,下一篇blog再做分析。

  • 4
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值