1. 程式人生 > >Okhttp3原碼解析(一)

Okhttp3原碼解析(一)

首先看一下Okhttp3是怎麼進行請求的

//建立OkHttpClient物件
OkHttpClient client = new OkHttpClient.Builder().readTimeout(5, TimeUnit.SECONDS)
        .writeTimeout(5, TimeUnit.SECONDS)
        .connectTimeout(5, TimeUnit.SECONDS).build();
Request request = new Request.Builder().url("www.baidu.com").get().build();//建立Request物件
Call call = client.newCall(request);//將Request封裝成Call 再用Call進行同步或者非同步請求

同步請求:

//同步 :開始請求後就會進入阻塞狀態,直接接到響應
try {
    Response response = call.execute();//響應報文的資訊,比如響應頭,體...
} catch (IOException e) {
    e.printStackTrace();
}

非同步請求:

//非同步 :不會阻塞當前執行緒
call.enqueue(new Callback() {
    @Override
    public void onFailure(@NonNull Call call, @NonNull IOException e) {
    }
    @Override
    public void onResponse(@NonNull Call call, @NonNull Response response) throws IOException {
    }
});

以上就是Okhttp的用法,接下來我們來分析一下請求裡面做了什麼操作

先看一下client.newCall(request)這個方法

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

其實是建立了RealCall類,再跟進new RealCall裡面看下

RealCall(OkHttpClient client, Request originalRequest, boolean forWebSocket) {
 
//將client和之前建立的request在這裡面進行了賦值,並且建立了一個攔截器RetryAndFollowUpInterceptor(也叫重定向攔截器,後面會再進行分析)
  final EventListener.Factory eventListenerFactory = client.eventListenerFactory();
  this.client = client;
  this.originalRequest = originalRequest;
  this.forWebSocket = forWebSocket;
  this.retryAndFollowUpInterceptor = new RetryAndFollowUpInterceptor(client, forWebSocket);
  // TODO(jwilson): this is unsafe publication and not threadsafe.
  this.eventListener = eventListenerFactory.create(this);
}

再進一步看看call.execute()裡面是怎麼操作的(其實就是在RealCall裡面具體實現)看如下程式碼

@Override public Response execute() throws IOException {
  synchronized (this) {
    //這個程式碼就是讓同一個請進只能進行一次操作
     if (executed) throw new IllegalStateException("Already Executed");
    executed = true;
  }
  captureCallStackTrace();
  try {
    client.dispatcher().executed(this);//用dispatcher(排程器)分配執行的任務
    Response result = getResponseWithInterceptorChain();//獲取響應報文和一些攔截器操作,以後會重點介紹
    if (result == null) throw new IOException("Canceled");
    return result;
  } finally {
    client.dispatcher().finished(this);
  }
}

再看看Dispatch的executed方法

private final Deque<RealCall> runningSyncCalls = new ArrayDeque<>();
通過dispahch將call請求新增到了同步請求佇列裡面,很簡單
//dispatch的executed方法
synchronized void executed(RealCall call) {
  runningSyncCalls.add(call);
}

再看一下非同步的方法

call.enqueue(CallBack)
@Override public void enqueue(Callback responseCallback) {
  synchronized (this) {
    if (executed) throw new IllegalStateException("Already Executed");
    executed = true;
  }
  captureCallStackTrace();
  client.dispatcher().enqueue(new AsyncCall(responseCallback));
}

看看dispatcher的enqueque,跟同步就大不一樣,看以下幾個方法

private final Deque<AsyncCall> runningAsyncCalls = new ArrayDeque<>();//正在執行的非同步請求佇列
private int maxRequests = 64;//最大的請求佇列
private int maxRequestsPerHost = 5;//最大請求主機數
private final Deque<AsyncCall> readyAsyncCalls = new ArrayDeque<>();//非同步快取佇列
synchronized void enqueue(AsyncCall call) {
    //如果正在執行的非同步請求佇列小於最大的請求佇列  及 最大的請求主機數小於設定值時 則將任務新增到正在執行的非同步佇列中然後執行
  if (runningAsyncCalls.size() < maxRequests && runningCallsForHost(call) < maxRequestsPerHost) {
    runningAsyncCalls.add(call);
    executorService().execute(call);//建立執行緒池並且執行該任務
  } else {//同理將其新增到快取佇列中
    readyAsyncCalls.add(call);
  }
}
public synchronized ExecutorService executorService() {//建立執行緒池
  if (executorService == null) {
    //0:當核心執行緒數為0時,一段時間後會將裡面的所有執行緒給清掉;Integer.MAX_VALUE最大的執行緒數其實在okhttp3裡面不會超過64; 60:大於核心執行緒數時,每個執行緒最長的存活時間60S
     executorService = new ThreadPoolExecutor(0, Integer.MAX_VALUE, 60, TimeUnit.SECONDS
        new SynchronousQueue<Runnable>(), Util.threadFactory("OkHttp Dispatcher", false
  }
  return executorService;
}

非同步方法執行任務最終也會呼叫到getRequestWithInterseptorChain方法中,我們在呼叫非同步請求時會將Callback封裝成AsynCall,其實AsynCall繼承NamedRunnable最終繼承Runnable 當執行請求任務時就會呼叫到AsynCall的execute()方法,其實當執行緒池執行佇列任務裡就會執行asynCall的run方法 以下是程式碼AsynCall的execute()方法程式碼

@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 {
      responseCallback.onFailure(RealCall.this, e);
    }
  } finally {
    client.dispatcher().finished(this);//最終都會走到這,將任務結束掉
  }
}
void finished(AsyncCall call) {//非同步結束任務的程式碼
  finished(runningAsyncCalls, call, true);
}
private <T> void finished(Deque<T> calls, T call, boolean promoteCalls) {
  int runningCallsCount;
  Runnable idleCallback;
  synchronized (this) {//同步
    if (!calls.remove(call)) throw new AssertionError("Call wasn't in-flight!");//先將任務remove掉
    if (promoteCalls) promoteCalls();//非同步promoteCalls傳過來的是true,如果會走promoteCalls()方法,就是對任務進行重新排列
    runningCallsCount = runningCallsCount();
    idleCallback = this.idleCallback;
  }
  if (runningCallsCount == 0 && idleCallback != null) {
    idleCallback.run();
  }
}
//將非同步正在執行的任務佇列和快取佇列任務進行重新排列
private void promoteCalls() {
  if (runningAsyncCalls.size() >= maxRequests) return; // 大於64return
  if (readyAsyncCalls.isEmpty()) return; //快取佇列沒有任務,return
  for (Iterator<AsyncCall> i = readyAsyncCalls.iterator(); i.hasNext(); ) {
    AsyncCall call = i.next();//取到第一個快取佇列裡的任務
    if (runningCallsForHost(call) < maxRequestsPerHost) {//判斷請求主機數是否大於5
      i.remove();//將任務從快取佇列中移除
      runningAsyncCalls.add(call);//再新增到非同步執行佇列中
      executorService().execute(call);//最後線上程池中執行該任務
    }
    if (runningAsyncCalls.size() >= maxRequests) return; // 正在執行的佇列大小小>=64 結束迴圈
  }
}

所以非同步快取佇列裡的任務是在promoteCalls方法中執行的.

Okhttp3網路請求簡單的分析到這也就結束了,有時間再對裡面再具體的細節進行分析