Android开发Android开发经验谈Android技术知识

OkHttp基本原理浅析

2019-07-22  本文已影响8人  像程序那样思考

我们先来看下OkHttp的两个使用方法
同步:

   //同步执行方法,不应该在UI线程使用
    response = client.newCall(request).execute();
    //异步方法    
    response = client.newCall(request).enqueue(new Callback() {
            @Override
            public void onFailure(Call call, IOException e) {

            }

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

                }
            }
        });

让我们进入newCall一探究竟~

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

    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是个什么东西,主要看他的 execute和enqueue方法

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

由此可见execute确实是执行在调用它的线程里面的,看代码应该是通过getResponseWithInterceptorChain()来具体执行的,看到了似曾相识的interceptor,其实这里是运用了责任链设计模式的拦截器,这个稍后再说 execute方法暂时先告一段落,接下来看enqueue

   @Override public void enqueue(Callback responseCallback) {
    synchronized (this) {
      if (executed) throw new IllegalStateException("Already Executed");
      executed = true;
    }
    captureCallStackTrace();
    eventListener.callStart(this);
    //看来这里才是真正执行的方法,继续跟踪,发现了一个dispather,并且还new了一个AsyncCall对象,传入了我们的匿名内部类接口实例
    client.dispatcher().enqueue(new AsyncCall(responseCallback));
    }

上面跟踪到了一个dispather,这个又是个什么鬼?字面理解是分发器,看来事情没有这么简单啊,继续跟进

  synchronized void enqueue(AsyncCall call) {
    if (runningAsyncCalls.size() < maxRequests && runningCallsForHost(call) < maxRequestsPerHost) {
      runningAsyncCalls.add(call);
      //咦,这个不是线程池嘛 
      executorService().execute(call);
    } else {
      readyAsyncCalls.add(call);
    }
    }

我们逮到了dispather的enqueue,发现他的call是丢给了线程池了,那么这个call,也就是AsyncCall是什么?先来看看它吧

   /**
      *注意这个类在RealCall类里面哦,是其内部类
      */
     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;
    }

    //发现AsyncCall也有一个execute方法,和RealCall差不多
    @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;
          //这个是传进来的回调啦,它不是UI线程
          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);
          //这个也是传进来的回调啦,它也不是UI线程
          responseCallback.onFailure(RealCall.this, e);
        }
      } finally {
        client.dispatcher().finished(this);
      }
    }
    }

原来它实现了NamedRunnable,那么这个NamedRunnable应该是实现了Runnable,然后我们这个execute没有在这里被调用,猜想应该是被父类重写的run给调用了,然后这个run应该是在线程池中的,验证下

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

回到RealCall的enqueued来,是不是思路清晰多了。
首先,newCall(request)初始化request后生成了一个RealCall,紧接着RealCall的execute开始执行这个请求,如果异步,就是传入enqueue(callback),然后调用dispather.enqueue(new AsyncCall(callback)),这个AsyncCall呢他是RealCall内部类所以同样可以访问到newCall(request)初始化后的request,它持有callback回调同时又是个Runnable,并且它的run方法调用了他自己和外部类RecalCall名称相同功能相似的execute方法,然后这个AsyncCall在dispather里面是丢给线程池执行的,这样一来问题就通了。

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

说了这么久dispather?这个dispather到底是啥?别急,继续看

 private int maxRequests = 64;
     private int maxRequestsPerHost = 5;
     ...
     public Dispatcher(ExecutorService executorService) {
    this.executorService = executorService;
    }

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

原来真是个线程池的封装类。。。这个线程池核心线程0,非核心线程int的最大值,但是外面maxRequest和 maxRequestsPerHost已经限制了它的并发线程数了,非核心线程60s超时时间,阻塞队列是SynchronousQueue,也就是不存储元素的队列,进了必出。到此问题已经基本解决。

下面说一下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);
    }

继续看chain.proceed,看之前顺便提下责任链设计模式。典型的责任链模式,是定义一个责任链节点对象抽象类,定义一个消息抽象类,每个节点对象必须实现抽象类的handleMessage(Object message)方法,然后对传进来的消息按实际情况处理,未处理完或者不能处理的交给下一个链节点对象,直到消息处理完毕。

责任链体现了典型的分层思想,分层的好处是显而易见的,复杂简单化,灵活和易于维护。比如TCP/IP协议就是经典的例子。

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

简单总结下上面代码,其实大概就是getResponseWithInterceptorChain()里面先实例化一条责任链,注册各种拦截器(链节点对象),然后通过chain.proceed获得下一个拦截器(节点)

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

每次调用chain对象proceed获得节点后,节点的intercept取出chain里面的request处理,然后用户在里面又调用procee,index+1,获得下一个拦截器,然后调用拦截器的拦截方法intercept,用户里面又取出chain里面的request处理,继续循环下去。

简要说下拦截器

拦截器


最后

如果你看到了这里,觉得文章写得不错就给个呗?如果你觉得那里值得改进的,请给我留言。一定会认真查询,修正不足。谢谢。

希望读到这的您能转发分享关注一下我,以后还会更新技术干货,谢谢您的支持!

转发+点赞+关注,第一时间获取最新知识点

Android架构师之路很漫长,一起共勉吧!
——————分割线——————
简书点赞可以有好几种赞,长按点赞按钮就会有选项,大家点赞的时候,麻烦点个超赞~

上一篇 下一篇

猜你喜欢

热点阅读