Okhttp

okhttp的含義java

okhttp是一個處理網絡請求的開源項目,是安卓端最火熱的輕量級框架
複製代碼

【 OkHttp網絡架構源碼深刻 】web

功能:json

get,post請求 
文件的上傳下載
加載圖片(內部會圖片大小自動壓縮)
支持請求回調,直接返回對象、對象集合
支持session的保持
複製代碼

優勢:設計模式

容許鏈接到同一個主機地址的全部請求,提升請求效率
共享Socket,減小對服務器的請求次數
經過鏈接池,減小了請求延遲
緩存響應數據來減小重複的網絡請求
減小了對數據流量的消耗
自動處理GZip壓縮
googleplay推薦使用的網絡框架
複製代碼

OkHttp源碼和簡單用法緩存

1、基本使用服務器

同步請求markdown

json
 // (1)建立 OkHttpClient 對象OkHttpClient client = new OkHttpClient();
 // (2)建立 Request 對象
Request request = new Request.Builder()
        .url(url)
        .build();
// (3)建立 Call 對象。
Call call = client.newCall(request);
// (4)發送請求並獲取服務器返回的數據
Response response = call.execute();
// (5)取出相應的數據
String data = response.body().string();
複製代碼

異步請求cookie

json
// (1)建立 OkHttpClient 對象OkHttpClient client = new OkHttpClient();
// (2)建立 Request 對象
Request request = new Request.Builder()
        .url(url)
        .build();
// (3)建立 Call 對象。
Call call = client.newCall(request);
// (4)發送請求並獲取服務器返回的數據
call.enqueue(new Callback() {
    @Override
    public void onFailure(Call call, IOException e) {
    }
    @Override
    public void onResponse(Call call, Response response) throws IOException {
// (5)取出相應的數據
        String data = response.body().string();
    }
});
複製代碼

2、源碼分析網絡

OkHttpClient 對象session

OkHttpClient(Builder builder) {
    this.dispatcher = builder.dispatcher;
    this.proxy = builder.proxy;
    this.protocols = builder.protocols;
    this.connectionSpecs = builder.connectionSpecs;
    this.interceptors = Util.immutableList(builder.interceptors);
    this.networkInterceptors = Util.immutableList(builder.networkInterceptors);
    this.eventListenerFactory = builder.eventListenerFactory;
    this.proxySelector = builder.proxySelector;
    this.cookieJar = builder.cookieJar;
    this.cache = builder.cache;
    this.internalCache = builder.internalCache;
    this.socketFactory = builder.socketFactory;

    boolean isTLS = false;
    for (ConnectionSpec spec : connectionSpecs) {
      isTLS = isTLS || spec.isTls();
    }

    if (builder.sslSocketFactory != null || !isTLS) {
      this.sslSocketFactory = builder.sslSocketFactory;
      this.certificateChainCleaner = builder.certificateChainCleaner;
    } else {
      X509TrustManager trustManager = Util.platformTrustManager();
      this.sslSocketFactory = newSslSocketFactory(trustManager);
      this.certificateChainCleaner = CertificateChainCleaner.get(trustManager);
    }

    if (sslSocketFactory != null) {
      Platform.get().configureSslSocketFactory(sslSocketFactory);
    }

    this.hostnameVerifier = builder.hostnameVerifier;
    this.certificatePinner = builder.certificatePinner.withCertificateChainCleaner(
        certificateChainCleaner);
    this.proxyAuthenticator = builder.proxyAuthenticator;
    this.authenticator = builder.authenticator;
    this.connectionPool = builder.connectionPool;
    this.dns = builder.dns;
    this.followSslRedirects = builder.followSslRedirects;
    this.followRedirects = builder.followRedirects;
    this.retryOnConnectionFailure = builder.retryOnConnectionFailure;
    this.connectTimeout = builder.connectTimeout;
    this.readTimeout = builder.readTimeout;
    this.writeTimeout = builder.writeTimeout;
    this.pingInterval = builder.pingInterval;

    if (interceptors.contains(null)) {
      throw new IllegalStateException("Null interceptor: " + interceptors);
    }
    if (networkInterceptors.contains(null)) {
      throw new IllegalStateException("Null network interceptor: " + networkInterceptors);
    }
  }
複製代碼

在new OkHttpClient()內部使用構造器模式初始化了一些配置信息:支持協議、任務分發器(其內部包含一個線程池,執行異步請求)、鏈接池(其內部包含一個線程池,維護connection)、鏈接/讀/寫超時時長等信息。

