OkHttp 开源库使用与源码解析

栏目: 软件资讯 · 发布时间: 5年前

内容简介:Android 开发只需添加依赖,如下:官方示例1:获取一个 url 上的内容并输出

Android 开发只需添加依赖,如下:

implementation("com.squareup.okhttp3:okhttp:3.13.1")

官方示例1:获取一个 url 上的内容并输出

//Http 客户端
OkHttpClient client = new OkHttpClient();

String run(String url) throws IOException {
    //构造请求
  Request request = new Request.Builder()
      .url(url)
      .build();
  //执行请求,获取数据
  try (Response response = client.newCall(request).execute()) {
    return response.body().string();
  }
}
复制代码

官方示例2:给服务器 post 数据

public static final MediaType JSON
    = MediaType.get("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();
  try (Response response = client.newCall(request).execute()) {
    return response.body().string();
  }
}
复制代码

简单分析一下这段代码:这个 post 请求操作看起来很简单。但是我们需要学习其中几个很重要的接口:

OKHttpClient:它代表着 http 客户端

Request:它封装了请求对象,可以构造一个 http 请求对象

Response:封装了响应结果

Call:client.newCall()调用后生成一个请求执行对象Call,它封装了请求执行过程。

下面我们结合这个例子来分析源码:

newCall().execute()

跟踪源码后发现这个方法是在 Call 中的接口,代码如下:

/**
 * A call is a request that has been prepared for execution. A call can be canceled. As this object
 * represents a single request/response pair (stream), it cannot be executed twice.
 */
public interface Call extends Cloneable {
    //省略部分代码
    
    //同步执行请求
    Response execute() throws IOException;
    
    //请求加入队列
    void enqueue(Callback responseCallback);
    
    //省略部分代码
}
复制代码

Call 的实现类是 RealCall,继续追踪源码的 RealCall.java 文件,可以看到 execute 方法:

@Override public Response execute() throws IOException {
    //同步锁检查该请求是否已经执行,如果没有则标记executed = ture,否则抛出异常
    synchronized (this) {
      if (executed) throw new IllegalStateException("Already Executed");
      executed = true;
    }
    
    captureCallStackTrace();
    timeout.enter();
    //调用了回调方法 callStart
    eventListener.callStart(this);
    try {
      //okhttp 客户端调用 dispatcher 将执行请求对象
      client.dispatcher().executed(this);
      //调用了 getResponseWithInterceptorChain 方法获取到响应数据 Response,后期还会继续分析
      Response result = getResponseWithInterceptorChain();
      if (result == null) throw new IOException("Canceled");
      return result;
    } catch (IOException e) {
      e = timeoutExit(e);
      //请求失败的回调 callFailed
      eventListener.callFailed(this, e);
      throw e;
    } finally {
      client.dispatcher().finished(this);
      //使用 dispather 对象调用 finished 方法,完成请求
    }
  }
复制代码

接下来我们详细分析一下 dispatcher.execute 和 getResponseWithInterceptorChain 这两个方法:

跟踪源码 Dispatcher

public final class Dispatcher {
    //省略部分代码
  /** Executes calls. Created lazily. */
  private @Nullable 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<>();

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

  void enqueue(AsyncCall call) {
    synchronized (this) {
      readyAsyncCalls.add(call);

      // Mutate the AsyncCall so that it shares the AtomicInteger of an existing running call to
      // the same host.
      if (!call.get().forWebSocket) {
        AsyncCall existingCall = findExistingCallWithHost(call.host());
        if (existingCall != null) call.reuseCallsPerHostFrom(existingCall);
      }
    }
    promoteAndExecute();
  }
  
  private <T> void finished(Deque<T> calls, T call) {
    Runnable idleCallback;
    synchronized (this) {
      if (!calls.remove(call)) throw new AssertionError("Call wasn't in-flight!");
      idleCallback = this.idleCallback;
    }
    boolean isRunning = promoteAndExecute();
    if (!isRunning && idleCallback != null) {
      idleCallback.run();
    }
  }

}
复制代码

发现 Dispatcher 是一个调度器,它的作用是对请求进行分发。它的内部有三个队列,分别是同步请求进行队列、异步请求等待队列、异步请求执行队列。

核心方法:getResponseWithInterceptorChain

Response getResponseWithInterceptorChain() throws IOException {
    // Build a full stack of interceptors.
    List<Interceptor> interceptors = new ArrayList<>();
    interceptors.addAll(client.interceptors());//添加自定义拦截器
    interceptors.add(retryAndFollowUpInterceptor);//重试重定向拦截器
    interceptors.add(new BridgeInterceptor(client.cookieJar()));
    interceptors.add(new CacheInterceptor(client.internalCache()));
    interceptors.add(new ConnectInterceptor(client));
    if (!forWebSocket) {
      interceptors.addAll(client.networkInterceptors());
    }
    interceptors.add(new CallServerInterceptor(forWebSocket));//调用服务器拦截

    Interceptor.Chain chain = new RealInterceptorChain(interceptors, null, null, null, 0,
        originalRequest, this, eventListener, client.connectTimeoutMillis(),
        client.readTimeoutMillis(), client.writeTimeoutMillis());
    //开始责任链模式
    return chain.proceed(originalRequest);
  }
复制代码

此方法构建数个拦截器之后,又构造了一个拦截器责任链。跟踪源码 RealInterceptorChain:

RealInterceptorChain

该类主要负责将所有的拦截器串连起来,使所有的拦截器以递归的方式进行实现,从而确保只有所有的拦截器都执行完之后才会返回 Response。以下对该类中的主要方法 proceed 进行讲解:

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");
    }
    //生成下一个拦截器调动该方法的 RealInterceptorChain 对象,其中 index+1 用于获取下一个拦截器
    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;
  }
复制代码

总结

以上就是我们对 OkHttp 核心源码的分析。当我们发起一个请求的时候会初始化一个 Call 的实例,然后根据同步和异步的不同,分别调用它的 execute() 和 enqueue() 方法。大致过程都是通过拦截器组成的责任链,依次经过重试、桥接、缓存、连接和访问服务器等过程,来获取到一个响应并交给用户。


以上所述就是小编给大家介绍的《OkHttp 开源库使用与源码解析》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

Fluent Python

Fluent Python

Luciano Ramalho / O'Reilly Media / 2015-8-20 / USD 39.99

Learn how to write idiomatic, effective Python code by leveraging its best features. Python's simplicity quickly lets you become productive with it, but this often means you aren’t using everything th......一起来看看 《Fluent Python》 这本书的介绍吧!

Base64 编码/解码
Base64 编码/解码

Base64 编码/解码

MD5 加密
MD5 加密

MD5 加密工具

html转js在线工具
html转js在线工具

html转js在线工具