【OkHttp3原始碼分析】(二)Request的enqueue

yangxi_001發表於2017-06-20

前言

如果沒有閱讀本系列文章的第一篇,請先閱讀:

【OkHttp3原始碼分析】(一)Request的execute

因為這兩者之間是有關聯的!

enqueue執行流程原始碼分析

先來看看原始碼:

  @Override 
  public void enqueue(Callback responseCallback) {
    enqueue(responseCallback, false);//繼續呼叫下面一層
  }

  void enqueue(Callback responseCallback, boolean forWebSocket) {
    synchronized (this) {//跟execute一樣,防止多個子執行緒同時呼叫執行的方法。
      if (executed) throw new IllegalStateException("Already Executed");
      executed = true;
    }
    client.dispatcher().enqueue(new AsyncCall(responseCallback, forWebSocket));//重點!
  }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

看到重點的那一行:

client.dispatcher().enqueue(new AsyncCall(responseCallback, forWebSocket));//重點!
  • 1
  • 1

我們發現,它將我們的Callback介面封裝到一個新的類AsyncCall中去。

我們來看看它的程式碼:

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() {
      boolean signalledCallback = false;
      try {
        Response response = getResponseWithInterceptorChain(forWebSocket);//和execute呼叫的是同一個!
        if (canceled) {
          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!
          logger.log(Level.INFO, "Callback failure for " + toLoggableString(), e);
        } else {
          responseCallback.onFailure(RealCall.this, e);
        }
      } finally {
        client.dispatcher().finished(this);//移除請求
      }
    }
  }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54

要注意它是繼承NamedRunnable的,這裡我們從名字也可以看出,實際上它就是一個Runnable了。

簡單看看NamedRunnable原始碼:

/**
 * Runnable implementation which always sets its thread name.
 */
public abstract class NamedRunnable implements Runnable {
  protected final String name;

  public NamedRunnable(String format, Object... args) {
    this.name = String.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();
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22

瞭解完之後,我們就可以知道哪個是重要的方法了。

沒錯就是:execute方法。(注意這個execute不是我們第一篇文章講的那個)

然後我們發現它呼叫了:

Response response = getResponseWithInterceptorChain(forWebSocket);//和execute呼叫的是同一個!
  • 1
  • 1

實際上這個和Request的execute呼叫是一樣的,只不過多了一個介面回掉的過程。這裡我們就不再重複分析了。

OK,瞭解完AnsycCall類,我們回來來看看這句的enqueue:

client.dispatcher().enqueue(new AsyncCall(responseCallback, forWebSocket));//重點!
  • 1
  • 1

首先我們都知道,OkHttp3中enqueue是非同步通過執行,然後通過介面回撥的!

再加上AsyncCall本質是一個Runnable。

所以用我們程式設計師本能的嗅覺,其實已經能夠大概擦覺到,這個client.dispatcher()通過自己封裝的執行緒池,執行我們的AsyncCall的execute方法了!

但是要是沒有察覺到也沒有關係,我們看看原始碼就知道是什麼了!

我們來看看Dispatcher類的enqueue方法:

  synchronized void enqueue(AsyncCall call) {
    //如果佇列任務沒有爆滿且小於每個Request的請求次數
    if (runningAsyncCalls.size() < maxRequests && runningCallsForHost(call) < maxRequestsPerHost) {
      runningAsyncCalls.add(call);//新增到佇列,和第一篇文章加入佇列的目的是一樣的,也是方便用來取消任務。
      executorService().execute(call);//用執行緒池執行
    } else {
      //如果爆滿,就放到準備佇列
      readyAsyncCalls.add(call);
    }
  }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

嘿嘿,果然和我們想的沒有錯,的確是使用了執行緒池:

  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;
  }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

小吐槽…這樣寫好像有點影響效能的樣子。通過synchronized實現單例模式。 還不如使用雙重判斷鎖的方式來實現,可以提升一定的效能,也避免synchronized開銷啊!!!

至此,我們已經完成了enqueue的分析了! 如果看懂了上篇文章的execute,看懂這個真的只是分分鐘的事情了!!

-Hans 2016.4.3 22:05

轉自:http://blog.csdn.net/qq_18402085/article/details/51052350

相關文章