public Builder() {
      dispatcher = new Dispatcher();// 分發器
      protocols = DEFAULT_PROTOCOLS;// HTTP 協議
      connectionSpecs = DEFAULT_CONNECTION_SPECS;// 傳輸層版本和鏈接協議
      eventListenerFactory = EventListener.factory(EventListener.NONE);// 事件監聽工廠
      proxySelector = ProxySelector.getDefault();// 代理選擇器
      cookieJar = CookieJar.NO_COOKIES;// cookie
      socketFactory = SocketFactory.getDefault();// socket 工廠
      hostnameVerifier = OkHostnameVerifier.INSTANCE;// 主機名字確認
      certificatePinner = CertificatePinner.DEFAULT;// 證書鏈
      proxyAuthenticator = Authenticator.NONE;// 代理服務器身份驗證
      authenticator = Authenticator.NONE;// 源服務器身份驗證
      connectionPool = new ConnectionPool();// 鏈接池
      dns = Dns.SYSTEM;// 域名
      followSslRedirects = true;// 是否遵循 ssl 重定向
      followRedirects = true;// 是否遵循重定向
      retryOnConnectionFailure = true;// 鏈接失敗的時候是否重試
      connectTimeout = 10_000;// 鏈接超時
      readTimeout = 10_000;// 讀超時
      writeTimeout = 10_000;// 寫超時
      pingInterval = 0;// HTTP / 2 和 Web 套接字 ping 之間的時間間隔
    }
複製代碼

Request 對象

Request request = new Request.Builder()
        .url(url)
        .build();

/*Request*/
  //...
  final HttpUrl url;
  final String method;
  final Headers headers;
  final @Nullable RequestBody body;
  final Map<Class<?>, Object> tags;
  //...
複製代碼

每一次網絡請求都是一個Request,Request是對url,method,header,body的封裝,也是對Http協議中請求行,請求頭,實體內容的封裝

一般咱們經過構建折模式來構建一個Request對象來來設置一些請求連接(url)、請求方法(method)、請求頭(headers)、請求體(body)、標籤(tag,可做爲取消請求的標記)

Call對象

Call call = client.newCall(request);
  @Override public Call newCall(Request request) {
    return RealCall.newRealCall(this, request, false /* for web socket */);
  }
  static RealCall newRealCall(OkHttpClient client, Request originalRequest, boolean forWebSocket) {
    // Safely publish the Call instance to the EventListener.
    RealCall call = new RealCall(client, originalRequest, forWebSocket);
    call.eventListener = client.eventListenerFactory().create(call);
    return call;
  }
複製代碼

RealCall 是 Call 的實現類,Call 定義了請求相關的操做,例如同步異步、取消請求等方法。因此後續的請求相關操做基本都是在調用 Call 定義的方法,而這些方法真正的執行是它的實現類 RealCall

請求數據

同步請求 代碼以下:

@Override public Response execute() throws IOException {
    synchronized (this) {
      if (executed) throw new IllegalStateException("Already Executed");
      executed = true;
    }
    captureCallStackTrace();
    eventListener.callStart(this);
    try {
      client.dispatcher().executed(this);// (1)
      Response result = getResponseWithInterceptorChain();// (2)
      if (result == null) throw new IOException("Canceled");
      return result;
    } catch (IOException e) {
      eventListener.callFailed(this, e);
      throw e;
    } finally {
      client.dispatcher().finished(this);// (3)
    }
  }

 /*Dispatcher*/
  synchronized void executed(RealCall call) {
    runningSyncCalls.add(call);
  }

// RealCall.java
Response getResponseWithInterceptorChain() throws IOException {
    // Build a full stack of interceptors.
    // 建立一個攔截器鏈
    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));
    // originalRequest:咱們寫的 request
    Interceptor.Chain chain = new RealInterceptorChain(interceptors, null, null, null, 0,
        originalRequest, this, eventListener, client.connectTimeoutMillis(),
        client.readTimeoutMillis(), client.writeTimeoutMillis());

    return chain.proceed(originalRequest);
}
複製代碼

①根據上面源碼咱們能夠知道getResponseWithInterceptorChain()返回了 Response ,因而可知訪問網絡從服務器獲取數據的操做都在getResponseWithInterceptorChain()內

②這裏引入了攔截器鏈,Request 須要經過攔截器連接收相應的處理最後纔會發送到服務器並獲取服務器返回的響應

