androidokhttp緩存
A. 如何高效的使用Okhttp
OkHttp 是一個在開發可汗學院Android APP過程中非常重要的依賴庫。它的默認的配置為我們提供了非常重要實用功能,下面一些步驟我們可以讓Okhttp提供更多功能使用靈活和內省能力。
1. 啟用文件系統上的響應緩存
默認情況下,Okhttp不支持響應緩存,包括HTTP Cache-Control頭允許緩存響應。因此,客戶端通過一次又一次的請求相同的資源浪費時間和帶寬。而不是簡單地讀取初始響應後緩存的副本。
要在文件系統中啟用響應緩存,需要配置com.squareup.okhttp.Cache實例,並把它傳遞給你的OkHttpClient實例的setCache方法。你必須初始化緩存與存放目錄的文件,並以位元組為單位的最大值。
響應返回數據可以寫入給定目錄文件,如果一個響應的緩存超過了給定的大小。我們可以採取 LRU policy 。
我們可以在 stackoverflow 查看 Jesse Wilson 的回復。我們可以通過context.getCacheDir()在子目錄中緩存我們的響應:
// Base directory recommended by http://stackoverflow.com/a/32752861/400717.
// Guard against null, which is possible according to
// https://groups.google.com/d/msg/android-developers/-694j87eXVU/YYs4b6kextwJ and
// 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));
}
// Base directory recommended by http://stackoverflow.com/a/32752861/400717.
// Guard against null, which is possible according to
// https://groups.google.com/d/msg/android-developers/-694j87eXVU/YYs4b6kextwJ and
// http://stackoverflow.com/q/4441849/400717.
final @NullableFilebaseDir = context.getCacheDir();
if (baseDir != null) {
final FilecacheDir = new File(baseDir, "HttpResponseCache");
okHttpClient.setCache(new Cache(cacheDir, HTTP_RESPONSE_DISK_CACHE_MAX_SIZE));
}
在可汗學院的程序中我們指定 HTTP_RESPONSE_DISK_CACHE_MAX_SIZE as 10 * 1024 * 1024 , or 10 MB的大小
2. 集成Stetho
Stetho 是Facebook的一個可愛的庫,可以使用Chrome瀏覽器的Chrome開發人員工具功能來檢查你的Andr oid應用程序。
Stetho除了允許你檢查你的應用程序的SQLite資料庫,還可以查看View的層次結構。允許你檢查由OkHttp發起的每個請求和響應:
這種自省機制是確保伺服器返回允許資源緩存的HTTP頭是非常有用的,以及驗證沒有請求時,保證緩存的資源存在。
要想使用Stetho,只需添加一個StethoInterceptor實例的網路攔截器列表:
okHttpClient.networkInterceptors().add(new StethoInterceptor());
okHttpClient.networkInterceptors().add(new StethoInterceptor());
然後,運行應用程序,打開瀏覽器後,輸入chrome://inspect。然後你就會看到應用程序的設備和標識符的列表。然後滑鼠右鍵選擇inspect 打開開發者工具,然後打開新的tab,開始監控OkHttp請求。
3. 使用Picasso 和 Retrofit
你可能使用過 Picasso 來載入網路圖片,或者使用 Retrofit 來簡化發出請求和解碼響應。這些第三方庫將隱式地創建自己的OkHttpClient供內部使用,如果你不明確指定一個。
Picasso version 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;
}
private static () {
OkHttpClientclient = 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實例去載入圖片。
這就需要構建配置了您Picasso 的OkHttpClient實例,如果你在你的代碼中使用
Picasso.with(context).load(...)
Picasso.with(context).load(...)
載入圖片,你是用的是Picasso的單例模式。這是通過with方法懶漢模式地實例化並配置自己的OkHttpClient。因此,我們必須使我們自己的Picasso實例在單例之前通過wiht方法調用。
實現這個,可以簡單的將OkHttpClient實例封裝在OkHttpDownloader中,然後傳遞給 Picasso.Builder 實例的downloader方法。
final Picasso picasso = new Picasso.Builder(context)
.downloader(new OkHttpDownloader(okHttpClient))
.build();
// The client should inject this instance whenever it is needed, but replace the singleton
// instance just in case.
Picasso.setSingletonInstance(picasso);
final Picassopicasso = new Picasso.Builder(context)
.downloader(new OkHttpDownloader(okHttpClient))
.build();
// The client should inject this instance whenever it is needed, but replace the singleton
// instance just in case.
Picasso.setSingletonInstance(picasso);
在Retrofit中要使用OkHttpClient實例,需要改造1.9.x的一個RestAdapter,需要將OkHttpClient封裝OkClient的實例中。然後把它傳遞給RestAdapter.Builder實例的setClient方法。
restAdapterBuilder.setClient(new OkClient(httpClient));
restAdapterBuilder.setClient(new OkClient(httpClient));
在 Retrofit 2.0中只需要簡單的將OkHttpClient傳遞給Retrofit.Builder實例的client方法。
在可汗學院的APP中我們通過 Dagger 依賴注入來確保我們只有一個OkHttpClient的實例。這種方法同樣也適用於Picasso和Retrofit我們提供了一個為OkHttpClient實例提供單例模式的註解示例:
@Provides
@Singleton
public OkHttpClient okHttpClient(final Context context, ...) {
final OkHttpClient okHttpClient = new OkHttpClient();
configureClient(okHttpClient, ...);
return okHttpClient;
}
@Provides
@Singleton
public OkHttpClientokHttpClient(final Contextcontext, ...) {
final OkHttpClientokHttpClient = new OkHttpClient();
configureClient(okHttpClient, ...);
return okHttpClient;
}
OkHttpClient將會通過Dagger的註解創建一個實例提供給我們的Picasso和Retrofit。
4.指定一個用戶代理攔截器
日誌文件和分析為我們提供了更多有用的信息,當客戶在每個請求提供詳細的User-Agent
header值的時候。默認情況下,Okhttp包含User-Agent值只有在特定的Okhttp版本中。為了指定我們自己的user
agent。首先創建攔截器的替換值, 我們可以看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);
}
}
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 Responseintercept(Chainchain) throws IOException {
final RequestoriginalRequest = chain.request();
final RequestrequestWithUserAgent = originalRequest.newBuilder()
.removeHeader(USER_AGENT_HEADER_NAME)
.addHeader(USER_AGENT_HEADER_NAME, userAgentHeaderValue)
.build();
return chain.proceed(requestWithUserAgent);
}
}
為了創建User-Agent header值人然後傳遞給UserAgentInterceptor的構造器,使用你得到的任何信息。
我們可以使用:
android 的系統信息可以清晰的傳遞出這是一台android 設備
Build.MODEL 或者「製造商提供的用戶可見最終可見的名稱」
Build.BRAND或者「消費者可見的品牌與產品/硬體相關信息」
Build.VERSION.SDK_INT或者「消費者可見的Android提供的SDK版本號」
BuildConfig.APPLICATION_ID
BuildConfig.VERSION_NAME
BuildConfig.VERSION_CODE
最後三個值由的applicationID,VERSIONCODE和VERSIONNAME的值在我們的Gradle build腳本中
了解更多信息可以查看 versioning your applications 和 configuring your applicationId with Gradle
請注意,如果您的應用程序使用的是WebView,您可以配置使用相同的 User-Agent header值,你可以通過下面方法創建UserAgentInterceptor:
WebSettings settings = webView.getSettings();
settings.setUserAgentString(userAgentHeaderValue);
WebSettingssettings = webView.getSettings();
settings.setUserAgentString(userAgentHeaderValue);
5.指定合理的超時
2.5.0版本之前,OkHttp請求默認為永不超時。2.5.0版本開始如果建立連接請求超時,如果從連接讀取下一個位元組或寫入的下一個位元組到連接,花費超過10秒,就終止。這樣做需要更新到2.5.0版本我們就不需要在我們的代碼中修改bug。原因很簡單是我因為我們第一次使用的時候使用了錯誤的路徑。
要覆蓋這些默認值,可以分別調用setConnectTimeout,setReadTimeout或setWriteTimeout。
需要注意的是Picasso和Retrofit為OkHttpClient實例指定不同的超時值時,默認情況下,Picasso指定:
連接超過15秒.
讀取超過20秒
寫入超過20秒
而Retrofit指定:
連接超過15秒.
讀取超過20秒
沒有寫入超時
通過配置Picasso和Retrofit自己的OkHttpClient實例你可以確保所有的請求超時是一致的
B. Android 開發框架源碼解析:「Retrofit、Okhttp、ButterKnife、Glide....」
閱讀源碼是提升能力的絕佳方式,不僅有助於理解系統設計,還能深入學習優秀的設計模式和思想。對於Android開發者而言,研究源碼尤其重要,它能增強代碼的優雅性和解決問題的效率。
源碼分析能夠全面而深入地挖掘知識,尤其在面試中受到青睞。通過源碼學習,開發者不僅能夠掌握技術的細節,還能培養系統思考和問題解決的能力。
為了幫助Android開發者深入理解框架源碼,一份由阿里大佬整理的《Android百大框架源碼解析》資料應運而生。這份1880頁的資料涵蓋了Retrofit、OkHttp等常用框架,以及Butter Knife、Glide、LeakCanary等經典框架。盡管有些框架可能不再常用,但通過源碼學習,開發者可以深入了解其中的精髓,豐富自己的技術視野。
讓我們來一探部分框架的奧秘:Retrofit是類型安全的網路框架,基於HTTP協議,為Android和Java提供便利。OkHttp是一款基於HTTP和HTTP2.0協議的網路框架,服務於Java和Android客戶端。Butter Knife通過註解生成模板代碼,實現view與方法、參數的綁定。MPAndroidChart則是一款強大的圖表框架,幫助開發者輕松繪制各種圖表。Glide專注於提供流暢的圖片載入和緩存能力,是圖片管理的高效解決方案。LeakCanary是一款內存檢測框架,服務於Java和Android客戶端,幫助開發者識別內存泄漏問題。Android-Universal-Image-Loader曾經是圖片載入領域的王者,為開發者提供了高性能的圖片載入體驗。EventBus是一款本地組件間通信框架,簡化了組件間的通信流程。ZXing是一款條碼圖像處理庫,廣泛應用於二維碼掃描等領域。Picasso則是一款強大的圖片下載、緩存框架,提升了圖片載入的效率。
通過深入學習這些框架的源碼,開發者不僅能夠掌握技術的核心,還能培養解決問題的創新思維。這份詳盡的資料,旨在為開發者提供一份系統的學習資源,助力技術成長。
在追求技術進步的道路上,持續學習和堅持實踐是關鍵。編程是一門需要耐心和時間積累的技藝,只有不斷地學習和實踐,才能在技術的海洋中游刃有餘。
因此,不論你的目標是成為技術大牛還是僅僅提升自我,都需要保持學習的熱情和堅持,通過源碼分析不斷提升自己的技能水平。記住,每個人的時間和精力都是有限的,因此每一步都要做到極致,讓每一次學習都成為一次精彩的旅程。