如何更高效地使用 OkHttp
- 原文連結: 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 的每一條請求和響應訊息:
這種自我檢查方式(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 的可用性和自我檢查能力,並且提升你的應用的質量。
相關文章
- 更高效、更安全地操作 CSSOM :CSS Typed OMCSS
- Go 程式如何高效地除錯?Go除錯
- WWDC 2018:在Swift中如何高效地使用集合Swift
- 如何實現高效地IT服務管理
- 如何使用 RxJS 更優雅地進行定時請求JS
- Swift開發應用時如何更方便地使用顏色?Swift
- 如何讓成年玩家更健康地玩遊戲?遊戲
- 如何更優雅地切換 Git 分支Git
- 如何高效地學習開源專案
- OkHttp使用教程HTTP
- [譯] 在 flutter 中高效地使用 BLoC 模式FlutterBloC模式
- 如何優雅而高效地使用Matplotlib實現資料視覺化視覺化
- OkHttp使用詳解HTTP
- Okhttp 使用詳解HTTP
- 如何高效的使用 GitGit
- 教你更優雅地寫 API 之「列舉使用」API
- AWS中國區域釋出財務管理服務 讓客戶更經濟高效地使用雲服務
- 使用Kotlin高效地開發Android App(四)KotlinAndroidAPP
- 使用Kotlin高效地開發Android App(一)KotlinAndroidAPP
- 使用Kotlin高效地開發Android App(二)KotlinAndroidAPP
- 使用Kotlin高效地開發Android App(三)KotlinAndroidAPP
- OkHttp使用分析—WebSocket篇HTTPWeb
- 【譯】如何高效的使用 GitGit
- 程式設計師跳槽時,如何高效地準備面試?程式設計師面試
- 如何安全高效地進行網點檔案下發?
- 如何安全高效地進行分公司檔案下發?
- 如何優雅地使用 macOSMac
- 如何優雅地使用 GitGit
- 如何更無憂的使用MacMac
- 晶片公司如何高效有安全地共享專案檔案?晶片
- 如何有序高效地進行大檔案跨國傳輸?
- okhttp get post 使用原始碼HTTP原始碼
- OKHttp3簡單使用HTTP
- 使用Glide以及OkHttp整合IDEHTTP
- 【Android】在Kotlin中更優雅地使用LiveDataAndroidKotlinLiveData
- 使用 udev 高效、動態地管理 Linux 裝置檔案devLinux
- 關於JDK原始碼:我想聊聊如何更高效地閱讀JDK原始碼
- 如何高效地遠端部署?自動化運維利器 Fabric 教程運維