OkHttp 源碼分析

一、概述

在日常開發(fā)中最常用的請求框架就是Okhttp了,本文將對okhttp的請求流程 由淺入深進(jìn)行分析,由于我項目中使用的Okhttp版本為3.12.0,所以我們根據(jù)3.12.0的源碼進(jìn)行分析。

Okhttp特點:
OkHttp 是一個默認(rèn)高效的 HTTP 客戶端:

  • HTTP/2 支持允許對同一主機的所有請求共享一個套接字。
  • 連接池減少了請求延遲(如果 HTTP/2 不可用)。
  • 透明 GZIP 縮小了下載大小。
  • 響應(yīng)緩存完全避免了網(wǎng)絡(luò)重復(fù)請求。

二、源碼分析

在源碼分析之前,我們先看一下Okhttp的簡單使用。

        //創(chuàng)建一個OkhttpClient
        OkHttpClient client = new OkHttpClient();
        //創(chuàng)建Request
        Request request = new Request.Builder()
                .url("https://www.baidu.com")
                .build();
        //同步請求
        Response response = client.newCall(request).execute();

這樣就很簡單的實現(xiàn)了一個請求,我們一點一點來分析,先來看OkhttpClient

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

OkHttpClient(Builder builder) {
    //省略部分代碼
    this.dispatcher = builder.dispatcher;
    this.proxy = builder.proxy;
    this.protocols = builder.protocols;
    //省略部分代碼
  }

可以看到在創(chuàng)建OkHttpClient時,使用的是建造者模式,最終創(chuàng)建出OkhttpClient,Builder的代碼太長這里就不貼了,就是對一些配置的初始化,例如超時時間等,另外我們可以通過這個Builder.build()方法來構(gòu)建出OkhttpClient。

接下來我們再來看Request是什么:

public final class Request {
  final HttpUrl url;
  final String method;
  final Headers headers;
  final @Nullable RequestBody body;
  final Map<Class<?>, Object> tags;

  private volatile @Nullable CacheControl cacheControl; // Lazily initialized.

  Request(Builder builder) {
    this.url = builder.url;
    this.method = builder.method;
    this.headers = builder.headers.build();
    this.body = builder.body;
    this.tags = Util.immutableMap(builder.tags);
  }
    //省略部分代碼
}

Request包含的就是請求的url,method,header,RequestBody等信息。同樣是通過Builder來構(gòu)建。

最后就是執(zhí)行請求了,到這里之后就是重點了,好好看哦,

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

            }

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

            }
        });

同樣我們按照順序,先來看client的newCall方法做了什么:

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

newCall方法返回值為Call,返回了一個RealCall(Call的實現(xiàn)類)。

既然newCall返回的是RealCall,那么enqueue方法就一定再RealCall里面,就是說我們的同步/異步請求最終都是通過這個RealCall來執(zhí)行的。所以我們來看RealCall的enqueue()方法:

  @Override public void enqueue(Callback responseCallback) {
    synchronized (this) {
        //如果為true 直接拋出異常,說明這個請求已經(jīng)執(zhí)行了
      if (executed) throw new IllegalStateException("Already Executed");
      executed = true;
    }
    captureCallStackTrace();
      //事件監(jiān)聽回調(diào)
    eventListener.callStart(this);
      //重點
    client.dispatcher().enqueue(new AsyncCall(responseCallback));
  }

上面的最后一行代碼又調(diào)用了client.dispatcher().enqueue()方法,就是Dispatcher類的enqueue(),我們來點進(jìn)去看:

  void enqueue(AsyncCall call) {
    synchronized (this) {
      readyAsyncCalls.add(call);
    }
    promoteAndExecute();
  }

enqueue方法又將call加入了readyAsyncCalls,我們先來看看這是啥:

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

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

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

在Dispatcher類中有三個雙端隊列:
readyAsyncCalls:準(zhǔn)備隊列,
runningAsyncCalls:運行時異步隊列
runningSyncCalls:運行時同步隊列

所以說上面的enqueue方法中,就是將call先放到了準(zhǔn)備隊列,然后調(diào)用了promoteAndExecute()方法,我們來看看這個方法:

private boolean promoteAndExecute() {
    assert (!Thread.holdsLock(this));

    //用于保存要執(zhí)行請求的隊列
    List<AsyncCall> executableCalls = new ArrayList<>();
    boolean isRunning;
    synchronized (this) {
      for (Iterator<AsyncCall> i = readyAsyncCalls.iterator(); i.hasNext(); ) {
        AsyncCall asyncCall = i.next();

          //如果運行中隊列大于maxRequests 64 終止執(zhí)行
          //就是正在請求的數(shù)量 不能大于64
        if (runningAsyncCalls.size() >= maxRequests) break; // Max capacity.
          //如果相同host同時請求大于maxRequestsPerHost 結(jié)束本次循環(huán)
          //就是對同一個主機的同時請求 不能大于5
        if (runningCallsForHost(asyncCall) >= maxRequestsPerHost) continue; // Host max capacity.

        //符合條件時,將請求從準(zhǔn)備隊列中移除
        i.remove();
        //將請求加到執(zhí)行隊列
        executableCalls.add(asyncCall);
        //將請求加到運行中隊列
        runningAsyncCalls.add(asyncCall);
      }
      isRunning = runningCallsCount() > 0;
    }

    //遍歷執(zhí)行隊列
    for (int i = 0, size = executableCalls.size(); i < size; i++) {
      AsyncCall asyncCall = executableCalls.get(i);
      //執(zhí)行請求
      asyncCall.executeOn(executorService());
    }

    return isRunning;
  }

