Android實現(xiàn)網(wǎng)絡(luò)訪問攔截器的常見方式
1、接口訪問鑒權(quán)原型
一般我們在訪問服務(wù)器端的時候,比如登錄成功了,會有一個信息的返回,一般會有一個auth值,也有可能是一個sessionid,這樣我們就可以帶著這個值或者id,去訪問一些需要鑒權(quán)的接口了。
2、客戶端調(diào)用header鑒權(quán)
在android端,我們通常會編寫一些攔截器來進行自動加入header,這樣就可以去自動加入,不需要手工處理。

3、攔截器的方式
方式一(withContext / runBlocking)
每次請求都同步獲取 jsessionId
方式二(CoroutineScope + 緩存)
異步加載并緩存 jsessionId,支持并發(fā)控制
方式三(純 runBlocking)
完全阻塞方式獲取 jsessionId
4、三種方式的對比

1. 方式一:使用 runBlocking 獲取值
適用場景:如果你能確保該攔截器僅運行在非 UI 線程(如 OkHttp 的后臺線程),可以接受。
不推薦原因:
若攔截器在主線程執(zhí)行(如某些測試或調(diào)試場景),會導(dǎo)致卡頓甚至 ANR。
每次請求都重新獲取 JSESSIONID,效率低且無必要。
val jsessionId = runCatching {
runBlocking(dispatcher) {
appDataStore.getJSessionId().firstOrNull()
}
}.getOrNull()2. 方式二:使用 CoroutineScope + 緩存(推薦)
異步加載,不會阻塞主線程。
使用緩存避免頻繁讀取數(shù)據(jù)源,提高性能。
支持并發(fā)控制(通過 synchronized)。
實現(xiàn)了 Closeable 接口,便于資源釋放。
推薦作為生產(chǎn)代碼使用,尤其適合需要高性能、穩(wěn)定性強的 App。
private var cachedJSessionId: String? = null
private val scope = CoroutineScope(SupervisorJob() + Dispatchers.IO)
private fun updateJSessionId() {
scope.launch {
appDataStore.getJSessionId().firstOrNull()?.let {
synchronized(updateLock) {
cachedJSessionId = it
}
}
}
}3. 方式三:純 runBlocking 同步獲取
在任何上下文中使用都會導(dǎo)致線程阻塞。
如果 appDataStore.getJSessionId() 是耗時操作(如從磁盤讀?。?,極易造成 ANR。無法應(yīng)對異常情況。
val jsessionId: String? = runBlocking {
appDataStore.getJSessionId().firstOrNull()
}
5、相關(guān)代碼
方式一:使用 withContext
/**
* 攔截器,用于添加 JSESSIONID 到請求頭
* 在指定的協(xié)程調(diào)度器 dispatcher 中,從 appDataStore 獲取 JSessionId 的值,并忽略可能的異常,最終將結(jié)果賦值給 jsessionId。
* 具體解釋如下:
* runCatching { ... }:捕獲代碼塊中的異常,避免程序崩潰。
* runBlocking(dispatcher) { ... }:在指定的協(xié)程調(diào)度器中啟動一個阻塞式協(xié)程。
* appDataStore.getJSessionId().firstOrNull():獲取 JSessionId 的第一個值,若無數(shù)據(jù)則返回 null。
* .getOrNull():從 runCatching 的結(jié)果中取出值,若發(fā)生異常則返回 null。
*/
class AuthInterceptor @Inject constructor(
private val appDataStore: AppDataStore,
private val dispatcher: CoroutineDispatcher = Dispatchers.IO
) : Interceptor {
override fun intercept(chain: Interceptor.Chain): Response {
val request = chain.request()
// 使用 withContext 在 IO 線程獲取數(shù)據(jù)
val jsessionId = runCatching {
runBlocking(dispatcher) {
appDataStore.getJSessionId().firstOrNull()
}
}.getOrNull()
val newRequest = if (jsessionId != null) {
val cookie = request.header("Cookie").orEmpty()
val newCookie = if (cookie.isEmpty()) {
"JSESSIONID=$jsessionId"
} else {
"$cookie; JSESSIONID=$jsessionId"
}
request.newBuilder()
.header("Cookie", newCookie)
.build()
} else {
request
}
return chain.proceed(newRequest)
}
}該 Kotlin 代碼定義了一個 AuthInterceptor類,用于在發(fā)起網(wǎng)絡(luò)請求時自動添加 `JSESSIONID` 到 Cookie 中。其邏輯如下:
1. 從 AppDataStore中異步獲取 `JSESSIONID`;
2. 若存在 `JSESSIONID`,則將其拼接到請求頭的 `Cookie` 字段中;
3. 最終繼續(xù)執(zhí)行修改后的請求。
方式二:使用 CoroutineScope
class AuthInterceptor @Inject constructor(
private val appDataStore: AppDataStore
) : Interceptor, Closeable {
private var cachedJSessionId: String? = null
private val updateLock = Any()
private val scope = CoroutineScope(SupervisorJob() + Dispatchers.IO)
init {
updateJSessionId()
}
private fun updateJSessionId() {
scope.launch {
appDataStore.getJSessionId().firstOrNull()?.let {
synchronized(updateLock) {
cachedJSessionId = it
}
}
}
}
override fun intercept(chain: Interceptor.Chain): Response {
val request = chain.request()
var jsessionId = cachedJSessionId
if (jsessionId == null) {
synchronized(updateLock) {
jsessionId = cachedJSessionId
if (jsessionId == null) {
updateJSessionId()
return chain.proceed(request)
}
}
}
val cookieHeader = request.header("Cookie").orEmpty()
val newCookie = if (cookieHeader.isEmpty()) {
"JSESSIONID=$jsessionId"
} else {
"$cookieHeader; JSESSIONID=$jsessionId"
}
val newRequest = request.newBuilder()
.header("Cookie", newCookie)
.build()
return chain.proceed(newRequest)
}
// 實現(xiàn) Closeable 接口,確保資源釋放
override fun close() {
scope.cancel()
}
}推薦作為生產(chǎn)代碼使用,尤其適合需要高性能、穩(wěn)定性強的 App。
該 Kotlin 代碼實現(xiàn)了一個 AuthInterceptor用于在 HTTP 請求中自動添加 `JSESSIONID` Cookie。其功能如下:
1. **構(gòu)造與初始化**:通過依賴注入獲取 AppDataStore用于讀取會話 ID,并在初始化時嘗試更新會話 ID。
2. **intercept 函數(shù)**:
- 若已有緩存的 `JSESSIONID`,則將其添加到請求頭的 Cookie 中;
- 若沒有緩存,則重新加載會話 ID;
- 使用鎖保證線程安全。
3. **updateJSessionId 函數(shù)**:使用協(xié)程從 AppDataStore 異步加載會話 ID 并緩存。
4. **close 函數(shù)**:取消協(xié)程作用域,釋放資源。
整體作用:為每個請求自動注入最新的 `JSESSIONID`,確保會話狀態(tài)有效。
方式三:runBlocking阻塞性獲取
class AuthInterceptor @Inject constructor(
private val appDataStore: AppDataStore
) : Interceptor {
override fun intercept(chain: Interceptor.Chain): Response {
val request = chain.request()
// 在攔截器中同步獲取 jsessionid(假設(shè)可以阻塞)
val jsessionId: String? = runBlocking {
appDataStore.getJSessionId().firstOrNull()
}
// 構(gòu)建新的請求,添加 cookie(僅當(dāng) jsessionId 存在時)
val newRequest = request.newBuilder().apply {
if (jsessionId != null) {
// 獲取現(xiàn)有 Cookie 并追加新的 JSESSIONID
val existingCookies = request.header("Cookie")
val newCookie = "JSESSIONID=$jsessionId"
val finalCookie = if (existingCookies.isNullOrEmpty()) {
newCookie
} else {
"$existingCookies; $newCookie"
}
addHeader("Cookie", finalCookie)
}
}.build()
return chain.proceed(newRequest)
}
}該 Kotlin 代碼定義了一個 AuthInterceptor 攔截器,其功能是:
從 AppDataStore 中同步獲取 jsessionId(使用 runBlocking 阻塞等待);
若 jsessionId 存在,則將其添加到 HTTP 請求頭的 Cookie 字段中;
最終構(gòu)建新請求并繼續(xù)攔截器鏈的執(zhí)行。
作用:為每個請求自動添加會話標(biāo)識 JSESSIONID。
6、總結(jié)
綜合上述幾種方式,其實測試下來都是可以的,基本上是沒有什么區(qū)別,但是如果考慮綜合因素,或者多環(huán)境下的適應(yīng)性,還是考慮用使用 CoroutineScope + 緩存(推薦)。
到此這篇關(guān)于Android實現(xiàn)網(wǎng)絡(luò)訪問攔截器的常見方式的文章就介紹到這了,更多相關(guān)Android攔截器內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Android 6.0 掃描不到 Ble 設(shè)備需開啟位置權(quán)限的方法
今天小編就為大家分享一篇Android 6.0 掃描不到 Ble 設(shè)備需開啟位置權(quán)限的方法,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧2018-07-07
Flutter?文字中劃線動畫StrikeThroughTextAnimation
這篇文章主要為大家介紹了Flutter?文字中劃線動畫StrikeThroughTextAnimation示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪2023-04-04

