Android开源框架之OkHttp

OkHttp相信搞android的都不陌生,它是目前应用最多的网络请求开源框架,虽然现在Retrofit更加流行,但到底层其实也是基于OkHttp的。你要是说你没用过OkHttp都不好意思说自己是做过Android开发。那么今天就来聊聊OkHttp。
本文的要点如下:

  • 概述
  • OkHttp的使用
  • 源码分析
    • 同步请求
    • 异步请求
  • 总结

概述

OkHttp是一个网络请求开源库,即将网络请求的相关功能封装好的类库。要知道,没有网络请求框架之前,App想与服务器进行网络请求交互是一件很痛苦的事,因为Android的主线程不能进行耗时操作,那么就需另开1个线程请求、考虑到线程池,缓存等一堆问题。
于是乎,网络请求库出现了,网络请求库的本质其实是封装了 网络请求 + 异步 + 数据处理功能的库

OkHttp的使用

同步请求:

GET请求:

    OkHttpClient client = new OkHttpClient();
    Request request = new Request.Builder()
            .url(url)
            .build();
    Response response = client.newCall(request).execute();
    return response.body().string();

POST请求:

    OkHttpClient client = new OkHttpClient();
    RequestBody requestBody = new FormBody.Builder()
              .add("username","abc")
              .add("password","123456")
              .build();
    Request request = new Request.Builder()
              .url("http://www.baidu.com")
              .post(requestBody)
              .build();
    Response response = client.newCall(request).execute();
    return response.body().string();

异步请求(以GET为例):

  OkHttpClient client = new OkHttpClient();
  Request request = new Request.Builder()
            .url(url)
            .build();
  client.newCall(request).enqueue(new Callback() {
    @Override
    public void onFailure(Call call, IOException e) {
    }
    @Override
    public void onResponse(Call call, Response response) throws IOException {
        System.out.println(response.body().string());
    }
});

使用这一块没什么好讲的,不同类型的请求方式有些许的不同,不过都比较好理解。下面我们还是来看看源码中究竟是怎么做的吧。

源码分析

ps:我用的源码是OkHttp3.14.1。
首先,使用OkHttp时,必然要先创建OkHttpClient对象。

 OkHttpClient client = new OkHttpClient();

一行代码就搞定了,似乎有点过于简单了,我们来看看OkHttpClient()里面做了什么:

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

原来是方便我们使用,提供了一个默认的配置,直接传入了一个默认的Builder类型的对象。Builder在初始化时就会设置这一些参数,全都是默认值。这里就是运用了Builder模式,简化了构建过程。

public Builder() {
      dispatcher = new Dispatcher();//调度器
      protocols = DEFAULT_PROTOCOLS;
      connectionSpecs = DEFAULT_CONNECTION_SPECS;
      eventListenerFactory = EventListener.factory(EventListener.NONE);
      proxySelector = ProxySelector.getDefault();
      if (proxySelector == null) {
        proxySelector = new NullProxySelector();
      }
      cookieJar = CookieJar.NO_COOKIES;
      socketFactory = SocketFactory.getDefault();
      hostnameVerifier = OkHostnameVerifier.INSTANCE;
      certificatePinner = CertificatePinner.DEFAULT;
      proxyAuthenticator = Authenticator.NONE;
      authenticator = Authenticator.NONE;
      connectionPool = new ConnectionPool();
      dns = Dns.SYSTEM;
      followSslRedirects = true;
      followRedirects = true;
      retryOnConnectionFailure = true;
      callTimeout = 0;
      connectTimeout = 10_000;
      readTimeout = 10_000;
      writeTimeout = 10_000;
      pingInterval = 0;
    }

同步请求

尽管现在项目中很少用同步请求了,但是其实异步请求的基础还是同步请求,只不过中间转换了线程而已。
在同步请求,初始化之后,我们又用Builder构建了Request对象,然后执行了OKHttpClient的newCall方法,那么咱们就看看这个newCall里面都做什么操作?

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

