okhttp的使用和及简单源码分析

okhttp是目前非常常用的网络请求框架,我们在使用它的同时也要看他是怎么实现的,这篇文章我们简单分析以下它的请求流程。
参考并感谢:https://blog.csdn.net/json_it/article/details/78404010
https://blog.csdn.net/dingding_android/article/details/51942000
https://blog.csdn.net/mwq384807683/article/details/71173442?locationNum=8&fps=1
1、使用(同步请求)

        OkHttpClient client = new OkHttpClient();
        Request request = new Request.Builder().url("http://www.baidu.com")
              .build();
        Response response = client.newCall(request).execute();

首先我们new了一个OkHttpClient对象,这个类包含了我们网络请求需要的协议,http版本协议,连接池,调度器等。之后利用Builder模式创建了请求对象Request,默认的请求方式是get。最后通过newCall(request)创建了Call对象并执行了该对象的excute方法。具体分析来看源码:

public class OkHttpClient implements Cloneable, Call.Factory, WebSocket.Factory {
   @Override 
   public Call newCall(Request request) {
    return new RealCall(this, request, false /* for web socket */);
   }

我们newCall方法实际是新建了一个RealCall对象,那么RealCall对象又是什么呢,RealCall实际上是实现了Call接口

public interface Call {
  Request request();
  Response execute() throws IOException;//同步请求,会在当前线程执行请求并阻塞当前线程
  void enqueue(Callback responseCallback);//异步请求,后面会分析
  void cancel();
  boolean isCanceled();
  interface Factory {
    // 根据一个Http请求生成一个OKHttp请求。
    Call newCall(Request request);
  }
}

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

从代码中可以看到,首先判断这个请求是否是第一次执行,如果是,则继续向下执行,否则会抛出一个异常,也就是说一个请求只能执行一次。
之后,调用client.dispatcher().executed(this),即调用调度器的excuted方法,将当前请求加入请求队列中。内部具体的实现我们在后面异步请求中分析。
之后调用getResponseWithInterceptorChain()得到响应Response,并返回。同步请求分析到这里就结束了,重头戏在后面——异步请求。

2、异步请求


client.newCall(request).enqueue(new Callback() {
         @Override
                public void onFailure(Call call, IOException e) {

                  }
        @Override
           public void onResponse(Call call, Response response) throws IOException {

                   }
});

这里我们使用RealCall的enqueue方法并传入一个CallBack对象来进行回调
我们来看下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));
}

同样的,异步请求首先也要判断该请求是否是第一次执行。
之后调用 client.dispatcher().enqueue(new AsyncCall(responseCallback)),我们的callback对象被当作参数封装进了AsynCall对象,我们来看下AsynCall对象的源码

final class AsyncCall extends NamedRunnable {
    private final Callback responseCallback;
    private final boolean forWebSocket;

    private AsyncCall(Callback responseCallback, boolean forWebSocket) {
      super("OkHttp %s", originalRequest.url().toString());
      this.responseCallback = responseCallback;
      this.forWebSocket = forWebSocket;
    }

    String host() {
      return originalRequest.url().host();
    }

    Request request() {
      return originalRequest;
    }

    Object tag() {
      return originalRequest.tag();
    }

    void cancel() {
      RealCall.this.cancel();
    }

    RealCall get() {
      return RealCall.this;
    }

    /**
      过一遍拦截器链,并执行请求,然后调用回调函数。
    */
    @Override protected void execute() {
      // 保证onFailure最多只会被调用一次
      boolean signalledCallback = false;
      try {
        // 进入连接器链,并执行请求
        Response response = getResponseWithInterceptorChain(forWebSocket);
        // 如果请求被取消,调用onFailure
        if (canceled) {
          signalledCallback = true;
          responseCallback.onFailure(RealCall.this, new IOException("Canceled"));
        } else {
          // 正常情况,调用onResponse
          signalledCallback = true;
          responseCallback.onResponse(RealCall.this, response);
        }
      } catch (IOException e) {
        // 如果上面有调用过回调,就不调了,这里保证onFailure只会被调用一次
        if (signalledCallback) {
          logger.log(Level.INFO, "Callback failure for " + toLoggableString(), e);
        } else {
          responseCallback.onFailure(RealCall.this, e);
        }
      } finally {
        // 运行队列移除请求
        client.dispatcher().finished(this);
      }
    }
  }

