Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> Android資訊 >> 如何更高效地使用 OkHttp

如何更高效地使用 OkHttp

編輯:Android資訊

  • 原文鏈接: Effective OkHttp
  • 原文作者 : Michael Parker
  • 譯文出自 : 掘金翻譯計劃
  • 譯者 : Brucezz
  • 校對者: iThreeKing ,  Adam Shen ,  Jaeger
  • 轉載請聯系,並注明出處。

在為 可汗學院 開發  Android app 時, OKHttp 是一個很重要的開源庫。雖然它的默認配置已經提供了很好的效果,但是我們還是采取了一些措施提高 OkHttp 的可用性和自我檢查能力:

1. 在文件系統中開啟響應緩存

有些響應消息通過包含 Cache-Control HTTP 首部字段允許緩存,但是默認情況下,OkHttp 並不會緩存這些響應消息。因此你的客戶端可能會因為不斷請求相同的資源而浪費時間和帶寬,而不是簡單地讀取一下首次響應消息的緩存副本。

為了在文件系統中開啟響應緩存,需要配置一個 com.squareup.okhttp.Cache 實例,然後把它傳遞給 OkHttpClient 實例的 setCache 方法。你必須用一個表示目錄的 File 對象和最大字節數來實例化 Cache 對象。那些能夠緩存的響應消息會被寫在指定的目錄中。如果已緩存的響應消息導致目錄內容超過了指定的大小,響應消息會按照最近最少使用( LRU Policy )的策略被移除。

正如 Jesse Wilson 所建議的 ,我們將響應消息緩存在 context.getCacheDir() 的子文件夾中:

// 緩存根目錄,由這裡推薦 -> http://stackoverflow.com/a/32752861/400717.
// 小心可能為空,參考下面兩個鏈接
// https://groups.google.com/d/msg/android-developers/-694j87eXVU/YYs4b6kextwJ 和
// http://stackoverflow.com/q/4441849/400717.
final @Nullable File baseDir = context.getCacheDir();
if (baseDir != null) {
  final File cacheDir = new File(baseDir, "HttpResponseCache");
  okHttpClient.setCache(new Cache(cacheDir, HTTP_RESPONSE_DISK_CACHE_MAX_SIZE));
}

在可汗學院的應用中,我們指定了 HTTP_RESPONSE_DISK_CACHE_MAX_SIZE 的大小為  10 * 1024 * 1024 ,即 10MB。

2. 集成 Stetho

Stetho 是一個 Facebook 出品的超贊的開源庫,它可以讓你用 Chrome 的功能—— 開發者工具 來檢查調試你的 Android 應用。

Stetho 不僅能夠檢查應用的 SQLite 數據庫和視圖層次,還可以檢查 OkHttp 的每一條請求和響應消息:

如何更高效地使用 OkHttp

這種自我檢查方式(Introspection)有效地確保了服務器返回允許緩存資源的 HTTP 首部時,且核緩存資源存在時,不再發出任何請求。

開啟 Stetho,只用簡單地添加一個 StethoInterceptor 實例到網絡攔截器(Network Interceptor)的列表中去:

okHttpClient.networkInterceptors().add(new StethoInterceptor());

應用運行完畢之後,打開 Chrome 然後跳轉到 chrome://inspect。設備、應用以及應用標識符信息會被陳列出來。直接點擊“inspect”鏈接就可以打開開發者工具,然後切換到 Network 標簽開始監測 OkHttp 發出的請求。

3. 使用 Picasso 和 Retrofit

可能和我們一樣,你使用 Picasso 來加載網絡圖片,或者使用  Retrofit 來簡化網絡請求和解析響應消息。在默認情況下,如果你沒有顯式地指定一個 OkHttpClient,這些開源庫會隱式地創建它們自己的 OkHttpClient 實例以供內部使用。以下代碼來自於 Picasso 2.5.2 版本的OkHttpDownloader 類:

private static OkHttpClient defaultOkHttpClient() {
  OkHttpClient client = new OkHttpClient();
  client.setConnectTimeout(Utils.DEFAULT_CONNECT_TIMEOUT_MILLIS, TimeUnit.MILLISECONDS);
  client.setReadTimeout(Utils.DEFAULT_READ_TIMEOUT_MILLIS, TimeUnit.MILLISECONDS);
  client.setWriteTimeout(Utils.DEFAULT_WRITE_TIMEOUT_MILLIS, TimeUnit.MILLISECONDS);
  return client;
}

Retrofit 也有類似的工廠方法用來創建它自己的 OkHttpClient。

圖片是應用中需要加載的最大的資源之一。Picasso 是嚴格地按照 LRU 策略在內存中維護它的圖片緩存。如果客戶端嘗試用 Picasso 加載一張圖片,並且 Picasso 沒有在內存緩存中找到該圖片,那麼它會委托內部的 OkHttpClient 實例來加載該圖片。在默認情況下,由於前面的defaultOkHttpClient 方法沒有在文件系統中配置響應緩存,該實例會一直從服務器加載圖片。

自定義一個 OkHttpClient 實例,將從文件系統返回一個已緩存的響應消息這種情況考慮在內。沒有一張圖片直接從服務器加載。這在應用第一次加載時是尤為重要的。在這個時候,Picasso 的內存中的緩存是 “冷” 的,它會頻繁地委托 OkHttpClient 實例去加載圖片。