可以看出client.newCall(request).execute();实际上执行的是RealCall的execute方法,现在咱们再回来看下RealCall的execute的具体实现。

@Override 
public Response execute() throws IOException {
    synchronized (this) {
      //同步判断,保证每个call只用一次,重复使用会抛出异常
      if (executed) throw new IllegalStateException("Already Executed");
      executed = true;
    }
    transmitter.timeoutEnter();
    transmitter.callStart();
    try {
      client.dispatcher().executed(this);
      return getResponseWithInterceptorChain();
    } finally {
      client.dispatcher().finished(this);
    }
  }

这里主要做了 4 件事:

  1. 检查这个 call 是否已经被执行了,每个 call 只能被执行一次,如果想要一个完全一样的 call,可以利用call#clone方法进行克隆。
  2. 利用client.dispatcher().executed(this)来进行实际执行,dispatcher是刚才看到的OkHttpClient.Builder的成员之一。
  3. 调用getResponseWithInterceptorChain()函数获取 HTTP 返回结果,从函数名也可以看出,这一步还会进行一系列“拦截”操作。
  4. 最后还要通知dispatcher自己已经执行完毕,释放dispatcher。

那么我们看下dispatcher里面的execute()是如何处理的。

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

可以看到,其实也很简单,runningSyncCalls执行了add方法,添加的参数是RealCall。runningSyncCalls是什么呢?

/** Ready async calls in the order they'll be run. */
  private final Deque readyAsyncCalls = new ArrayDeque<>();

  /** Running asynchronous calls. Includes canceled calls that haven't finished yet. */
  private final Deque runningAsyncCalls = new ArrayDeque<>();

  /** Running synchronous calls. Includes canceled calls that haven't finished yet. */
  private final Deque runningSyncCalls = new ArrayDeque<>();

可以看到runningSyncCalls是双向队列。另外,我们发现Dispatcher里面定义了三个双向队列,看下注释,我们大概能明白readyAsyncCalls是一个存放了等待执行任务Call的双向队列,runningAsyncCalls是一个存放异步请求任务Call的双向任务队列,runningSyncCalls是一个存放同步请求的双向队列
那么这一步的目的就是将RealCall放入同步队列中

回到之前,执行完client.dispatcher().executed()方法,要执行getResponseWithInterceptorChain()方法,这就是OkHttp的核心拦截器的工作了:

Response getResponseWithInterceptorChain() throws IOException {
    // Build a full stack of interceptors.
    List interceptors = new ArrayList<>();
    //添加开发者应用层自定义的Interceptor
    interceptors.addAll(client.interceptors());
    //这个Interceptor是处理请求失败的重试,重定向    
    interceptors.add(new RetryAndFollowUpInterceptor(client));
    //这个Interceptor工作是添加一些请求的头部或其他信息
    //并对返回的Response做一些友好的处理(有一些信息你可能并不需要)
    interceptors.add(new BridgeInterceptor(client.cookieJar()));
    //这个Interceptor的职责是判断缓存是否存在,读取缓存,更新缓存等等
    interceptors.add(new CacheInterceptor(client.internalCache()));
    //这个Interceptor的职责是建立客户端和服务器的连接
    interceptors.add(new ConnectInterceptor(client));
    if (!forWebSocket) {
      //添加开发者自定义的网络层拦截器
      interceptors.addAll(client.networkInterceptors());
    }
    interceptors.add(new CallServerInterceptor(forWebSocket));

    Interceptor.Chain chain = new RealInterceptorChain(interceptors, transmitter, null, 0,
        originalRequest, this, client.connectTimeoutMillis(),
        client.readTimeoutMillis(), client.writeTimeoutMillis());

    boolean calledNoMoreExchanges = false;
    try {
      //把chain传递到第一个Interceptor手中
      Response response = chain.proceed(originalRequest);
      if (transmitter.isCanceled()) {
        closeQuietly(response);
        throw new IOException("Canceled");
      }
      return response;
    } catch (IOException e) {
      calledNoMoreExchanges = true;
      throw transmitter.noMoreExchanges(e);
    } finally {
      if (!calledNoMoreExchanges) {
        transmitter.noMoreExchanges(null);
      }
    }
  }

