Okhttp3源码分析

okhttp3源码分析基于okhttp3.10.0。

关于okhttp的特点及相关功能的介绍可以查看官网的介绍:

  1. http://square.github.io/okhttp/
  2. https://github.com/square/okhttp/wiki

基本使用

使用okhttp发起一次网络请求,只需要以下流程即可:

  1. 创建OkHttpClient
  2. 创建Request对象
  3. 创建一个Call对象,用于发起请求
  4. 发起网络请求(同步请求execute; 异步请求enqueue)

代码演示:

public void request()  {

    // 1、创建OkHttpClient
    OkHttpClient okHttpClient = new OkHttpClient();

    // 2、创建Request请求
    Request request = new Request.Builder()
            .url("https://blog.csdn.net/kaifa1321")
            .build();

    // 3、创建一个Call对象,用于发起请求
    Call call = okHttpClient.newCall(request);

    // 4、发起网络请求

    // 4.1、执行一个同步请求,获取Response
    Response response = call.execute();

    // 4.2、执行一个异步请求,通过Callback获取Response
    call.enqueue(new Callback() {
        @Override
        public void onFailure(Call call, IOException e) {}

        @Override
        public void onResponse(Call call, Response response) throws IOException {
            // 处理响应
            String string = response.body().string();
        }
    });
}

源码分析

针对上文中提到的okhttp的使用流程,这里将对这些流程的源码逐一分析。

创建OkHttpClient实例

OkHttpClient对象的创建,okhttp中提供了两种方式:

  • 通过无参数构造方法创建OkHttpClient
  • 通过OkHttpClient内部类Builder,使用建造者模式创建OkHttpClient实例
// 1、构造方法创建OkHttpClient
OkHttpClient okHttpClient = new OkHttpClient();

// 2、通过Builder创建OkHttpClient
OkHttpClient okHttpClient = new OkHttpClient.Builder()
        .addInterceptor(null)
        .cache(null)
        .build();

OkHttpClient实例化过程

这里看下OkHttpClient的构造方法:

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

OkHttpClient(Builder builder) {
    // 这不是该方法的完成代码,具体请看源码
    this.dispatcher = builder.dispatcher;
    this.interceptors = Util.immutableList(builder.interceptors);
    this.cache = builder.cache;
    this.internalCache = builder.internalCache;

    this.connectTimeout = builder.connectTimeout;
    this.readTimeout = builder.readTimeout;
    this.writeTimeout = builder.writeTimeout;

}

通过上面源码可以得知,OkHttpClient的无参构造方法最后也是通过创建一个Builder对象,完成OkHttpClient的实例化。其实无参参构造方法实例化OkHttpClient和通过Builder.build()方法实例化原理上完全一样,都是通过创建一个Builder对象初始化OkHttpClient,唯一不同的是Builder对象参数(系统默认和开发者自己设置)

这里看下Builder.build()方法

public OkHttpClient build() {
   // 这个this就是Builder对象本身
  return new OkHttpClient(this);
}

OkHttpClient通过建造者模式完成实例化,这种创建模式在okhttp中有使用非常多,例如Request、Response的创建都使用了该模式。

定制化OkHttpClient实例

在okhttp的官网中建议开发者在使用okhttp时,OkHttpClient的实例最好是通过单一实例,这样可以更好的管理okhttp的连接池和线程池。
但是,一般在应用开发中,会遇到一些特殊的请求,比如添加一个拦截器、添加缓存等的操作。如果我们直接修改单一实例的OkHttpClient,这样势必会影响其他的请求,okhttp也考虑到了这种情况,也给我们提供给了定制化OkHttpClient实例的方法newBuidler。

  OkHttpClient client = new OkHttpClient();
  client.newBuilder()
          .readTimeout(10, TimeUnit.SECONDS)
          .writeTimeout(10, TimeUnit.SECONDS)
          .build();

