Okhttp 思考


Okhttp 基礎(chǔ)知識導(dǎo)圖

Okhttp 框架

Okhttp 使用
1,創(chuàng)建一個客戶端。
2,創(chuàng)建一個請求。
3,發(fā)起請求(入?yún)⒒卣{(diào))。

Request request = new Request.Builder().url( "http://xxxxx").build();
//創(chuàng)建客戶端
OkHttpClient mOkHttpClient= new OkHttpClient.Builder()
        .connectTimeout(30000, TimeUnit.MICROSECONDS)
        .readTimeout(30000, TimeUnit.MICROSECONDS)
        .writeTimeout(30000, TimeUnit.MICROSECONDS).build();
//創(chuàng)建請求
Call mCall= mOkHttpClient.newCall(request);
//請求+回調(diào)
mCall.enqueue(new Callback() {
    @Override
    public void onFailure(Call arg0, IOException arg1) {
    }
    @Override
    public void onResponse(Call arg0, Response arg1) throws IOException {
    }
});

一、任務(wù)分發(fā)

網(wǎng)絡(luò)請求不能占用主線程資源,在多個請求并發(fā)條件下,需要一個管理器實現(xiàn)任務(wù)分發(fā),將任務(wù)交給線程池。

任務(wù)分發(fā)

1,RealCall 類
Request 類是一個請求實體,配置 url,組裝 Header,請求體RequestBody, method 支持 GET、HEAD、POST、DELETE、PUT、PATCH。
每個 Request 實體創(chuàng)建一個 RealCall 對象,負責一個具體請求的 http 事務(wù)。

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

Call 接口,內(nèi)部工廠 Call.Factory 的 newCall() 方法創(chuàng)建,OkHttpClient 類實現(xiàn)工廠接口,創(chuàng)建 RealCall。
RealCall 類同步方法。

@Override
public Response execute() throws IOException {
    synchronized (this) {
    ...
    try {
      client.dispatcher().executed(this);
      Response result = getResponseWithInterceptorChain();//同步,耗時處
      return result;
    } finally {
      client.dispatcher().finished(this);
    }
}

異步方法。

@Override
public void enqueue(Callback responseCallback) {
    synchronized (this) {
    ...
    client.dispatcher().enqueue(new AsyncCall(responseCallback));
}

execute() 方法,同步請求需要自己實現(xiàn)線程池。enqueue() 方法,創(chuàng)建一個異步任務(wù),交給分發(fā)者,Dispatcher 類分發(fā),(同步或異步)。
2,Dispatcher 分發(fā)者
Dispatcher 類控制多個 http 請求的節(jié)奏,負責 RealCall 的并發(fā),管理(等待/運行)隊列,線程池調(diào)度。

請求分發(fā)流程
synchronized void executed(RealCall call) {
    runningSyncCalls.add(call);
}

同步分發(fā),將 RealCall 對象加入同步隊列,表示 RealCall 正在執(zhí)行。

synchronized void enqueue(AsyncCall call) {
    //maxRequests=64,maxRequestsPerHost=5
    if (runningAsyncCalls.size() < maxRequests && 
            runningCallsForHost(call) < maxRequestsPerHost) {
      runningAsyncCalls.add(call);//加入運行隊列
      executorService().execute(call);//線程池執(zhí)行任務(wù)
    } else {
      readyAsyncCalls.add(call);
    }
}

異步分發(fā),maxRequests:最大請求數(shù)量,maxRequestsPerHost:每個主機最大并發(fā)請求數(shù)量。
滿足運行時,將 AsyncCall (Runnable 任務(wù),RealCall 內(nèi)部類),加入運行隊列,派發(fā)線程池處理,不滿足運行時,加入等待隊列。
3,請求結(jié)束
每次(同步/異步)請求完成時,在 try/finally() 方法結(jié)束,Dispatcher 類的 finished()方法。

private <T> void finished(Deque<T> calls, T call, boolean promoteCalls) {
    int runningCallsCount;
    Runnable idleCallback;
    synchronized (this) {
      //同步隊列刪除RealCall,異步運行隊列刪除AsyncCall任務(wù)
      if (!calls.remove(call)) throw new AssertionError("Call wasn't in-flight!");
      if (promoteCalls) promoteCalls();//異步promoteCalls參數(shù)是true
      runningCallsCount = runningCallsCount();
      idleCallback = this.idleCallback;
    }
    if (runningCallsCount == 0 && idleCallback != null) {
      idleCallback.run();
    }
}

異步請求時,繼續(xù)查詢等待隊列中的請求。

private void promoteCalls() {
    if (runningAsyncCalls.size() >= maxRequests) return;
    if (readyAsyncCalls.isEmpty()) return; 
    for (Iterator<AsyncCall> i = readyAsyncCalls.iterator(); i.hasNext(); ) {
        AsyncCall call = i.next();
        if (runningCallsForHost(call) < maxRequestsPerHost) {
            i.remove();//滿足條件,等待隊列刪除
            runningAsyncCalls.add(call);//加入運行中隊列
            executorService().execute(call);//開始執(zhí)行
        }
        if (runningAsyncCalls.size() >= maxRequests) return; 
    }
}

遍歷等待隊列的任務(wù),滿足運行條件時,從等待隊列刪除,加入運行隊列,派發(fā)線程池。

二、線程池管理

分發(fā)者內(nèi)部閥值[0, Integer.MAX_VALUE]的緩存線程池。

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;
}
  • 不保留 corePoolSize 核心線程,最大可創(chuàng)建線程 MAX_VALUE。
  • 工作線程 keepAliveTime 設(shè)置60存活期線程復(fù)用。
  • SynchronousQueue 阻塞隊列不存儲元素,管道。

大量http請求并發(fā)時,線程池啟動多個工作線程(臨時線程),確保每個任務(wù)都有線程及時處理。
線程執(zhí)行主體是 NamedRunnable 類 run() 方法,子類是運行隊列中的 AsyncCall任務(wù)類,實現(xiàn) execute() 抽象方法。
AsyncCall 構(gòu)造方法,傳入外部Callback回調(diào)對象。

@Override
protected void execute() {
    boolean signalledCallback = false;
    try {
        //觸發(fā)RealCall類的方法,AsyncCall任務(wù)類定義在RealCall類內(nèi)部。
        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) {
    } finally {
        //異步請求結(jié)束后finish。
        client.dispatcher().finished(this);
    }
}

外部 RealCall 類的 getResponseWithInterceptorChain() 方法,(同步/異步)請求耗時操作,返回 Response 實體。

Response getResponseWithInterceptorChain() throws IOException {
    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);
    return chain.proceed(originalRequest);
}

