Android网络请求框架OkHttp 同步异步源码分析

本文使用的OkHttp版本 ↓

implementation 'com.squareup.okhttp3:okhttp:3.10.0'

关于OkHttp,大家应该都不陌生了,这里就不多介绍,从我们最常见的使用场景开始

1,同步

                //第一种方式
                OkHttpClient okHttpClient = new OkHttpClient();
                //第二种方式
                //OkHttpClient okHttpClient = new OkHttpClient.Builder().build();
                Request request = new Request.Builder()
                        .url("https://www.baidu.com/")
                        .get()
                        .build();
                try {
                    Response response = okHttpClient.newCall(request).execute();
                    Log.d(TAG, "response: " + response.body().string());
                } catch (IOException e) {
                    e.printStackTrace();
                }

获取OkHttp实例

首先我们要获取OkHttpClient的实例,这里有两种
一种是直接通过无参构造方法获取,一种是通过构建者模式获取
让我先来看看无参构造

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

  OkHttpClient(Builder builder) {
    this.dispatcher = builder.dispatcher;
    this.proxy = builder.proxy;
    this.protocols = builder.protocols;
    this.connectionSpecs = builder.connectionSpecs;
    this.interceptors = Util.immutableList(builder.interceptors);
    this.networkInterceptors = Util.immutableList(builder.networkInterceptors);
    this.eventListenerFactory = builder.eventListenerFactory;
    this.proxySelector = builder.proxySelector;
    this.cookieJar = builder.cookieJar;
    this.cache = builder.cache;
    this.internalCache = builder.internalCache;
    this.socketFactory = builder.socketFactory;

    ···

  }

内部调用了Builder为入参的构造方法,传入了一个Builder实例,这个Builder是OkHttpClient的内部类,
Builder在无参构造方法中,获取了OkHttpClient需要的一些实例和默认配置,最后依次赋给了OkHttpClient,
如果需要进行额外的配置,我们可以直接操作这个OkHttpClient

  public static final class Builder {

    public Builder() {
      dispatcher = new Dispatcher();
      protocols = DEFAULT_PROTOCOLS;
      connectionSpecs = DEFAULT_CONNECTION_SPECS;
      eventListenerFactory = EventListener.factory(EventListener.NONE);
      proxySelector = ProxySelector.getDefault();
      cookieJar = CookieJar.NO_COOKIES;
      socketFactory = SocketFactory.getDefault();
      hostnameVerifier = OkHostnameVerifier.INSTANCE;
      certificatePinner = CertificatePinner.DEFAULT;
      proxyAuthenticator = Authenticator.NONE;
      authenticator = Authenticator.NONE;
      connectionPool = new ConnectionPool();

      ···

    }

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

如果我们要采用构建者来初始化OkHttp,我们需要先获取Builder的实例,调用Builder提供的方法,进行参数配置,最后调用Builder的build方法,将Builder传入OkHttpClient的无参构造,返回OkHttpClient

构建者模式可以通过链式调用构建对象,在构建复杂对象的时候可以减少代码量,并对其进行约束
在OkHttp3.0+版中很多地方都用到了这种设计模式(Builder),例如接下来的Request

构建Request对象

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

也是通过构建者模式获取,内部实现,这里我们就不看了

发送请求

Response response = okHttpClient.newCall(request).execute();

这里我们直接调用了OkHttpClient的newCall方法,传入了上面生产的Request对象

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

内部实际上调用了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是Call接口的实现类)
并对其注册了事件监听,这个监听将会在Call的各个周期中调用

执行

在newCall方法后,我们拿到了RealCall对象,如果我们希望这个请求是同步的,我们可以调用execute方法
我们来看看execute方法在RealCall类中的实现

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

这里我们重点看

      Response result = getResponseWithInterceptorChain();

getResponseWithInterceptorChain()直接返回了一个Response
我们都知道,OkHttp从3.0版本起,请求和响应都是用拦截器来实现

拦截器责任链

  Response getResponseWithInterceptorChain() throws IOException {
    // Build a full stack of interceptors.
    List 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);
  }