这里看下newBuilder方法

  public Builder newBuilder() {
    // this就是OkHttpClient单一实例
    return new Builder(this);
  }

通过已有的OkHttpClient实例创建一个新的Builder对象,这个Builder对象默认持有原来OkHttpClient实例的属性,这样就可以完全复用存在的OkHttpClient的相关设置,只需要对一些特定的属性做出修改即可。例如readTimeout,writeTimeout等。

创建Request对象

Request对象的主要作用就是封装Http请求的主要参数,这些参数包括:

  • url
  • http请求方式:GET、POST等
  • 请求体RequestBody,POST等请求方式时需要
  • http请求头

Request对象和OkHttpClient一样,也是通过建造者模式创建的.

创建Request对象:

    Request request = new Request.Builder()
        .url("https://api.github.com/repos/square/okhttp/issues")
        .header("User-Agent", "OkHttp Headers.java")
        .addHeader("Accept", "application/json; q=0.5")
        .addHeader("Accept", "application/vnd.github.v3+json")
        .post(RequestBody.create(MediaType, File))
        .build();

Request的创建流程相对简单,在上面中基本能够一目了然,这里就不做过多的介绍了。

在上面的代码示例中,需要注意的就是在Request中添加Header请求头的方法header()和addHeader()

在Request的源码中也对两个方法做了具体说明:

  • header():设置唯一的请求头键值对,如果存在相同name的请求头,value将会被覆盖。
  • addHeader():对相同name的请求头可以设置多个Value

RequestBody

在Request的创建过程中,如果该Request是一个POST请求,那么需要为该Request创建一个请求体RequestBody,RequestBody的创建需要调用静态方法create(),RequestBody中有几个重载的create()方法,主要针对不同的请求体类型创建请求体实例。

这里看下RequestBody类的具体内容。

    public abstract class RequestBody {
        // 请求体类型
        public abstract @Nullable MediaType contentType();

        // 请求体长度
        public long contentLength() throws IOException {
            return -1;
        }


        // 将请求体内容写到sink,sink是okio中的类,可以理解成输出流
        public abstract void writeTo(BufferedSink sink) throws IOException;

        /**
         * 创建一个请求体,
         */
        public static RequestBody create(@Nullable MediaType contentType, String content) {
            // 使用UTF-8编码
            Charset charset = Util.UTF_8;
            if (contentType != null) {
                charset = contentType.charset();
                if (charset == null) {
                    charset = Util.UTF_8;
                    contentType = MediaType.parse(contentType + "; charset=utf-8");
                }
            }
            byte[] bytes = content.getBytes(charset);
            return create(contentType, bytes);
        }

        /**
         * 创建请求体
         */
        public static RequestBody create(
                final @Nullable MediaType contentType, final ByteString content) {
            return new RequestBody() {
                @Override public @Nullable MediaType contentType() {
                    return contentType;
                }

                @Override public long contentLength() throws IOException {
                    return content.size();
                }

                @Override public void writeTo(BufferedSink sink) throws IOException {
                    sink.write(content);
                }
            };
        }

        /**
         * 创建请求体
         */
        public static RequestBody create(final @Nullable MediaType contentType, final byte[] content) {
            return create(contentType, content, 0, content.length);
        }


        /**
         * 创建请求体
         */
        public static RequestBody create(final @Nullable MediaType contentType, final byte[] content,
                                         final int offset, final int byteCount) {
            if (content == null) throw new NullPointerException("content == null");
            Util.checkOffsetAndCount(content.length, offset, byteCount);
            return new RequestBody() {
                @Override public @Nullable MediaType contentType() {
                    return contentType;
                }

                @Override public long contentLength() {
                    return byteCount;
                }

                @Override public void writeTo(BufferedSink sink) throws IOException {
                    // 将请求体内容写出到sink
                    sink.write(content, offset, byteCount);
                }
            };
        }

        /**
         * 创建一个请求体,请求体类型为file,一般文件上传时使用
         */
        public static RequestBody create(final @Nullable MediaType contentType, final File file) {
            if (file == null) throw new NullPointerException("content == null");

            return new RequestBody() {
                @Override public @Nullable MediaType contentType() {
                    return contentType;
                }

                @Override public long contentLength() {
                    return file.length();
                }

                @Override public void writeTo(BufferedSink sink) throws IOException {
                    Source source = null;
                    try {
                        source = Okio.source(file);
                        sink.writeAll(source);
                    } finally {
                        Util.closeQuietly(source);
                    }
                }
            };
        }
    }