可以看到,AsynCall实际上本身是个任务,执行时会调用execute方法,在该方法中我们先调用Response response = getResponseWithInterceptorChain(forWebSocket),通过责任链来获取response,并执行对应的回调函数。那么getResponseWithInterceptorChain()这个方法,就是发送请求和获取响应的主要核心了,我们看看具体是怎么实现的:


Response getResponseWithInterceptorChain() throws IOException {
    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);
}

这里面我们将自定义的拦截器和okhttp自身的拦截器加入至list中,并封装成一个责任链对象Interceptor.Chain,最后调用chain.proceed(request)来执行我们的请求并获得响应。那我们再来看看chain.proceed里干了些什么

public Response proceed(Request request, StreamAllocation streamAllocation, HttpCodec httpCodec,
      RealConnection connection) throws IOException {
    if (index >= interceptors.size()) throw new AssertionError();
 
    ......
 
    // 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);
 
    ......
 
    return response;
}

可以看到,proceed方法中判断index(此时为0)是否大于或者等于client.interceptors(List )的大小。由于httpStream为null,所以首先创建next拦截器链,主需要把索引置为index+1即可;然后获取第一个拦截器,调用其intercept方法。在intercept方法中我们再一次调用了chain.proceed的方法去调用下一个拦截器的intercept方法。这样我们的request请求层层向下传递,而最后一个拦截器处理完毕时,返回的response又层层向上返回,最后得到的就是最终经过加工后的响应。

那么这些拦截器有那些呢?
1)在配置 OkHttpClient 时设置的 interceptors;
2)负责失败重试以及重定向的 RetryAndFollowUpInterceptor;
3)负责把用户构造的请求转换为发送到服务器的请求、把服务器返回的响应转换为用户友好的响应的 BridgeInterceptor;
4)负责读取缓存直接返回、更新缓存的 CacheInterceptor;
5)负责和服务器建立连接的 ConnectInterceptor;
6)配置 OkHttpClient 时设置的 networkInterceptors;
7)负责向服务器发送请求数据、从服务器读取响应数据的 CallServerInterceptor。
请求流程分析到这里,我们知道我们的请求都是通过调度器将请求加入请求队列中,所以我们最后再来看一下dispatch是如何工作的。

public final class Dispatcher {
  /** 最大并发请求数为64 */
  private int maxRequests = 64;
  /** 每个主机最大请求数为5 */
  private int maxRequestsPerHost = 5;

  /** 线程池 */
  private ExecutorService executorService;

  /** 准备执行的请求 */
  private final Deque readyAsyncCalls = new ArrayDeque<>();

  /** 正在执行的异步请求,包含已经取消但未执行完的请求 */
  private final Deque runningAsyncCalls = new ArrayDeque<>();

  /** 正在执行的同步请求,包含已经取消单未执行完的请求 */
  private final Deque runningSyncCalls = new ArrayDeque<>();

再调度器中,创建了一个线程池

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

该线程池没有核心线程,随时创建更多线程,阻塞队列只保留一个任务,当该任务被执行,下一个任务才能进入队列。线程的存活时间为60秒,60秒还没有任务需要执行则线程销毁。
之后我们来看enqueue方法

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

可以看到如果正在执行的请求总数<=64 && 单个Host正在执行的请求<=5,则将请求加入到runningAsyncCalls集合中,紧接着就是利用线程池执行该请求,否则就将该请求放入readyAsyncCalls集合中。上面我们已经说了,AsyncCall是Runnable的子类(间接),因此,在线程池中最终会调用AsyncCall的execute()方法执行异步请求。

这就是okhttp的工作流程,就分析到这里。

你可能感兴趣的:(okhttp的使用和及简单源码分析)