編輯:Android資訊
- 原文鏈接: Effective OkHttp
- 原文作者 : Michael Parker
- 譯文出自 : 掘金翻譯計劃
- 譯者 : Brucezz
- 校對者: iThreeKing , Adam Shen , Jaeger
- 轉載請聯系,並注明出處。
在為 可汗學院 開發 Android app 時, OKHttp 是一個很重要的開源庫。雖然它的默認配置已經提供了很好的效果,但是我們還是采取了一些措施提高 OkHttp 的可用性和自我檢查能力:
有些響應消息通過包含 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。
Stetho 是一個 Facebook 出品的超贊的開源庫,它可以讓你用 Chrome 的功能—— 開發者工具 來檢查調試你的 Android 應用。
Stetho 不僅能夠檢查應用的 SQLite 數據庫和視圖層次,還可以檢查 OkHttp 的每一條請求和響應消息:
這種自我檢查方式(Introspection)有效地確保了服務器返回允許緩存資源的 HTTP 首部時,且核緩存資源存在時,不再發出任何請求。
開啟 Stetho,只用簡單地添加一個 StethoInterceptor 實例到網絡攔截器(Network Interceptor)的列表中去:
okHttpClient.networkInterceptors().add(new StethoInterceptor());
應用運行完畢之後,打開 Chrome 然後跳轉到 chrome://inspect。設備、應用以及應用標識符信息會被陳列出來。直接點擊“inspect”鏈接就可以打開開發者工具,然後切換到 Network 標簽開始監測 OkHttp 發出的請求。
可能和我們一樣,你使用 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 裡。
當客戶端在每一次請求中都提供一個詳細的 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 的構造函數。我們使用了這些字段:
最後三個字段是根據我們的 Gradle 構建腳本中的 applicationId, versionCode 和versionName 的值來確定的。了解更多信息請參考文檔 應用版本控制 ,和 使用 Gradle 配置你的 applicationId 。
小提示:如果你的應用中用到了 WebView,你可以配置使用相同的 User-Agent 值,即之前創建的 UserAgentInterceptor:
WebSettings settings = webView.getSettings(); settings.setUserAgentString(userAgentHeaderValue);
在 2.5.0 版本之前,OkHttp 請求默認永不超時。從 2.5.0 版本開始,如果建立了一個連接,或從連接讀取下一個字節,或者向連接寫入下一個字節,用時超過了10秒,請求就會超時。分別調用 setConnectTimeout,setReadTimeout 或 setWriteTimeout 方法可以重寫那些默認值。
小提示:Picasso 和 Retrofit 為它們的默認 OkHttpClient 實例指定不同的超時時長。 默認情況下, Picasso 設定如下:
Retrofit 設定如下:
用你自己的 OkHttpClient 實例配置好 Picasso 和 Retrofit 之後,就能確保所有請求超時的一致性了。
再次強調,OkHttp 的默認配置提供了顯著的效果,但是采取以上的措施,可以提高 OkHttp 的可用性和自我檢查能力,並且提升你的應用的質量。
Android安全加密專題文章索引 Android安全加密:對稱加密 Android安全加密:非對稱加密 Android安全加密:消息摘要Message Dig
有時候我們需要錄制Android手機的屏幕,比如寫了一個Demo應用,需要發布到博客和微博上。 如下是我錄制轉GIF的效果圖 &
模式的定義 適配器模式把一個類的接口變換成客戶端所期待的另一種接口,從而使原本因接口不匹配而無法在一起工作的兩個類能夠在一起工作。 使用場景 用電源接口做例子,筆
看到大家提出的關於Android的問題,有一部分可以用EventBus解決,而也有相當多的人推薦使用EventsBus,因為其和GreenDAO出自一家公司,並