这里给出核心代码


public static class Builder {

    Headers.Builder headers;

    public Builder header(String name, String value) {
      headers.set(name, value);
      return this;
    }

    public Builder addHeader(String name, String value) {
      headers.add(name, value);
      return this;
    }
}


public final class Headers {

    final List<String> namesAndValues = new ArrayList<>(20);

    // 添加一组键值对
    public Builder add(String name, String value) {
      checkNameAndValue(name, value);
      return addLenient(name, value);
    }


    // 添加唯一键值对
    public Builder set(String name, String value) {
      checkNameAndValue(name, value);
      // 如果存在,移除
      removeAll(name);
      addLenient(name, value);
      return this;
    }

    // 添加到list集合中
    Builder addLenient(String name, String value) {
      namesAndValues.add(name);
      namesAndValues.add(value.trim());
      return this;
    }
}

创建Call对象

// 3、创建一个Call启动请求
Call call = okHttpClient.newCall(request);

在okHttpClient.newCall()方法方法中通过调用RealCall.newRealCall()方法获取一个Call(RealCall)实例。

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

发起网络请求

详见下节:Call接口及实现类RealCall

Call接口及实现类RealCall

Call

Call接口,主要实现网络请求调用。

  • 通过execute方法实现同步请求获取请求响应。
  • 通过enqueue方法实现异步请求通过CallBack回调获取请求响应。


Call接口源码

public interface Call extends Cloneable {

  // 返回原始请求对象
  Request request();

  // 同步请求
  Response execute() throws IOException;

  // 异步请求
  void enqueue(Callback responseCallback);

  // 取消请求
  void cancel();

  // 是否正在执行
  boolean isExecuted();

  // 是否取消
  boolean isCanceled();

  Call clone();

  interface Factory {
    Call newCall(Request request);
  }
}

RealCall

RealCall是Call接口的唯一实现类,因此okhttp的网络请求都是在RealCall类里面实现的。
所以这里将会对RealCall做详细的分析。

创建RealCall实例

在上文中说到,RealCall对象是在okHttpClient.newCall()方法中通过调用RealCall.newRealCall方法完成创建的。

这里看下RealCall.newRealCall()方法:

static RealCall newRealCall(OkHttpClient client, Request originalRequest, boolean forWebSocket) {
    // Safely publish the Call instance to the EventListener.
    RealCall call = new RealCall(client, originalRequest, forWebSocket);
    call.eventListener = client.eventListenerFactory().create(call);
    return call;
}

在该方法中,主要就是通过构造方法完成RealCall对象的创建,并且该RealCall对象持有了OkHttpClient和Requst实例。

RealCall对象创建以后,就可以开始正式的网络请求了,这里主要分为同步请求和异步请求。


// 执行一个同步请求,获取Response
try {
    Response response = call.execute();
} catch (IOException e) {
    e.printStackTrace();
}


// 执行一个异步请求,通过Callback获取Response
call.enqueue(new Callback() {
    @Override
    public void onFailure(Call call, IOException e) {}

    @Override
    public void onResponse(Call call, Response response) throws IOException {
        // 处理响应
        String string = response.body().string();
    }
});

这里对这两个请求方式一一分析。

execute同步请求