我们大概梳理一遍流程
首先把所有的拦截器都放入了一个List,
依次是:
用户设置的拦截器、
retryAndFollowUpInterceptor(重试重定向)、
BridgeInterceptor(将用户请求转化为真实的网络请求,网络响应转化为用户响应)、
CacheInterceptor(为 从缓存中请求、将响应写入缓存 服务)、
ConnectInterceptor(和目标服务器建立链接)、
networkInterceptors(如果不是WebSocket的话会添加,用户设置的网络拦截器)、
CallServerInterceptor(链中最后一个拦截器,向目标服务器发送请求)

用这些组成了RealInterCeptroChain,
最后调用了chain的proceed方法

这里我画了张图帮助大家理解


Android网络请求框架OkHttp 同步异步源码分析_第1张图片
OkHttp拦截器责任链流程图

至此,我们就拿到了返回的Response,直接通过return返回

2,异步

要进行异步请求,需要传入回调,我们找到Call接口中除了execute还有一个enqueue方法,入参就是一个Callback

        OkHttpClient okHttpClient = new OkHttpClient.Builder().build();
        Request request = new Request.Builder()
                .url("https://www.baidu.com/")
                .get()
                .build();
        okHttpClient.newCall(request).enqueue(new Callback() {
            @Override
            public void onFailure(Call call, IOException e) {
                Log.d(TAG, "onFailure: " + e);
            }

            @Override
            public void onResponse(Call call, Response response) throws IOException {
                Log.d(TAG, "onResponse: " + response.body().string());
            }
        });

让我们来看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));
  }

做了一些校验,然后用AsyncCall接收了回调(AsyncCall继承自NamedRunnable,NamedRunnable继承自Runnable)
这里我们主要看dispatcher的enqueue方法

    client.dispatcher().enqueue(new AsyncCall(responseCallback));
  synchronized void enqueue(AsyncCall call) {
//获取当前运行的异步请求的大小,如果小于最大请求的数量(默认64), 而且运行在当前主机的连接数小于每个主机最大的请求数(默认5)
    if (runningAsyncCalls.size() < maxRequests && runningCallsForHost(call) < maxRequestsPerHost) {
      runningAsyncCalls.add(call);
      executorService().execute(call);
    } else {
      readyAsyncCalls.add(call);
    }
  }

如果满足if的条件,就会加入运行中的异步队列,并立刻加入线程池中去执行
如果不满足,就会放入准备执行的队列

既然是放入线程池中运行,最后肯定是调用Runnable的run方法,这里我们传入的是AsyncCall,它的run方法是在父类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();
}

这里很简单,只是调用了一下execute方法

@Override protected void execute() {
      boolean signalledCallback = false;
      try {
//与同步方法一致
        Response response = getResponseWithInterceptorChain();
//如果OkHttp初始化时设置了重试,而且这个Call被取消了
        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和同步方法中流程一样,这里不重复说了
如果交易成功返回,则会调用

responseCallback.onResponse(RealCall.this, response);

那么之前那些加入准备异步队列的请求什么时候调用呢?

在AsyncCall的execute方法的最后调用了dispatcher的finished方法

client.dispatcher().finished(this);
  /** Used by {@code AsyncCall#run} to signal completion. */
  void finished(AsyncCall call) {
    finished(runningAsyncCalls, call, true);
  }

  private  void finished(Deque 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是AsyncCall,在调用finished方法时,promoteCalls传入的值为true
当有一个AsyncCall完成的时候,会推进下一个请求执行
重点看promoteCalls方法

  private void promoteCalls() {
    if (runningAsyncCalls.size() >= maxRequests) return; //  大于上限,返回
    if (readyAsyncCalls.isEmpty()) return; // 预备队列中已经空了,返回

    for (Iterator i = readyAsyncCalls.iterator(); i.hasNext(); ) {
      AsyncCall call = i.next();
      //如果每个主机运行的Calls小于上限
      if (runningCallsForHost(call) < maxRequestsPerHost) {
        i.remove();
        runningAsyncCalls.add(call);
        //执行
        executorService().execute(call);
      }

      if (runningAsyncCalls.size() >= maxRequests) return; // Reached max capacity.
    }
  }

如果满足条件,会去遍历预备队列中的请求,依次加入线程池去执行,接下来的流程和上面一致了

3.总结

最后用一张图把同步和异步流程结合起来

Android网络请求框架OkHttp 同步异步源码分析_第2张图片
OkHttp同步异步请求源码分析

你可能感兴趣的:(Android网络请求框架OkHttp 同步异步源码分析)