可以看到,主要的操作就是new了一个ArrayList,然后就是不断的add拦截器Interceptor,之后new了一个RealInterceptorChain对象,最后调用了chain.proceed()方法。

我们来看看RealInterceptorChain()构造方法。

public RealInterceptorChain(List interceptors, StreamAllocation streamAllocation,
      HttpCodec httpCodec, Connection connection, int index, Request request) {
    this.interceptors = interceptors;//将拦截链保存
    this.connection = connection;
    this.streamAllocation = streamAllocation;
    this.httpCodec = httpCodec;
    this.index = index;
    this.request = request;
  }

就是一些赋值操作,将信息保存,关键的是this.interceptors = interceptors这里就保存了拦截链。

之后我们来看一下chain.proceed()方法获取返回的信息。由于Interceptor是个接口,所以应该是具体实现类RealInterceptorChain的proceed实现。

public Response proceed(Request request, StreamAllocation streamAllocation, HttpCodec httpCodec,
      Connection connection) throws IOException {
    //省略其他代码   
    calls++;
    RealInterceptorChain next = new RealInterceptorChain(
    interceptors, streamAllocation, httpCodec, connection, index + 1, request);
    Interceptor interceptor = interceptors.get(index);
    Response response = interceptor.intercept(next);
    //省略其他代码   
    return response;
}

然后看到在proceed方面里面又new了一个RealInterceptorChain类的next对象,这个next对象和chain最大的区别就是index属性值不同chain是0,而next是1,然后取interceptors下标为1的对象的interceptor。由从上文可知,如果没有开发者自定义的应用层Interceptor时,首先调用的RetryAndFollowUpInterceptor,如果有开发者自己定义的应用层interceptor则调用开发者interceptor。

后面的流程都差不多,在每一个interceptor的intercept方法里面都会调用chain.proceed()从而调用下一个interceptor的intercept(next)方法,这样就可以实现遍历getResponseWithInterceptorChain里面interceptors的item,实现遍历循环。

之前我们看过getResponseWithInterceptorChain里面interceptors的最后一个item是CallServerInterceptor,最后一个Interceptor(即CallServerInterceptor)里面是直接返回了response 而不是进行继续递归。

CallServerInterceptor返回response后返回给上一个interceptor,一般是开发者自己定义的networkInterceptor,然后开发者自己的networkInterceptor把他的response返回给前一个interceptor,依次以此类推返回给第一个interceptor,这时候又回到了realCall里面的execute()里面了。

最后把response返回给get请求的返回值。至此同步GET请求的大体流程就已经结束了。

异步请求

讲了这么多同步请求,其实异步请求才是重头戏,毕竟现在的项目中大多用的都是异步请求。
由于前面的步骤和同步一样new了一个OKHttp和Request。这块和同步一样就不说了,那么说说和同步不一样的地方,后面异步进入的是newCall()的enqueue()方法
之前分析过,newCall()里面是生成了一个RealCall对象,那么执行的其实是RealCall的enqueue()方法。我们来看源码:

 @Override public void enqueue(Callback responseCallback) {
    synchronized (this) {
      //同步判断,保证每个call只用一次,重复使用会抛出异常
      if (executed) throw new IllegalStateException("Already Executed");
      executed = true;
    }
    transmitter.callStart();
    client.dispatcher().enqueue(new AsyncCall(responseCallback));
  }

可以看到和同步请求的enqueue方法一样,还是先同步判断是否被请求过了,不一样的地方就在于调用了client.dispatcher().enqueue(new AsyncCall(responseCallback))方法。即实际调用的是Dispatcher的enqueue()方法:

void enqueue(AsyncCall call) {
    synchronized (this) {
      readyAsyncCalls.add(call);//将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();
  }

这里主要做了两件事:

  1. 将call添加到了异步请求队列;
  2. 调用promoteAndExecute方法。
private boolean promoteAndExecute() {
    assert (!Thread.holdsLock(this));

    List executableCalls = new ArrayList<>();
    boolean isRunning;
    synchronized (this) {
      for (Iterator i = readyAsyncCalls.iterator(); i.hasNext(); ) {
        AsyncCall asyncCall = i.next();

        if (runningAsyncCalls.size() >= maxRequests) break; // Max capacity.
        if (asyncCall.callsPerHost().get() >= maxRequestsPerHost) continue; // Host max capacity.

        i.remove();
        asyncCall.callsPerHost().incrementAndGet();
        executableCalls.add(asyncCall);//将异步请求添加到executableCalls中
        runningAsyncCalls.add(asyncCall);
      }
      isRunning = runningCallsCount() > 0;
    }

    for (int i = 0, size = executableCalls.size(); i < size; i++) {
      AsyncCall asyncCall = executableCalls.get(i);
      asyncCall.executeOn(executorService());
    }

    return isRunning;
  }

可以看出先是迭代了上面的队列,取出队列里的AsyncCall后添加到了executableCalls集合中。然后遍历这个集合,开始执行每个AsyncCall的executeOn方法。参数是executorService(),我们来具体看看:

public synchronized ExecutorService executorService() {
    if (executorService == null) {
      executorService = new ThreadPoolExecutor(0, Integer.MAX_VALUE, 60, TimeUnit.SECONDS,
          new SynchronousQueue<>(), Util.threadFactory("OkHttp Dispatcher", false));
    }
    return executorService;
  }

不难看出,这个方法传递进去的是ExecutorService线程池。那看来关键就在executeOn方法中了:

void executeOn(ExecutorService executorService) {
      assert (!Thread.holdsLock(client.dispatcher()));
      boolean success = false;
      try {
        executorService.execute(this);//用executorService线程池执行了当前的线程
        success = true;
      } catch (RejectedExecutionException e) {
        InterruptedIOException ioException = new InterruptedIOException("executor rejected");
        ioException.initCause(e);
        transmitter.noMoreExchanges(ioException);
        responseCallback.onFailure(RealCall.this, ioException);
      } finally {
        if (!success) {
          client.dispatcher().finished(this); // This call is no longer running!
        }
      }
    }

通过executorService线程池执行了当前的线程,也就是AsyncCall,那么AsyncCall由于继承了NamedRunnable,这个NamedRunnable的run方法里又执行了抽象方法execute,所以,实际上这里执行了AsyncCall的execute方法。

@Override protected void execute() {
      boolean signalledCallback = false;
      transmitter.timeoutEnter();
      try {
        Response response = getResponseWithInterceptorChain();
        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 {
          responseCallback.onFailure(RealCall.this, e);//失败
        }
      } finally {
        client.dispatcher().finished(this);
      }
    }

终于,我们看到了几个熟悉的名字,getResponseWithInterceptorChain、onResponse和onFailure。不难看出,这里依旧是用getResponseWithInterceptorChain()通过拦截链进行请求,最终执行结果回调给了我们传递进去的Callback。至此,异步请求的主要流程也分析完了。

对比一下同步请求和异步请求,不难看出,其实异步请求就是维护了一个线程池用于进行请求,在请求完成之后回调我们一开始传入的CallBack接口。

总结

  1. OkHttpClient实现了Call.Factory,负责为Request创建Call;
  2. RealCall为具体的Call实现,execute()为同步接口,通过getResponseWithInterceptorChain()函数实现;enqueue()为异步接口通过Dispatcher利用ExecutorService线程池实现,而最终进行网络请求时和同步接口一致,都是通过getResponseWithInterceptorChain()函数实现;
  3. getResponseWithInterceptorChain()中利用拦截链机制,分层实现缓存、透明压缩、网络 IO 等功能。

你可能感兴趣的:(Android开源框架之OkHttp)