okhttp之Dispatcher

轉載請以鏈接形式標明出處:
本文出自:103style的博客


base on 3.12.0


目錄

  • 簡介
  • Dispatcher成員變量介紹
  • Dispatcher構造方法介紹
  • Dispatcher主要方法介紹
  • 小結

簡介

首先我們來介紹下 Dispatcher,官方描述是這樣的:

Policy on when async requests are executed.
執行異步請求時的策略

所以Dispatcher是我們進行異步請求是 okhttp 給我們提供的 執行異步請求時的策略.

public final class Dispatcher {...}

我們可以看到 Dispatcher 類是由 final 修飾的,代表它不能被繼承。

我們先來看下 Dispatcher是什麼時候設置的。
通過查看下面 OkHttpClient 的代碼,我們知道在我們創建 OkHttpClient 的時候,如果我們沒有通過 builder.dispatcher(Dispatcher dispatcher) 修改 Dispatcher的配置的話,默認的 dispatcher 就是 默認配置的 Dispatcher類。

public class OkHttpClient implements ... {
    ...
    public Builder newBuilder() {
        return new Builder(this);
    }
    public static final class Builder {
        Dispatcher dispatcher;
        ...
        public Builder() {
            dispatcher = new Dispatcher();
            ...
        }
        public Builder dispatcher(Dispatcher dispatcher) {
            if (dispatcher == null) throw new IllegalArgumentException("dispatcher == null");
            this.dispatcher = dispatcher;
            return this;
        }
    }
}

Dispatcher成員變量介紹

  • int maxRequests = 64;
    默認同時執行的最大請求數, 可以通過setMaxRequests(int)修改.

  • int maxRequestsPerHost = 5;
    每個主機默認請求的最大數目, 可以通過setMaxRequestsPerHost(int)修改.

  • private @Nullable Runnable idleCallback;
    調度沒有請求任務時的回調.

  • ExecutorService executorService;
    執行異步請求的線程池,默認是 核心線程爲0,最大線程數爲Integer.MAX_VALUE,空閒等待爲60s.

  • Deque<AsyncCall> readyAsyncCalls = new ArrayDeque<>();
    異步請求的執行順序的隊列.

  • Deque<AsyncCall> runningAsyncCalls = new ArrayDeque<>();
    運行中的異步請求隊列.

  • Deque<RealCall> runningSyncCalls = new ArrayDeque<>();
    運行中的同步請求隊列.


Dispatcher構造函數

public Dispatcher(ExecutorService executorService) {
  this.executorService = executorService;
}
public Dispatcher() {}

可以自己設置執行任務的線程池。


Dispatcher主要方法介紹

  • 配置和獲取 同時執行請求的最大任務數、同主機允許同時執行的最大任務數
    public void setMaxRequests(int maxRequests) {...}
    public synchronized int getMaxRequests() {...}
    public void setMaxRequestsPerHost(int maxRequestsPerHost) {...}
    public synchronized int getMaxRequestsPerHost() {...}
    
  • 設置沒有請求任務時的回調
    public synchronized void setIdleCallback(@Nullable Runnable idleCallback) {
        this.idleCallback = idleCallback;
    }
    
  • 添加到請求隊列
    void enqueue(AsyncCall call) {
        synchronized (this) {
            readyAsyncCalls.add(call);
        }
        promoteAndExecute();
    }
    synchronized void executed(RealCall call) {
        runningSyncCalls.add(call);
    }
    
  • 執行等待隊列中的請求任務
    private boolean promoteAndExecute() {
        assert (!Thread.holdsLock(this));
        List<AsyncCall> executableCalls = new ArrayList<>();
        boolean isRunning;
        synchronized (this) {
            //獲取等待中的任務隊列
            for (Iterator<AsyncCall> i = readyAsyncCalls.iterator(); i.hasNext(); ) {
                AsyncCall asyncCall = i.next();
                // 超過可以同時運行的最大請求任務數
                if (runningAsyncCalls.size() >= maxRequests) break; 
                // 超過同一主機同時運行的最大請求任務數
                if (runningCallsForHost(asyncCall) >= maxRequestsPerHost) continue; 
                i.remove();
                executableCalls.add(asyncCall);
                runningAsyncCalls.add(asyncCall);
            }
            isRunning = runningCallsCount() > 0;
        }
        for (int i = 0, size = executableCalls.size(); i < size; i++) {
            AsyncCall asyncCall = executableCalls.get(i);
            asyncCall.executeOn(executorService());
        }
        return isRunning;
    }
    
  • 獲取執行任務的線程池.
    如果沒有通過構造方法Dispatcher(ExecutorService executorService) 設置線程池的話,默認就是 核心線程爲0,最大線程數爲Integer.MAX_VALUE,空閒等待爲60s,用SynchronousQueue保存等待任務 的線程池。
    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;
    }
    
  • 獲取和當前請求的host一樣的運行中的請求個數.
    private int runningCallsForHost(AsyncCall call) {
        int result = 0;
        for (AsyncCall c : runningAsyncCalls) {
            if (c.get().forWebSocket) continue;
            if (c.host().equals(call.host())) result++;
        }
        return result;
    }
    
  • 取消所有請求任務
    public synchronized void cancelAll() {
        for (AsyncCall call : readyAsyncCalls) {
            call.get().cancel();
        }
        for (AsyncCall call : runningAsyncCalls) {
            call.get().cancel();
        }
        for (RealCall call : runningSyncCalls) {
            call.cancel();
        }
    }
    
  • 結束請求任務
    void finished(AsyncCall call) {}
    void finished(RealCall call) {}
    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;
        }
        boolean isRunning = promoteAndExecute();
    
        if (!isRunning && idleCallback != null) {
            idleCallback.run();
        }
    }
    
  • 獲取等待中、執行中的請求任務
    public synchronized List<Call> queuedCalls() {
        List<Call> result = new ArrayList<>();
        for (AsyncCall asyncCall : readyAsyncCalls) {
            result.add(asyncCall.get());
        }
        return Collections.unmodifiableList(result);
    }
    public synchronized List<Call> runningCalls() {
        List<Call> result = new ArrayList<>();
        result.addAll(runningSyncCalls);
        for (AsyncCall asyncCall : runningAsyncCalls) {
            result.add(asyncCall.get());
        }
        return Collections.unmodifiableList(result);
    }
    public synchronized int queuedCallsCount() {
        return readyAsyncCalls.size();
    }
    public synchronized int runningCallsCount() {
        return runningAsyncCalls.size() + runningSyncCalls.size();
    }
    

小結

Dispatcher是我們進行異步請求是 okhttp 給我們提供的 執行異步請求時的策略

Dispatcher因爲是final修飾的類,所以我們我能繼承它,但是我們可以通過創建一個Dispatcher對象,然後修改 執行任務的線程池最大併發數同主機最大併發數 等。

執行任務的方法是promoteAndExecute().


以上

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章