RealCall.execute()

  public Response execute() throws IOException {

    // 1、首先判断该Call是否执行
    synchronized (this) {
      if (executed) throw new IllegalStateException("Already Executed");
      executed = true;
    }
    captureCallStackTrace();
    eventListener.callStart(this);


    try {

      // 2、调用OkHttpClient中的dispatcher的executed方法
      client.dispatcher().executed(this);

      // 3、通过拦截器链获取http响应Response
      Response result = getResponseWithInterceptorChain();
      if (result == null) throw new IOException("Canceled");
      return result;
    } catch (IOException e) {
      eventListener.callFailed(this, e);
      throw e;
    } finally {

      // 4、结束请求,从同步请求队列中移除该Call
      client.dispatcher().finished(this);
    }
  }

在execute()方法中主要完成了以下功能:

  1. 首先,判断该请求是否被执行,一个Call智能执行一次
  2. 其次,调用OkHttpClient.dispatcher对象的executed方法将请求放入到同步请求队列中。
  3. 然后,通过okhttp中的拦截器链获取http响应
  4. 最后,通过调用OkHttpClient.dispatcher对象的finished()方法结束请求(这个将在下文中Dispatcher分发器中具体分析)

在同步请求中,最终通过RealCall.getResponseWithInterceptorChain()方法获取响应,通过这个方法的名字也能知道该方法的作用:通过拦截器链获取响应。

核心功能:Dispatcher分发器。
这个将在后续的章节中做具体的分析,这里只需要知道同步异步请求最终都是在Dispatcher请求分发器中完成的即可


核心功能:Interceptor拦截器。
这个将在后续的章节中做具体的分析,这里只需要知道获取Http响应是通过Intercepter拦截器中完成的即可。

enqueue异步请求

RealCall.enqueue(Callback)

  @Override 
  public void enqueue(Callback responseCallback) {

        // 1、首先判断该Call是否执行
        synchronized (this) {
          if (executed) throw new IllegalStateException("Already Executed");
          executed = true;
        }
        captureCallStackTrace();
        eventListener.callStart(this);

        // 创建一个AsyncCall对象,将对象放入到异步队列中
        client.dispatcher().enqueue(new AsyncCall(responseCallback));
  }

在enqueue()方法中主要完成了以下功能:

  1. 首先,判断该请求是否被执行,一个Call只能执行一次
  2. 然后,调用OkHttpClient.dispatcher对象的enqueue()方法将请求添加到异步请求队列中被执行。

在上面enqueue方法中,最后执行client.dispatcher().enqueue(new AsyncCall(responseCallback))时,创建了一个AsyncCall对象。

这里需要对这个AsyncCall做进一步分析。

AsyncCall

AsyncCall是RealCall的一个内部类,AsyncCall继承自NamedRunnable,而NamedRunnable实现了Runnable接口,所以AsyncCall主要作用就是实现Runnable接口。

NamedRunnable

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

NamedRunnable是一个抽象类,实现了Runnable接口,在该类中定义了一个抽象方法execute(),主要就是实现具体的异步操作,这里对该类就不做过多解释了。

接下来主要看下NamedRunnable的子类AsyncCall

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 {
          eventListener.callFailed(RealCall.this, e);
          responseCallback.onFailure(RealCall.this, e);
        }
      } finally {
        // 结束异步请求,让线程池继续处理其他等待请求
        client.dispatcher().finished(this);
      }
    }
  }

在AsyncCall的execute方法中主要作用就两个:

  • 通过RealCall.getResponseWithInterceptorChain()方法获取请求响应,这个和同步请求方法中调用的是同一个方法。

  • 在获取网络响应后,通过调动 client.dispatcher().finished(this)将该请求从异步请求队列中移除,表示该请求已经处理完,并让线程池执行其他异步等待队列中的请求。

看到这里想必你就清楚了,异步处理流程和上文中RealCall.execute()同步请求是完全一样的,唯一不同的就是一个是在主线程中完成的,一个是在子线程(AsyncCall)中完成的。

关于Dispatcher分发器和Interceptor拦截器将在下面章节中具体介绍。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值