創(chuàng)建了一系列攔截器,鏈式處理,緩存、連接都通過攔截器鏈的節(jié)點實現(xiàn)。、

三、攔截器

攔截器順序

客戶端自定義攔截器
retryAndFollowUpInterceptor 攔截器
BridgeInterceptor 橋接
CacheInterceptor 緩存
ConnectInterceptor 連接
networkInterceptors 網(wǎng)絡(luò)
CallServerInterceptor 數(shù)據(jù)流讀寫

RealInterceptorChain 類是在攔截器之間傳遞的節(jié)點,第一次創(chuàng)建時,index 初始是0,入?yún)鬟f originalRequest 原始請求與攔截器列表。
在 RealInterceptorChain 類的 proceed() 方法,開始鏈式處理,利用攔截器表中各層攔截處理 Request 請求和 Response 回復(fù)。

public Response proceed(Request request, StreamAllocation streamAllocation, HttpCodec httpCodec, 
                    RealConnection connection) throws IOException {
    if (index >= interceptors.size()) throw new AssertionError();
    calls++;
    ..
    RealInterceptorChain next = new RealInterceptorChain(
        interceptors, streamAllocation, httpCodec, connection, index + 1, request);
    Interceptor interceptor = interceptors.get(index);
    Response response = interceptor.intercept(next);
    if (httpCodec != null && index + 1 < interceptors.size() && next.calls != 1) {
        //拋出異常
    }
    if (response == null) {
        //拋出異常
    }
    return response;
}

查找攔截器鏈表 index 索引的 Interceptor,新建 index++ 的 RealInterceptorChain 節(jié)點,觸發(fā) Interceptor的intercept() 方法,傳遞新 Chain。
列表中每一個 Interceptor 攔截器的 intercept() 方法。

  • 處理 Request 請求
  • Chain 節(jié)點的 proceed() 方法
  • 處理 Response 答復(fù)

每一個 Chain 節(jié)點 proceed() 方法邏輯。

  • 查找下一個攔截器
  • 創(chuàng)建新 Chain 節(jié)點
  • 攔截器 intercept() 方法,傳入新節(jié)點

當 index++索引到達最后一個攔截器 CallServerInterceptor 時,真正向 Server 發(fā)送數(shù)據(jù),獲取 Response,然后遞歸一層層按原路返回。
攔截器列表前部分 Interceptor 優(yōu)先處理原始 Request,后部分優(yōu)先處理原始 Response。

攔截器處理流程
public interface Interceptor {
    Response intercept(Chain chain) throws IOException;
    interface Chain {
        Request request();
        Response proceed(Request request) throws IOException;
        Connection connection();
    }
}

攔截器設(shè)計是一個遞歸調(diào)用過程,Chain 對象是鏈節(jié)點,proceed() 方法處理時,創(chuàng)建 index++的 Chain 新節(jié)點,傳遞給下一個攔截器。
proceed() 方法流入 Request 請求,流出 Response 答復(fù),攔截器目的是層層截斷流入與流出,先截流,加工處理,再放流。

四、緩存設(shè)計

Okhttp 緩存設(shè)計

五、連接池

Okhttp 連接池

六、數(shù)據(jù)流

Okhttp 數(shù)據(jù)流


任重而道遠

最后編輯于
?著作權(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)容