這就需要構建一個用你的 OkHttpClient 配置的 Picasso 實例。如果你在代碼中使用Picasso.with(context).load(…) 來加載圖片,你所使用的 Picasso 單例對象,是在with 方法中用自己的 OkHttpClient 延遲加載和配置的。因此我們必須在第一次調用 with方法之前指定自己的 Picasso 實例作為單例對象。

簡單地把 OkHttpClient 實例包裝到一個 OkHttpDownloader 對象中,然後傳遞給Picasso.Builder 實例的 downloader 方法:

final Picasso picasso = new Picasso.Builder(context)
    .downloader(new OkHttpDownloader(okHttpClient))
    .build();

//客戶端應該在任何需要的時候來創建這個實例
//以防萬一,替換掉那個單例對象
Picasso.setSingletonInstance(picasso);

在 Retrofit 1.9.x 中,通過 RestAdapter 使用你的 OkHttpClient 實例,把 OkHttpClient實例包裝到一個 OkClient 實例中,然後傳遞給 RestAdapter.Builder 實例的 setClient方法:

restAdapterBuilder.setClient(new OkClient(httpClient));

在 Retrofit 2.0 中,直接把 OkHttpClient 實例傳遞給 Retrofit.Builder 實例的 client即可。

在可汗學院的應用中,我們使用 Dagger 來確保只有一個 OkHttpClient 實例,而且 Picasso 和 Retrofit 都會使用到它。我們為帶 @Singleton 注解的 OkHttpClient 實例創建了一個 provider:

@Provides
@Singleton
public OkHttpClient okHttpClient(final Context context, ...) {
  final OkHttpClient okHttpClient = new OkHttpClient();
  configureClient(okHttpClient, ...);
  return okHttpClient;
}

這個 OkHttpClient 實例隨後通過 Dagger 注入到其他用來創建 RestAdapter 和 Picasso實例的 provider 裡。

4. 設置用戶代理攔截器(User-Agent Interceptor)

當客戶端在每一次請求中都提供一個詳細的 User-Agent 頭部信息時,日志文件和分析數據提供了很有用的信息。默認情況下,OkHttp 的 User-Agent 值僅僅只有它的版本號。要設定你自己的 User-Agent,創建一個攔截器(Interceptor)然後替換掉默認值,參考 StackOverflow 上的建議 :

public final class UserAgentInterceptor implements Interceptor {
  private static final String USER_AGENT_HEADER_NAME = "User-Agent";
  private final String userAgentHeaderValue;

  public UserAgentInterceptor(String userAgentHeaderValue) {
    this.userAgentHeaderValue = Preconditions.checkNotNull(userAgentHeaderValue);
  }

  @Override
  public Response intercept(Chain chain) throws IOException {
    final Request originalRequest = chain.request();
    final Request requestWithUserAgent = originalRequest.newBuilder()
        .removeHeader(USER_AGENT_HEADER_NAME)
        .addHeader(USER_AGENT_HEADER_NAME, userAgentHeaderValue)
        .build();
    return chain.proceed(requestWithUserAgent);
  }
}

使用任何你覺得有價值的信息,來創建 User-Agent 值,然後傳遞給UserAgentInterceptor 的構造函數。我們使用了這些字段:

  • os 字段,值設置為 Android,明確表明這是一個 Android 設備
  • Build.MODEL 字段,即用戶可見的終端產品的名稱
  • Build.BRAND 字段,即消費者可見的跟產品或硬件相關的商標
  • Build.VERSION.SDK_INT 字段,即用戶可見的 [Android] 框架版本號
  • BuildConfig.APPLICATION_ID 字段
  • BuildConfig.VERSION_NAME 字段
  • BuildConfig.VERSION_CODE字段

最後三個字段是根據我們的 Gradle 構建腳本中的 applicationId, versionCode 和versionName 的值來確定的。了解更多信息請參考文檔 應用版本控制 ,和  使用 Gradle 配置你的 applicationId 。

小提示:如果你的應用中用到了 WebView,你可以配置使用相同的 User-Agent 值,即之前創建的 UserAgentInterceptor:

WebSettings settings = webView.getSettings();
settings.setUserAgentString(userAgentHeaderValue);

5. 指定合理的超時

在 2.5.0 版本之前,OkHttp 請求默認永不超時。從 2.5.0 版本開始,如果建立了一個連接,或從連接讀取下一個字節,或者向連接寫入下一個字節,用時超過了10秒,請求就會超時。分別調用 setConnectTimeout,setReadTimeout 或 setWriteTimeout 方法可以重寫那些默認值。

小提示:Picasso 和 Retrofit 為它們的默認 OkHttpClient 實例指定不同的超時時長。 默認情況下, Picasso 設定如下:

  • 連接超時15秒
  • 讀取超時20秒
  • 寫入超時20秒

Retrofit 設定如下:

  • 連接超時15秒
  • 讀取超時20秒
  • 寫入無超時

用你自己的 OkHttpClient 實例配置好 Picasso 和 Retrofit 之後,就能確保所有請求超時的一致性了。

結論

再次強調,OkHttp 的默認配置提供了顯著的效果,但是采取以上的措施,可以提高 OkHttp 的可用性和自我檢查能力,並且提升你的應用的質量。

  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved