// Defining a cache of 5 MB size
val cacheSize = (5 * 1024 * 1024).toLong()

//Initializing instance of Cache class
val myCache = Cache(context.cacheDir, cacheSize)

//defining okhttpclient instance
val okHttpClient = OkHttpClient.Builder()
.cache(myCache)
.build()

定義緩存規(guī)則

根據(jù)設(shè)備是否連接到互聯(lián)網(wǎng),讓我們來(lái)定義一些基本的網(wǎng)絡(luò)緩存規(guī)則:

你可以為你的項(xiàng)目定義不同且更為細(xì)致的規(guī)則來(lái)應(yīng)對(duì)復(fù)雜的情況,但為了簡(jiǎn)單起見,我們將采用上述規(guī)則。

第一步是檢查用戶是否連接到互聯(lián)網(wǎng)。為此,我們可以使用 ConnectivityManager 類收集數(shù)據(jù)并檢查用戶是否連接到互聯(lián)網(wǎng)。讓我們來(lái)定義一個(gè)名為 hasNetwork() 的函數(shù),如下所示:

//CheckNetwork.kt
fun hasNetwork(context: Context): Boolean {
val connectivityManager = context
.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager
val nw = connectivityManager.activeNetwork ?: return false
val actNw = connectivityManager.getNetworkCapabilities(nw) ?: return false
return when {
actNw.hasTransport(NetworkCapabilities.TRANSPORT_WIFI) -> true
actNw.hasTransport(NetworkCapabilities.TRANSPORT_CELLULAR) -> true
actNw.hasTransport(NetworkCapabilities.TRANSPORT_ETHERNET) -> true
actNw.hasTransport(NetworkCapabilities.TRANSPORT_BLUETOOTH) -> true
else -> false
}
}

我們檢查用戶是否連接到WiFi、蜂窩移動(dòng)網(wǎng)絡(luò)或藍(lán)牙網(wǎng)絡(luò),并根據(jù)情況返回true或false。

為了實(shí)現(xiàn)我們緩存規(guī)則的定義,我們將使用 OkHttp 庫(kù)中的一個(gè)組件,叫做 Interceptor。攔截器是一個(gè)強(qiáng)大的機(jī)制,允許您在應(yīng)用程序發(fā)送或接收 HTTP 請(qǐng)求和響應(yīng)之前攔截、處理甚至修改它們。它們?cè)谕ㄐ帕鞒讨衅鸬街虚g人的作用,讓您更加靈活地控制 OkHttp 處理網(wǎng)絡(luò)請(qǐng)求的方式。

在攔截器中,我們將提供指導(dǎo),告訴我們的 okHttpClient 實(shí)例何時(shí)使用緩存響應(yīng)。

在構(gòu)建客戶端實(shí)例時(shí),讓我們使用 addInterceptor() 函數(shù)來(lái)添加攔截器。我們可以在這個(gè)函數(shù)中修改我們的 API 請(qǐng)求。在 lambda 函數(shù)中,我們可以獲取請(qǐng)求對(duì)象,并在最終發(fā)送到服務(wù)器之前對(duì)其進(jìn)行修改。為了添加緩存功能,我們使用 cacheControl() 函數(shù),該函數(shù)接受 CacheControl 類的參數(shù)。

我們需要實(shí)現(xiàn)的第一條規(guī)則是,如果設(shè)備連接到互聯(lián)網(wǎng),則使用 30 分鐘前的響應(yīng)(如果有的話),否則獲取新的響應(yīng)。我們使用 maxAge() 函數(shù)來(lái)實(shí)現(xiàn)這一點(diǎn)。

我們需要實(shí)現(xiàn)的第二條規(guī)則是,如果設(shè)備斷開連接,則使用最多 1 天前的響應(yīng)。為此,我們使用 maxStale() 函數(shù)。

// Defining a cache of 5 MB size
val cacheSize = (5 * 1024 * 1024).toLong()

//Initializing instance of Cache class
val myCache = Cache(context.cacheDir, cacheSize)

//defining okhttpclient instance
val okHttpClient = OkHttpClient.Builder()
.cache(myCache)
.addInterceptor { chain ->
var request = chain.request()
request = if (hasNetwork(context))
request
.newBuilder()
.cacheControl(
CacheControl.Builder()
.maxAge(30, TimeUnit.MINUTES)
.build()
)
.build()
else
request
.newBuilder()
.cacheControl(
CacheControl.Builder()
.maxStale(1, TimeUnit.DAYS)
.build()
)
.build()
chain.proceed(request)
}
.build()

maxAge() 與 maxStale() 的區(qū)別

max-age 和 max-stale 是 HTTP 緩存中用來(lái)控制響應(yīng)新鮮度的指令。它們有著不同的含義:

以上是它們的區(qū)別?,F(xiàn)在我們已經(jīng)在 OkHttpClient 中實(shí)現(xiàn)了緩存系統(tǒng),是時(shí)候?yàn)槟愕捻?xiàng)目構(gòu)建一個(gè)快速可靠的用戶體驗(yàn)了。如果你使用 Retrofit 作為 OkHttp 的封裝器,也可參考以下代碼片段來(lái)實(shí)現(xiàn)緩存!

//RetrofitClient.kt 
class RetrofitClient(private val context: Context) {
val cacheSize = (5 * 1024 * 1024).toLong()

val instance: Api by lazy {
val myCache = Cache(context.cacheDir, cacheSize)

val okHttpClient = OkHttpClient.Builder()
.cache(myCache)
.addInterceptor { chain ->
var request = chain.request()
request = if (hasNetwork(context))
request
.newBuilder()
.cacheControl(
CacheControl.Builder()
.maxAge(30, TimeUnit.MINUTES)
.build()
)
.build()
else
request
.newBuilder()
.cacheControl(
CacheControl.Builder()
.maxStale(1, TimeUnit.DAYS)
.build()
)
.build()
chain.proceed(request)
}
.addInterceptor(HttpLoggingInterceptor().apply {
this.level = HttpLoggingInterceptor.Level.BODY }
)
.build()

val retrofit = Retrofit.Builder()
.baseUrl("BASE_URL")
.addConverterFactory(GsonConverterFactory.create())
.client(okHttpClient)
.build()

retrofit.create(Api::class.java)
}
}

現(xiàn)在,你可以開始在實(shí)際設(shè)備或模擬器上運(yùn)行你的項(xiàng)目了。要檢查緩存是否有效,請(qǐng)使用Android Studio中的App Inspection標(biāo)簽頁(yè),或者使用OkHttp的日志攔截器來(lái)記錄所有網(wǎng)絡(luò)調(diào)用。

結(jié)論

對(duì)于長(zhǎng)時(shí)間不變的API響應(yīng),可以通過上述技術(shù)進(jìn)行緩存,從而節(jié)省大量資源。軟件開發(fā)始終涉及根據(jù)時(shí)間和空間做出一定的權(quán)衡。在這種情況下,我們?yōu)榱斯?jié)省時(shí)間而犧牲了一點(diǎn)空間(用于存儲(chǔ)API響應(yīng))。

本文章轉(zhuǎn)載微信公眾號(hào)@虎哥Lovedroid

上一篇:

Zabbix API開發(fā)實(shí)戰(zhàn),創(chuàng)建報(bào)警媒介和代碼示例

下一篇:

用gin寫簡(jiǎn)單的crud后端API接口
#你可能也喜歡這些API文章!

我們有何不同?

API服務(wù)商零注冊(cè)

多API并行試用

數(shù)據(jù)驅(qū)動(dòng)選型,提升決策效率

查看全部API→
??

熱門場(chǎng)景實(shí)測(cè),選對(duì)API

#AI文本生成大模型API

對(duì)比大模型API的內(nèi)容創(chuàng)意新穎性、情感共鳴力、商業(yè)轉(zhuǎn)化潛力

25個(gè)渠道
一鍵對(duì)比試用API 限時(shí)免費(fèi)

#AI深度推理大模型API

對(duì)比大模型API的邏輯推理準(zhǔn)確性、分析深度、可視化建議合理性

10個(gè)渠道
一鍵對(duì)比試用API 限時(shí)免費(fèi)