promoteAndExecute()方法的主要操作就是將符合執(zhí)行條件的請求執(zhí)行,最終調(diào)用的是asyncCall.executeOn(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;
  }

這個方法其實就是返回了一個線程池,所以說異步請求是通過線程池來處理的,我們再返回去看asyncCall.executeOn()方法:

    void executeOn(ExecutorService executorService) {
      assert (!Thread.holdsLock(client.dispatcher()));
      boolean success = false;
      try {
        //通過線程池 執(zhí)行請求 this = RealCall
        executorService.execute(this);
        success = true;
      } catch (RejectedExecutionException e) {
        //異常處理
        InterruptedIOException ioException = new InterruptedIOException("executor rejected");
        ioException.initCause(e);
        eventListener.callFailed(RealCall.this, ioException);
        responseCallback.onFailure(RealCall.this, ioException);
      } finally {
        if (!success) {
            //
          client.dispatcher().finished(this); // This call is no longer running!
        }
      }
    }

在上面的方法中,調(diào)用了線程池的execute(this)方法,就相當(dāng)于調(diào)用了RealCall的execute,繼續(xù)來看這個方法:

@Override protected void execute() {
      boolean signalledCallback = false;
      timeout.enter();
      try {
          //這里最終通過攔截器 來拿到response
        Response response = getResponseWithInterceptorChain();
          //下面就是一些判斷處理操作 就不詳細(xì)看了
        if (retryAndFollowUpInterceptor.isCanceled()) {
          signalledCallback = true;
          responseCallback.onFailure(RealCall.this, new IOException("Canceled"));
        } else {
          signalledCallback = true;
            //回調(diào)
          responseCallback.onResponse(RealCall.this, response);
        }
      } catch (IOException e) {
        e = timeoutExit(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 {
          //最終調(diào)用了finished
        client.dispatcher().finished(this);
      }
    }

RealCall的execut方法就是通過攔截器來拿到response,最終調(diào)用了client.dispatcher().finished(this)方法,攔截器我們先放在后面講,先繼續(xù)看client.dispatcher().finished(this):

//最終調(diào)用的這個方法
private <T> void finished(Deque<T> calls, T call) {
    Runnable idleCallback;
    synchronized (this) {
      if (!calls.remove(call)) throw new AssertionError("Call wasn't in-flight!");
      idleCallback = this.idleCallback;
    }

    //這里是重點 這里又調(diào)用了promoteAndExecute()
    boolean isRunning = promoteAndExecute();

    if (!isRunning && idleCallback != null) {
      idleCallback.run();
    }
  }

到這里是不是有一點豁然開朗了?這個方法里又調(diào)用了promoteAndExecute()方法,還記得剛開始的enqueue里面也是執(zhí)行了這個方法嘛?所以說當(dāng)一個請求結(jié)束后,又再次處理隊列中的其他請求。

到這里整個請求的流程就結(jié)束了。我們繼續(xù)來看一下上面提到的攔截器;

Response response = getResponseWithInterceptorChain();

Response getResponseWithInterceptorChain() throws IOException {
    // Build a full stack of interceptors.
    //聲明一個list 存儲攔截器
    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));
    //自定義的的network攔截器
    if (!forWebSocket) {
      interceptors.addAll(client.networkInterceptors());
    }
    //請求服務(wù)攔截器
    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中有五種攔截器:重試攔截器,橋接攔截器,緩存攔截器,連接攔截器,請求服務(wù)攔截器,另外還有用戶自定義的攔截器。其中攔截器是責(zé)任鏈模式,通過層層調(diào)用最終返回我們想要的Response,攔截器暫時不深入介紹了,后面有時間再研究吧。

異步請求看完之后,接下來就剩同步請求了,其實同步請求更簡單,

@Override public Response execute() throws IOException {
    synchronized (this) {
      if (executed) throw new IllegalStateException("Already Executed");
      executed = true;
    }
    captureCallStackTrace();
    timeout.enter();
    eventListener.callStart(this);
    try {
      client.dispatcher().executed(this);
      Response result = getResponseWithInterceptorChain();
      if (result == null) throw new IOException("Canceled");
      return result;
    } catch (IOException e) {
      e = timeoutExit(e);
      eventListener.callFailed(this, e);
      throw e;
    } finally {
      client.dispatcher().finished(this);
    }
  }
 synchronized void executed(RealCall call) {
    runningSyncCalls.add(call);
  }

同步請求則直接將請求通過dispatcher加入了運行中同步隊列中,其他處理與異步請求大致相同,這里就不做太多介紹了。

三、總結(jié)

至此我們已經(jīng)分析完了Okhttp請求的處理流程,我們最后總結(jié)一下:

  1. 創(chuàng)建OkhttpClient
  2. 構(gòu)建Request請求
  3. newCall得到RealCall
  4. 通過RealCall調(diào)用enqueue/execute
  5. 通過dispatcher進(jìn)行任務(wù)分發(fā)
  6. 最終通過攔截器得到Response

最后畫一張流程圖僅供參考:


img.jpg
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容