Android技术知识Android开发技术人扯技术

拆轮子系列---OkHttp3源码分析

2018-08-08  本文已影响1人  a49f87ef5d4f

0.前言

okhttp是一个有square开发网络请求框架,square出品,必属精品。以前也看过这个框架的源码,但是时间久了,好多都记不清了,悔当初没有好好记录下来,今天有时间,又看了一遍,把思路分析记录下来。

1.基本使用

从最简单的Get请求分析,okhttp请求分为同步请求和异步请求:

同步请求:

 private final OkHttpClient client = new OkHttpClient();

  public void run() throws Exception {
    Request request = new Request.Builder()
        .url("https://publicobject.com/helloworld.txt")
        .build();

    try (Response response = client.newCall(request).execute()) {
      if (!response.isSuccessful()) throw new IOException("Unexpected code " + response);

      Headers responseHeaders = response.headers();
      for (int i = 0; i < responseHeaders.size(); i++) {
        System.out.println(responseHeaders.name(i) + ": " + responseHeaders.value(i));
      }

      System.out.println(response.body().string());
    }
  }

异步请求:

private final OkHttpClient client = new OkHttpClient();

  public void run() throws Exception {
    Request request = new Request.Builder()
        .url("http://publicobject.com/helloworld.txt")
        .build();

    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 {
        try (ResponseBody responseBody = response.body()) {
          if (!response.isSuccessful()) throw new IOException("Unexpected code " + response);

          Headers responseHeaders = response.headers();
          for (int i = 0, size = responseHeaders.size(); i < size; i++) {
            System.out.println(responseHeaders.name(i) + ": " + responseHeaders.value(i));
          }

          System.out.println(responseBody.string());
        }
      }
    });
  }

代码分析就从这最基本的开始。

2.基本流程

先看同步请求,首先创建了一个Request,Request采用了建造者模式,可以很好的避免构造函数参数的数量爆棚,Request代表了一个http请求,包含了请求方法,请求头,和请求体。

接着是调用了OkHttpClient的newCall(request)

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

这个方法返回一个RealCall,接着看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的创建,里面包含了okhttpclient还有request,同时为这个RealCall注册了一个事件监听,这个eventListenerFactory就是在构建okhttp的时候传入的

new OkHttpClient.Builder()
      .eventListenerFactory(PrintingEventListener.FACTORY)
      .build();

网络请求的结果被封装在Response里,通过调用RealCall的execute方法,得到最终结果,所以可想而知,真正的网络请求也是在这一个过程进行的。接着就看一下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 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);
    }
  }

这个方法先判断是否已经执行该Call了,如果executed=true,那么就抛出异常,如果为false,就调用client.dispatcher().executed(this);Dispatcher根据注释来看,是用来决定何时执行异步策略的,看一下它的executed方法

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

runningSyncCalls是一个双向列表,这个方法就是单纯的将该RealCall添加进列表,所以网络请求不是在这里发起的,那么回到RealCall的execute方法,在dispatcher的executed方法调用后,接着调用了
getResponseWithInterceptorChain();该方法返回了Response,所以可以看出真正的网络请求是在这里做的。看一下这个方法:

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

这个方法的前办部分构建了一个拦截器列表,包括我们在构建okhttpclient时添的interceptor和networkInterceptor,接着又生成了一个RealInterceptorChain,调用它的proceed方法返回最终的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");
    }

    // Call the next interceptor in the chain.
    RealInterceptorChain next = new RealInterceptorChain(interceptors, streamAllocation, httpCodec,
        connection, index + 1, request, call, eventListener, connectTimeout, readTimeout,
        writeTimeout);
    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");
    }

    if (response.body() == null) {
      throw new IllegalStateException(
          "interceptor " + interceptor + " returned a response with no body");
    }

    return response;
  }

注重看这两行

 RealInterceptorChain next = new RealInterceptorChain(interceptors, streamAllocation, httpCodec,
        connection, index + 1, request, call, eventListener, connectTimeout, readTimeout,
        writeTimeout);
    Interceptor interceptor = interceptors.get(index);
    Response response = interceptor.intercept(next);

可以看到每次调用proceed方法,都会生成一个新的RealInterceptorChain,其他参数都一样,只是index增加了1,接着就是根据index在拦截器列表找到一个拦截器,然后调用拦截器的intercept方法,并将刚才生成的RealInterceptorChain传入,所以现在大家应该清楚了吧,在自定义拦截器时复写的intercept方法传入Chain,其实是RealInterceptorChain,在复写拦截器的时候,经常会写chain.processed(request),之后就会重复之前的过程,如果你不调用chain.processed(request)方法,你会发现没有网络请求发出(没有http缓存的情况下),这是因为整个网络请求过程采用的是责任链模式,不调用这个方法,就相当于断掉了这个链条的传递,因为最终的请求的发出和Response的构建是在CallServerInterceptor里完成的,是链条的最后一环,这里注意的是resposne是由下向上返回的,类似于递归。得到response后,我们再回到Realcall的execute方法,看最后有一个finally

 client.dispatcher().finished(this);

调用dispatcher的finished方法

    private <T> void finished(Deque<T> calls, T call, boolean promoteCalls) {
    int runningCallsCount;
    Runnable idleCallback;
    synchronized (this) {
      if (!calls.remove(call)) throw new AssertionError("Call wasn't in-flight!");
      if (promoteCalls) promoteCalls();
      runningCallsCount = runningCallsCount();
      idleCallback = this.idleCallback;
    }

    if (runningCallsCount == 0 && idleCallback != null) {
      idleCallback.run();
    }
  }

将call从dispatcher的列表中移除。并重置RunningCount的数量,当runningCallsCount的数量为0时,会回调idleCallback。
接下来看一下异步的get请求, client.newCall(request).enqueue,同样是生成了一个RealCall,只不过这次调用的是enqueue方法,

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

给方法还是调用了Dispatcher的enqueue方法,,但是你发现这里生成了一个新对象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);
      }
    }
  }

它有一个父类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接口,在run方法里调用了execute这个抽象方法。
我们在回到AsyncCall,看他的execute方法

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

getResponseWithInterceptorChain(),熟悉吧,还是原来的配方,还是原来的味道。再看Dispatcher的enqueu方法

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

很显然,现将这个call添加到一个双向列表,然后启用了一个线程池,调用了这个AsyncCall。最后也是调用dispatcher的finish方法。

盗张图

image

3.拦截器

从刚才分析的流程来看,拦截器是重点,okhttp允许我们自定义拦截器,也有一些默认的拦截器,下面分析一下这些拦截器的作用:

RetroyAndFollowUpInterceptor:处理重定向和失败重连

BridgeInterceptor:请求前添加一些http header,保存cookie,处理zip

CacheInterceptor:处理http响应缓存

ConnectInterceptor:建立与服务器的链接

CallServerInterceptor:发送request并解析response

这里只是简单说一下其作用,有时间单拿出一篇文章详细分析。

image

关注我的公众号

上一篇下一篇

猜你喜欢

热点阅读