攔截器執行順序

應用攔截器:開發者添加的攔截器

   retryAndFollowUpInterceptor:負責失敗重連操做,以及重定向,若是 call 被取消會拋出 IOException

   BridgeInterceptor:做爲網絡層與應用層之間的橋樑,把(應用層請求)user request轉化爲(網絡層請求)network request,而後向服務器發送network request,獲得(網絡層響應)network reseponse後轉化爲(應用層響應) user response

   CacheInterceptor:處理緩存中的 requests 以及把 responses 寫到緩存

   ConnectInterceptor:負責與目標服務器創建鏈接

   網絡攔截器:開發者添加的攔截器

   CallServerInterceptor:攔截器鏈的最後一個攔截器,負責向服務器發送請求和從服務器獲取響應數據
複製代碼

異步請求代碼以下

// RealCall.java
@Override public void enqueue(Callback responseCallback) {
    synchronized (this) { // 若是這個 call 已經被執行過,拋異常
        if (executed) throw new IllegalStateException("Already Executed");
        executed = true;
    }
    captureCallStackTrace();
    eventListener.callStart(this);
    client.dispatcher().enqueue(new AsyncCall(responseCallback));
}

AsyncCall call = new AsyncCall(responseCallback);
Dispatcher dispatcher = client.dispatcher();
dispatcher.enqueue(call);
複製代碼

把 responseCallback 封裝成 AsyncCall

返回了一個 Dispatcher 調用任務調度器 Dispatcher 的 enqueue() 異步執行 call

解一下 Dispatcher

// Dispatcher.java
public final class Dispatcher {
    // 同步請求和異步請求之和最大值
    private int maxRequests = 64;
    // 同一個 host 請求數最大值
    private int maxRequestsPerHost = 5;
    private @Nullable Runnable idleCallback;

    /** Executes calls. Created lazily. */
    /** 用於執行請求的線程池,且是懶加載的 */
    private @Nullable ExecutorService executorService;

    /** 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<>();

   ...

    public synchronized ExecutorService executorService() {
        if (executorService == null) {
             // 核心線程數爲0,最大線程數爲 Integer.MAX_VALUE ,空閒線程最多存活60秒
            executorService = new ThreadPoolExecutor(0, Integer.MAX_VALUE, 60, TimeUnit.SECONDS,
            new SynchronousQueue<Runnable>(), Util.threadFactory("OkHttp Dispatcher", false));
        }
        return executorService;
    }

    synchronized void enqueue(AsyncCall call) {
      if (runningAsyncCalls.size() < maxRequests 
          && runningCallsForHost(call) < maxRequestsPerHost) {
        // 若是正在執行的異步請求數沒有達到最大值
        // 就放到 runningAsyncCalls 標記爲正在執行
        runningAsyncCalls.add(call);
        // 用 executorService 執行這個異步請求
        executorService().execute(call);
      } else { 
        // 若是正在執行的異步請求數達到最大值
        // 就放到 readyAsyncCalls 標記爲等待執行
        readyAsyncCalls.add(call);
      }
    }

  ...
}
複製代碼

Dispatcher 是任務調度器,內部創建了一個線程池 ExecutorService ,並且維護了三個集合:

readyAsyncCalls : 等待被執行的異步請求集合

runningAsyncCalls : 正在執行的異步請求集合,包括已經被取消但未完成的請求

runningSyncCalls : 正在執行的同步請求集合,包括已經被取消但未完成的請求
複製代碼

全部異步請求都交由線程池 ExecutorService 來執行。

線程池實際上是 ThreadPoolExecutor ,且核心線程數爲 0 、最大線程數爲Integer.MAX_VALUE、空閒線程存活最大時間爲60秒,即全部線程執行完以後空閒60秒就會被銷燬,並且存在線程過多致使內存溢出問題等問題,可是在 Dispatcher 的調度下是不會發生線程過多狀況的,由於 Dispatcher 限制了正在執行的請求數(同步和異步之和)最大爲64,同一個host下請求同時存在數最大值爲 5 。

線程池會調用線程執行 AsyncCall 的 execute()

// RealCall.java
final class AsyncCall extends NamedRunnable {

  ...

  @Override protected void execute() {
    boolean signalledCallback = false;
    try {
      // 經過攔截器鏈獲得從服務器獲取的響應 Response
      Response response = getResponseWithInterceptorChain();
      // 若是 retryAndFollowUpInterceptor.cancel() 被調用過就報異常
      if (retryAndFollowUpInterceptor.isCanceled()) {
        signalledCallback = true; // 標記 callback 回調函數已被調用
        responseCallback.onFailure(RealCall.this, new IOException("Canceled"));
      } else {
        // 到這裏表示獲取響應成功
        signalledCallback = true; // 標記 callback 回調函數已被調用
        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 {
        eventListener.callFailed(RealCall.this, e);
        responseCallback.onFailure(RealCall.this, e);
      }
    } finally {
      // 最後要通知 dispatcher 標記該任務已完成
      client.dispatcher().finished(this);
    }
  }
}
複製代碼

AsyncCall 的 execute() 邏輯很簡單,getResponseWithInterceptorChain() 咱們已經在上篇文章中瞭解過了,獲取 Response 以後只須要判斷是回調 responseCallback 的 onFailure() 仍是 onResponse(),因此 enqueue() 中的回調方法是在子線程中被調用的,固然最後還要調用 finished() 通知 Dispatcher 該任務已經完成了,須要從runningAsyncCalls中移除該任務。

Dispatcher 的判斷就在每一個異步任務結束時調用的 finish(call) 內

// Dispatcher.java
// 異步請求
void finished(AsyncCall call) {
    finished(runningAsyncCalls, call, true);
}
// 同步請求
void finished(RealCall call) {
    finished(runningSyncCalls, call, false);
}

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!");
        // 異步請求 promoteCalls 爲 true,同步請求爲 false
        if (promoteCalls) promoteCalls();
        runningCallsCount = runningCallsCount();
        idleCallback = this.idleCallback;
    }

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

public synchronized int runningCallsCount() {
    // 返回全部正在運行的同步異步請求總數
    return runningAsyncCalls.size() + runningSyncCalls.size();
}

private void promoteCalls() {
    // 1.當正在運行的同步請求異步請求數大於64時直接 return
    if (runningAsyncCalls.size() >= maxRequests) return; // Already running max capacity.
    // 2.沒有等待執行的異步請求的時候 return
    if (readyAsyncCalls.isEmpty()) return; // No ready calls to promote.
    // 3.能夠從 readyAsyncCalls 中取任務放到 runningAsyncCalls 中並執行
    for (Iterator<AsyncCall> i = readyAsyncCalls.iterator(); i.hasNext(); ) {
        // 從迭代器獲取下一個等待的異步任務
        AsyncCall call = i.next();
       // 對同一個 host 請求數不能超過 5
        if (runningCallsForHost(call) < maxRequestsPerHost) {
            // 從 readyAsyncCalls 中刪除 call
            i.remove();
            // 把 call 添加到 runningAsyncCalls
            runningAsyncCalls.add(call);
            // 使用線程池執行 call
            executorService().execute(call);
        }
        // 一直執行 for 循環直到 runningAsyncCalls 數達到 64 個
        if (runningAsyncCalls.size() >= maxRequests) return; // Reached max capacity.
    }
}
複製代碼

同步請求和異步請求執行後最終都會調用 dispatcher.finish(),內部執行的區別是異步請求會調用 promoteCalls(),目的就是對 readyAsyncCalls 和 runningAsyncCalls 進行調度

攔截器

攔截器代碼以下:

/*RealCall*/
  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));
    // (1) 構建責任鏈
    Interceptor.Chain chain = new RealInterceptorChain(interceptors, null, null, null, 0,
        originalRequest, this, eventListener, client.connectTimeoutMillis(),
        client.readTimeoutMillis(), client.writeTimeoutMillis());
    // (2) 處理責任鏈中的攔截器
    return chain.proceed(originalRequest);
  }
複製代碼

能夠看到,這裏用到了不少攔截器,將這些攔截器構建成一條責任鏈,而後再一個個處理。這裏用到了責任鏈模式,每一個攔截器負責相應的功能,上一個攔截器完成會傳給下一個攔截器,直到最後一個攔截器執行完再一層層向上返回 Response。

看完源碼,發現 OkHttp 是一個設計得很是優秀的框架。該框架運用了不少設計模式,例如建造者模式、責任鏈模式等等。知道了 OkHttp 的核心是攔截器,這裏採用的就是責任鏈模式,每一個攔截器負責相應的功能,發起請求的時候由上往下依次執行每一個攔截器,響應的數據則層層往上傳遞。

相關文章
相關標籤/搜索