欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Android OKHttp攔截器和緩存案例詳解

 更新時(shí)間:2025年04月15日 14:41:40   作者:每次的天空  
OkHttp的攔截器鏈?zhǔn)且粋€(gè)有序的攔截器集合,請(qǐng)求和響應(yīng)會(huì)依次經(jīng)過(guò)每個(gè)攔截器,這篇文章主要介紹了Android OKHttp攔截器和緩存案例詳解,感興趣的朋友一起看看吧

深入理解 OkHttp 攔截器

1. 攔截器接口詳解

Interceptor 接口是自定義攔截器的基礎(chǔ),它僅包含一個(gè)抽象方法 intercept。以下是對(duì)該方法參數(shù)和返回值的詳細(xì)解釋?zhuān)?/p>

import okhttp3.Interceptor;
import okhttp3.Request;
import okhttp3.Response;
import java.io.IOException;
public class CustomInterceptor implements Interceptor {
    @Override
    public Response intercept(Chain chain) throws IOException {
        // chain 包含了當(dāng)前請(qǐng)求的所有信息以及后續(xù)攔截器的處理邏輯
        Request originalRequest = chain.request(); 
        // 可以對(duì)原始請(qǐng)求進(jìn)行修改,例如添加請(qǐng)求頭、修改請(qǐng)求方法等
        Request modifiedRequest = originalRequest.newBuilder()
               .header("Custom-Header", "Custom-Value")
               .build();
        // proceed 方法會(huì)將修改后的請(qǐng)求傳遞給下一個(gè)攔截器,并返回響應(yīng)
        Response response = chain.proceed(modifiedRequest);
        // 可以對(duì)響應(yīng)進(jìn)行處理,例如添加自定義響應(yīng)頭、解析響應(yīng)體等
        return response.newBuilder()
               .header("Custom-Response-Header", "Custom-Response-Value")
               .build();
    }
}

Chain 參數(shù):Chain 是一個(gè)接口,它代表了整個(gè)攔截器鏈。chain.request() 方法可以獲取當(dāng)前的請(qǐng)求對(duì)象;

chain.proceed(request) 方法會(huì)將請(qǐng)求傳遞給下一個(gè)攔截器,并返回響應(yīng)。

Response 返回值:intercept 方法必須返回一個(gè) Response 對(duì)象,這個(gè)對(duì)象可以是原始響應(yīng),也可以是經(jīng)過(guò)修改后的響應(yīng)。

2.攔截器鏈的詳細(xì)執(zhí)行流程

整體流程

OkHttp 的攔截器鏈?zhǔn)且粋€(gè)有序的攔截器集合,請(qǐng)求和響應(yīng)會(huì)依次經(jīng)過(guò)每個(gè)攔截器。攔截器鏈的執(zhí)行順序是固定的,如下所示:

1. 用戶自定義攔截器(client.interceptors())

  • 位置:攔截器鏈的最前端。
  • 作用:這是開(kāi)發(fā)者可以自定義添加的攔截器,開(kāi)發(fā)者可以在這個(gè)攔截器中實(shí)現(xiàn)一些通用的業(yè)務(wù)邏輯,比如統(tǒng)一添加請(qǐng)求頭、日志記錄、請(qǐng)求參數(shù)加密等操作。由于它處于攔截器鏈的最前端,所以可以對(duì)原始的請(qǐng)求進(jìn)行最早的處理,并且能獲取到最終的響應(yīng),方便進(jìn)行日志記錄等操作。
  • 示例代碼
import okhttp3.Interceptor;
import okhttp3.Request;
import okhttp3.Response;
import java.io.IOException;
public class CustomHeaderInterceptor implements Interceptor {
    @Override
    public Response intercept(Chain chain) throws IOException {
        Request originalRequest = chain.request();
        Request newRequest = originalRequest.newBuilder()
               .header("Custom-Header", "Custom-Value")
               .build();
        return chain.proceed(newRequest);
    }
}

2. 重試和重定向攔截器(RetryAndFollowUpInterceptor)

  • 位置:緊跟用戶自定義攔截器之后。
  • 作用:負(fù)責(zé)處理請(qǐng)求的重試和重定向邏輯。當(dāng)請(qǐng)求過(guò)程中出現(xiàn)網(wǎng)絡(luò)錯(cuò)誤(如連接超時(shí)、DNS 解析失敗等)時(shí),該攔截器會(huì)根據(jù)配置的重試策略進(jìn)行重試;當(dāng)服務(wù)器返回重定向響應(yīng)(如 301、302 狀態(tài)碼)時(shí),會(huì)自動(dòng)處理重定向請(qǐng)求,重新發(fā)起新的請(qǐng)求到重定向的地址。
  • 源碼分析:在 intercept 方法中,會(huì)不斷循環(huán)處理請(qǐng)求,直到請(qǐng)求成功或者達(dá)到最大重試次數(shù)。通過(guò)判斷響應(yīng)的狀態(tài)碼和異常類(lèi)型來(lái)決定是否進(jìn)行重試或重定向操作。

3. 橋接攔截器(BridgeInterceptor)

  • 位置:在重試和重定向攔截器之后。
  • 作用:主要負(fù)責(zé)將用戶的請(qǐng)求轉(zhuǎn)換為符合網(wǎng)絡(luò)傳輸規(guī)范的請(qǐng)求。它會(huì)添加一些必要的請(qǐng)求頭,如 Content-Type、Content-Length、User-Agent 等,同時(shí)處理請(qǐng)求體的編碼和壓縮。另外,它還會(huì)對(duì)響應(yīng)進(jìn)行一些處理,比如將響應(yīng)頭中的 Content-Encoding 信息解析出來(lái),對(duì)響應(yīng)體進(jìn)行相應(yīng)的解碼操作。
  • 源碼分析:在 intercept 方法中,會(huì)根據(jù)請(qǐng)求體的情況添加相應(yīng)的請(qǐng)求頭,然后調(diào)用 chain.proceed 方法將處理后的請(qǐng)求傳遞給下一個(gè)攔截器,最后對(duì)響應(yīng)進(jìn)行處理并返回。

4. 緩存攔截器(CacheInterceptor)

  • 位置:在橋接攔截器之后。
  • 作用:負(fù)責(zé)處理請(qǐng)求的緩存邏輯。它會(huì)根據(jù)請(qǐng)求的緩存策略(如 Cache-Control 頭信息)檢查本地緩存中是否存在符合條件的響應(yīng)。如果存在且緩存有效,則直接返回緩存的響應(yīng),避免進(jìn)行網(wǎng)絡(luò)請(qǐng)求;如果緩存無(wú)效或者不存在,則發(fā)起網(wǎng)絡(luò)請(qǐng)求,并將響應(yīng)存入緩存。
  • 源碼分析:在 intercept 方法中,會(huì)先從緩存中查找匹配的響應(yīng),然后根據(jù)請(qǐng)求和緩存的情況判斷是否可以使用緩存。如果可以使用緩存,則直接返回緩存響應(yīng);否則,調(diào)用 chain.proceed 方法發(fā)起網(wǎng)絡(luò)請(qǐng)求,并將響應(yīng)存入緩存。

5. 連接攔截器(ConnectInterceptor)

  • 位置:在緩存攔截器之后。
  • 作用:負(fù)責(zé)建立與服務(wù)器的連接。它會(huì)根據(jù)請(qǐng)求的 URL 和配置,選擇合適的連接(如 HTTP/1.1 或 HTTP/2 連接),并進(jìn)行 TCP 握手和 TLS 協(xié)商(如果是 HTTPS 請(qǐng)求)。同時(shí),它會(huì)管理連接池,復(fù)用已經(jīng)建立的連接,減少連接建立的開(kāi)銷(xiāo)。
  • 源碼分析:在 intercept 方法中,會(huì)從連接池中獲取可用的連接,如果沒(méi)有可用連接則創(chuàng)建新的連接,然后進(jìn)行連接的建立和握手操作,最后將連接傳遞給下一個(gè)攔截器。

6. 用戶自定義網(wǎng)絡(luò)攔截器(client.networkInterceptors())

  • 位置:在連接攔截器之后,僅在進(jìn)行網(wǎng)絡(luò)請(qǐng)求時(shí)會(huì)執(zhí)行。
  • 作用:與用戶自定義攔截器類(lèi)似,但它更側(cè)重于對(duì)網(wǎng)絡(luò)請(qǐng)求和響應(yīng)進(jìn)行處理。由于它在連接建立之后執(zhí)行,所以可以獲取到實(shí)際的網(wǎng)絡(luò)連接信息,并且可以對(duì)網(wǎng)絡(luò)請(qǐng)求和響應(yīng)進(jìn)行更底層的修改,比如修改請(qǐng)求的字節(jié)流、監(jiān)控網(wǎng)絡(luò)流量等。
  • 示例代碼
import okhttp3.Interceptor;
import okhttp3.Request;
import okhttp3.Response;
import java.io.IOException;
public class NetworkLoggingInterceptor implements Interceptor {
    @Override
    public Response intercept(Chain chain) throws IOException {
        Request request = chain.request();
        long t1 = System.nanoTime();
        System.out.println(String.format("Sending request %s on %s%n%s",
                request.url(), chain.connection(), request.headers()));
        Response response = chain.proceed(request);
        long t2 = System.nanoTime();
        System.out.println(String.format("Received response for %s in %.1fms%n%s",
                response.request().url(), (t2 - t1) / 1e6d, response.headers()));
        return response;
    }
}

7. 服務(wù)器調(diào)用攔截器(CallServerInterceptor)

  • 位置:攔截器鏈的最后一個(gè)攔截器。
  • 作用:負(fù)責(zé)向服務(wù)器發(fā)送請(qǐng)求并接收服務(wù)器的響應(yīng)。它會(huì)將請(qǐng)求數(shù)據(jù)寫(xiě)入網(wǎng)絡(luò)連接,然后讀取服務(wù)器返回的響應(yīng)數(shù)據(jù),包括響應(yīng)頭和響應(yīng)體。
  • 源碼分析:在 intercept 方法中,會(huì)將請(qǐng)求體寫(xiě)入連接的輸出流,發(fā)送請(qǐng)求頭,然后從連接的輸入流中讀取響應(yīng)頭和響應(yīng)體,最后返回響應(yīng)對(duì)象。

3.應(yīng)用攔截器和網(wǎng)絡(luò)攔截器的區(qū)別

應(yīng)用攔截器

  • 添加方式:通過(guò) OkHttpClient.Builder().addInterceptor(Interceptor interceptor) 方法添加。
  • 執(zhí)行時(shí)機(jī):在所有網(wǎng)絡(luò)相關(guān)操作之前執(zhí)行,僅處理應(yīng)用層發(fā)起的原始請(qǐng)求。
  • 特點(diǎn): 不會(huì)受到重定向、重試等網(wǎng)絡(luò)操作的影響,每個(gè)請(qǐng)求只會(huì)經(jīng)過(guò)應(yīng)用攔截器一次。可以獲取到最原始的請(qǐng)求和最終的響應(yīng),適合進(jìn)行日志記錄、請(qǐng)求頭添加等操作。
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;
import java.io.IOException;
public class ApplicationInterceptorExample {
    public static void main(String[] args) throws IOException {
        CustomInterceptor customInterceptor = new CustomInterceptor();
        OkHttpClient client = new OkHttpClient.Builder()
               .addInterceptor(customInterceptor)
               .build();
        Request request = new Request.Builder()
               .url("https://example.com")
               .build();
        Response response = client.newCall(request).execute();
        System.out.println(response.body().string());
    }
}

網(wǎng)絡(luò)攔截器

  • 添加方式:通過(guò) OkHttpClient.Builder().addNetworkInterceptor(Interceptor interceptor) 方法添加。
  • 執(zhí)行時(shí)機(jī):在建立網(wǎng)絡(luò)連接之后、發(fā)送請(qǐng)求到服務(wù)器之前執(zhí)行,會(huì)處理所有的網(wǎng)絡(luò)請(qǐng)求,包括重定向和重試的請(qǐng)求。
  • 特點(diǎn)
    • 可以處理網(wǎng)絡(luò)層的細(xì)節(jié),例如請(qǐng)求的重試、重定向等。
    • 可能會(huì)執(zhí)行多次,因?yàn)橹囟ㄏ蚝椭卦嚂?huì)導(dǎo)致請(qǐng)求多次經(jīng)過(guò)網(wǎng)絡(luò)攔截器。
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;
import java.io.IOException;
public class NetworkInterceptorExample {
    public static void main(String[] args) throws IOException {
        CustomInterceptor customInterceptor = new CustomInterceptor();
        OkHttpClient client = new OkHttpClient.Builder()
               .addNetworkInterceptor(customInterceptor)
               .build();
        Request request = new Request.Builder()
               .url("https://example.com")
               .build();
        Response response = client.newCall(request).execute();
        System.out.println(response.body().string());
    }
}

4. 攔截器的高級(jí)應(yīng)用場(chǎng)景

緩存控制攔截器

可以創(chuàng)建一個(gè)攔截器來(lái)動(dòng)態(tài)控制緩存策略,例如根據(jù)網(wǎng)絡(luò)狀態(tài)或用戶設(shè)置來(lái)決定是否使用緩存。

import okhttp3.*;
import java.io.IOException;
public class CacheControlInterceptor implements Interceptor {
    @Override
    public Response intercept(Chain chain) throws IOException {
        Request request = chain.request();
        if (isNetworkAvailable()) {
            // 網(wǎng)絡(luò)可用時(shí),設(shè)置緩存策略為最多緩存 60 秒
            request = request.newBuilder()
                   .header("Cache-Control", "max-age=60")
                   .build();
        } else {
            // 網(wǎng)絡(luò)不可用時(shí),強(qiáng)制使用緩存
            request = request.newBuilder()
                   .header("Cache-Control", "only-if-cached")
                   .build();
        }
        return chain.proceed(request);
    }
    private boolean isNetworkAvailable() {
        // 實(shí)現(xiàn)網(wǎng)絡(luò)狀態(tài)檢查邏輯
        return true;
    }
}

超時(shí)重試攔截器

可以創(chuàng)建一個(gè)攔截器來(lái)處理請(qǐng)求超時(shí)的情況,當(dāng)請(qǐng)求超時(shí)時(shí),自動(dòng)重試一定次數(shù)。

import okhttp3.*;
import java.io.IOException;
public class RetryInterceptor implements Interceptor {
    private static final int MAX_RETRIES = 3;
    @Override
    public Response intercept(Chain chain) throws IOException {
        Request request = chain.request();
        Response response = null;
        IOException exception = null;
        for (int i = 0; i < MAX_RETRIES; i++) {
            try {
                response = chain.proceed(request);
                if (response.isSuccessful()) {
                    break;
                }
            } catch (IOException e) {
                exception = e;
            }
        }
        if (response == null) {
            throw exception;
        }
        return response;
    }
}

擴(kuò)展追問(wèn):

如何保證OKHttp 攔截器鏈中每個(gè)攔截器能按預(yù)定順序執(zhí)行

答:OKHttp 的攔截器順序就像一場(chǎng) “接力賽”:

  • 框架規(guī)定了內(nèi)置攔截器的固定跑道(重試→橋接→緩存→連接→網(wǎng)絡(luò)請(qǐng)求);
  • 用戶攔截器按類(lèi)型插入特定位置(應(yīng)用攔截器在起點(diǎn),網(wǎng)絡(luò)攔截器在連接之后);
  • chain.proceed() 是接力棒,確保每個(gè)攔截器按順序處理請(qǐng)求,響應(yīng)按逆序回流,環(huán)環(huán)相扣,不會(huì)混亂。

原理:

攔截器的 intercept 方法調(diào)用

每個(gè)攔截器都實(shí)現(xiàn)了 Interceptor 接口,該接口有一個(gè) intercept 方法。在 intercept 方法中,需要調(diào)用傳入的 Chain 對(duì)象的 proceed 方法,將請(qǐng)求傳遞給下一個(gè)攔截器。例如 BridgeInterceptor 的 intercept 方法:

@Override public Response intercept(Chain chain) throws IOException {
    Request userRequest = chain.request();
    Request.Builder requestBuilder = userRequest.newBuilder();
    // 處理請(qǐng)求頭
    RequestBody body = userRequest.body();
    if (body != null) {
        MediaType contentType = body.contentType();
        if (contentType != null) {
            requestBuilder.header("Content-Type", contentType.toString());
        }
        long contentLength = body.contentLength();
        if (contentLength != -1) {
            requestBuilder.header("Content-Length", Long.toString(contentLength));
            requestBuilder.removeHeader("Transfer-Encoding");
        } else {
            requestBuilder.header("Transfer-Encoding", "chunked");
            requestBuilder.removeHeader("Content-Length");
        }
    }
    Request networkRequest = requestBuilder.build();
    // 調(diào)用 chain.proceed 方法將請(qǐng)求傳遞給下一個(gè)攔截器
    Response networkResponse = chain.proceed(networkRequest);
    // 處理響應(yīng)
    Response.Builder responseBuilder = networkResponse.newBuilder()
           .request(userRequest);
    return responseBuilder.build();
}

在 intercept 方法中調(diào)用 chain.proceed 方法,就會(huì)觸發(fā)下一個(gè)攔截器的執(zhí)行,進(jìn)而保證攔截器鏈按順序執(zhí)行。

———————————————————————————————————————————

OkHttp 緩存機(jī)制詳解(結(jié)合 Android 面試高頻考點(diǎn))

一、緩存核心組件與配置

Cache 類(lèi)

作用:OkHttp 的緩存通過(guò) Cache 類(lèi)實(shí)現(xiàn),基于磁盤(pán)存儲(chǔ)(默認(rèn)無(wú)內(nèi)存緩存,需手動(dòng)實(shí)現(xiàn))。

初始化

File cacheDir = new File(context.getCacheDir(), "okhttp_cache");
OkHttpClient client = new OkHttpClient.Builder()
    .cache(new Cache(cacheDir, 10 * 1024 * 1024)) // 10MB 緩存大小
    .build();

面試點(diǎn):緩存目錄通常放在應(yīng)用私有目錄(如 getCacheDir()),避免權(quán)限問(wèn)題;緩存大小需根據(jù)業(yè)務(wù)場(chǎng)景合理設(shè)置,過(guò)大浪費(fèi)存儲(chǔ),過(guò)小導(dǎo)致緩存命中率低。

CacheInterceptor 攔截器

  • 位置:攔截器鏈中的第三個(gè)攔截器(位于 RetryAndFollowUpInterceptor 和 ConnectInterceptor 之間)。
  • 核心功能:處理緩存的讀取、寫(xiě)入和更新,是緩存機(jī)制的核心邏輯載體。

二、緩存策略(面試高頻考點(diǎn))

OkHttp 支持 HTTP 標(biāo)準(zhǔn)緩存策略(基于 Cache-Control 頭)和 自定義策略,通過(guò) Request 的 CacheControl 對(duì)象配置,常見(jiàn)策略:

強(qiáng)制緩存(不與服務(wù)器交互)

  • CacheControl.FORCE_CACHE:優(yōu)先使用緩存,無(wú)緩存時(shí)拋異常(需配合 max-stale 等參數(shù))。
  • 場(chǎng)景:完全離線場(chǎng)景,如兜底頁(yè)面。

緩存優(yōu)先(無(wú)有效緩存時(shí)請(qǐng)求網(wǎng)絡(luò))

  • CacheControl.cacheControl(CacheControl.Builder() .maxStale(7, TimeUnit.DAYS) // 允許緩存過(guò)期 7 天 .build())
  • 流程:先查緩存,若緩存未過(guò)期或允許 max-stale,直接返回;否則請(qǐng)求網(wǎng)絡(luò),響應(yīng)寫(xiě)入緩存。

網(wǎng)絡(luò)優(yōu)先(忽略緩存,僅存儲(chǔ)響應(yīng))

  • CacheControl.FORCE_NETWORK:直接請(qǐng)求網(wǎng)絡(luò),響應(yīng)結(jié)果寫(xiě)入緩存(適用于實(shí)時(shí)數(shù)據(jù))。

協(xié)商緩存(與服務(wù)器驗(yàn)證緩存有效性)

  • 利用 ETag/If-None-Match 或 Last-Modified/If-Modified-Since 頭,服務(wù)器返回 304 Not Modified 時(shí)復(fù)用緩存。
  • 面試點(diǎn):區(qū)分強(qiáng)制緩存(200 狀態(tài)碼,直接讀緩存)和協(xié)商緩存(304 狀態(tài)碼,需服務(wù)器驗(yàn)證)。

三、緩存存儲(chǔ)結(jié)構(gòu)與 HTTP 頭解析

緩存存儲(chǔ)格式

OkHttp 將響應(yīng)以 二進(jìn)制文件 存儲(chǔ)在磁盤(pán),文件名由 URL 的哈希值生成,包含兩部分:

  • 響應(yīng)頭文件.headers):存儲(chǔ) Cache-Control、ETag 等元信息。
  • 響應(yīng)體文件(無(wú)擴(kuò)展名):存儲(chǔ)實(shí)際數(shù)據(jù)。

關(guān)鍵 HTTP 頭字段

Cache-Control

  • max-age
  • 緩存有效期(秒),優(yōu)先級(jí)高于 Expires。
  • no-cache:需走協(xié)商緩存(驗(yàn)證有效性),no-store:禁止緩存。
  • ETag/Last-Modified:協(xié)商緩存的核心字段,OkHttp 自動(dòng)處理 If-None-Match 和 If-Modified-Since 頭。

四、緩存流程與攔截器邏輯

  • 緩存讀?。?code>CacheInterceptor 前半段)
  • 從緩存中查找與請(qǐng)求匹配的響應(yīng)(根據(jù) URL、方法、頭信息)。
  • 若存在緩存,根據(jù) Cache-Control 判定是否有效:
  • 有效:直接返回緩存(跳過(guò)網(wǎng)絡(luò)請(qǐng)求)。
  • 過(guò)期但允許 max-stale:返回緩存,同時(shí)異步更新網(wǎng)絡(luò)數(shù)據(jù)。

網(wǎng)絡(luò)請(qǐng)求與緩存寫(xiě)入(CacheInterceptor 后半段)

  • 網(wǎng)絡(luò)響應(yīng)返回后,根據(jù) Cache-Control 決定是否寫(xiě)入緩存(如 max-age > 0)。
  • 寫(xiě)入前檢查響應(yīng)狀態(tài)碼(僅 200 OK 和 304 會(huì)被緩存),并提取必要的頭信息用于后續(xù)驗(yàn)證。

五、內(nèi)存緩存與磁盤(pán)緩存(面試易混點(diǎn))

  • 磁盤(pán)緩存:OkHttp 內(nèi)置,通過(guò) Cache 類(lèi)配置,持久化存儲(chǔ),適合大文件或需離線訪問(wèn)的場(chǎng)景。
  • 內(nèi)存緩存:需手動(dòng)實(shí)現(xiàn)(如使用 LruCache),OkHttp 不默認(rèn)支持,用于加速熱數(shù)據(jù)訪問(wèn),減少磁盤(pán) IO。
  • 面試問(wèn)法:“OkHttp 有沒(méi)有內(nèi)存緩存?如何實(shí)現(xiàn)?” 答:默認(rèn)只有磁盤(pán)緩存,內(nèi)存緩存需結(jié)合 Interceptor 手動(dòng)實(shí)現(xiàn),存儲(chǔ)已處理的 Response 對(duì)象。

六、緩存失效與更新

手動(dòng)清除緩存

client.cache().delete(); // 清除所有緩存
client.cache().evictAll(); // 同上(API 差異)

策略強(qiáng)制更新

發(fā)起請(qǐng)求時(shí)添加 CacheControl.noCache(),強(qiáng)制忽略緩存,走網(wǎng)絡(luò)請(qǐng)求。

七、面試高頻問(wèn)題總結(jié)

“OkHttp 緩存策略有哪些?如何實(shí)現(xiàn)緩存優(yōu)先?”

答:支持 FORCE_CACHE(強(qiáng)制讀緩存)、FORCE_NETWORK(強(qiáng)制網(wǎng)絡(luò))、協(xié)商緩存(304)等;緩存優(yōu)先可通過(guò) maxStale 允許過(guò)期緩存,配合網(wǎng)絡(luò)請(qǐng)求更新。

“304 狀態(tài)碼在 OkHttp 緩存中如何處理?”

答:OkHttp 自動(dòng)攜帶 ETag 生成 If-None-Match 頭,服務(wù)器返回 304 時(shí),復(fù)用本地緩存響應(yīng)體,僅更新頭信息(減少流量)。

“OkHttp 緩存和瀏覽器緩存的區(qū)別?”

答:核心邏輯一致(基于 HTTP 頭),但 OkHttp 需手動(dòng)配置 Cache 實(shí)例,且默認(rèn)無(wú)內(nèi)存緩存;瀏覽器緩存由瀏覽器自動(dòng)管理。

“緩存攔截器的作用是什么?在攔截器鏈中的位置?”

答:負(fù)責(zé)緩存的讀取和寫(xiě)入,位于攔截器鏈的中間位置(處理完重試、橋接,未處理連接和網(wǎng)絡(luò)請(qǐng)求)。 總結(jié)

OkHttp 緩存機(jī)制通過(guò) 攔截器鏈 和 HTTP 標(biāo)準(zhǔn)頭 實(shí)現(xiàn)高效的網(wǎng)絡(luò)請(qǐng)求優(yōu)化,核心在于合理配置 CacheControl 策略、利用協(xié)商緩存減少服務(wù)器壓力,并結(jié)合磁盤(pán) / 內(nèi)存緩存提升性能。--

_____________________________________________________________________________

     OkHttp 的連接池復(fù)用是優(yōu)化網(wǎng)絡(luò)請(qǐng)求性能的重要手段,其核心是通過(guò)ConnectionPool管理底層 TCP 連接,避免重復(fù)建立連接的開(kāi)銷(xiāo)。 

一、OkHttp 連接池復(fù)用的核心原理

目標(biāo)
復(fù)用相同 URL、相同協(xié)議(HTTP/HTTPS)的連接,減少 TCP 三次握手、TLS 握手的耗時(shí),提升請(qǐng)求速度。

核心類(lèi):ConnectionPool

  • 作用:維護(hù)一個(gè)連接隊(duì)列,緩存未關(guān)閉的空閑連接,供后續(xù)請(qǐng)求復(fù)用。
  • 默認(rèn)配置OkHttpClient默認(rèn)創(chuàng)建):
// OkHttpClient源碼中的默認(rèn)連接池
private static final ConnectionPool DEFAULT_CONNECTION_POOL = new ConnectionPool(
    5, // 最大空閑連接數(shù)(默認(rèn)5個(gè))
    5, TimeUnit.MINUTES // 空閑連接存活時(shí)間(默認(rèn)5分鐘)
);

關(guān)鍵參數(shù)

  • maxIdleConnections:最大空閑連接數(shù),超過(guò)則清理最舊的連接。
  • keepAliveDuration:空閑連接在池中的最長(zhǎng)存活時(shí)間,超時(shí)則關(guān)閉。

連接復(fù)用條件

  • 請(qǐng)求的 URL 的hostport相同,且協(xié)議(HTTP/HTTPS)一致。
  • 連接處于 “空閑狀態(tài)”(即當(dāng)前無(wú)請(qǐng)求正在使用,但未超時(shí))。

二、如何使用連接池復(fù)用?

1. 默認(rèn)使用(無(wú)需額外配置)

OkHttpClient 默認(rèn)啟用連接池,無(wú)需手動(dòng)設(shè)置,同一OkHttpClient實(shí)例的所有請(qǐng)求共享同一個(gè)連接池:

OkHttpClient client = new OkHttpClient.Builder()
    .connectTimeout(10, TimeUnit.SECONDS)
    .build(); // 內(nèi)部使用默認(rèn)的ConnectionPool

2. 自定義連接池配置(可選)

若需調(diào)整默認(rèn)參數(shù)(如增大空閑連接數(shù)或存活時(shí)間),可通過(guò)connectionPool()方法設(shè)置:

OkHttpClient client = new OkHttpClient.Builder()
    .connectionPool(new ConnectionPool(
        10, // 最大空閑連接數(shù)設(shè)為10
        10, TimeUnit.MINUTES // 空閑連接存活時(shí)間設(shè)為10分鐘
    ))
    .build();

3. 連接池的生命周期

  • 自動(dòng)清理:OkHttp 通過(guò)后臺(tái)線程(CleanupRunnable)定時(shí)檢查(每隔 5 秒),清理超時(shí)的空閑連接。
  • 手動(dòng)清理(罕見(jiàn)場(chǎng)景):如需立即釋放資源(如應(yīng)用退出時(shí)),可調(diào)用:
client.connectionPool().evictAll(); // 清除所有連接

三、源碼級(jí)實(shí)現(xiàn)細(xì)節(jié)(面試常問(wèn))

連接獲取流程

當(dāng)發(fā)起請(qǐng)求時(shí),OkHttp 先從ConnectionPool中查找可用的空閑連接:

// RealConnectionPool.java 查找連接的核心邏輯
RealConnection get(Address address, StreamAllocation streamAllocation) {
    // 遍歷連接池中的連接,尋找匹配address且空閑的連接
    for (RealConnection connection : connections) {
        if (connection.isEligible(address, streamAllocation)) {
            streamAllocation.acquire(connection);
            return connection;
        }
    }
    return null; // 無(wú)可用連接,新建連接
}

isEligible方法判斷連接是否符合復(fù)用條件(host、port、協(xié)議一致,且未達(dá)最大請(qǐng)求數(shù))。

連接釋放與空閑標(biāo)記

請(qǐng)求完成后,連接不會(huì)立即關(guān)閉,而是標(biāo)記為 “空閑” 并放回連接池:

// RealConnection.java 釋放連接的邏輯
void release(StreamAllocation streamAllocation) {
    if (streamAllocation == null) return;
    streamAllocation.release();
    if (allocationCount > 0 || noNewStreams) {
        return; // 連接仍在使用中
    }
    // 連接變?yōu)榭臻e,加入連接池的空閑隊(duì)列
    connectionPool.put(this); 
}

清理機(jī)制

ConnectionPool通過(guò)CleanupRunnable線程定時(shí)執(zhí)行cleanup()方法,移除超時(shí)或超出最大空閑數(shù)的連接:

// ConnectionPool.java 清理邏輯
private final Runnable cleanupRunnable = () -> {
    while (true) {
        long waitNanos = cleanup(System.nanoTime()); // 執(zhí)行清理,返回下次等待時(shí)間
        if (waitNanos == -1) return; // 無(wú)需要清理的連接,退出
        if (waitNanos > 0) {
            synchronized (this) {
                try {
                    wait(waitNanos / 1000000, (int) (waitNanos % 1000000));
                } catch (InterruptedException e) {
                    return;
                }
            }
        }
    }
};

四、面試高頻問(wèn)題與解答

1. 為什么需要連接池復(fù)用?相比 HTTPURLConnection 有什么優(yōu)勢(shì)?

原因:避免重復(fù)建立 TCP 連接(三次握手)和 TLS 握手(HTTPS 場(chǎng)景),減少延遲和資源消耗。

優(yōu)勢(shì)

  • HTTPURLConnection 默認(rèn)不支持連接復(fù)用(需手動(dòng)配置HttpURLConnection.setInstanceFollowRedirects(true),且管理復(fù)雜);
  • OkHttp 的ConnectionPool自動(dòng)管理連接生命周期,線程安全,開(kāi)箱即用。

2. 如何判斷兩個(gè)請(qǐng)求是否可以復(fù)用同一個(gè)連接?

必須滿足:

  • URL 的hostport相同;
  • 協(xié)議相同(均為 HTTP 或均為 HTTPS);
  • 連接處于空閑狀態(tài)(未被占用且未超時(shí))。

3. 連接池中的連接會(huì)一直存在嗎?如何避免內(nèi)存泄漏?

不會(huì)

  • 超過(guò)maxIdleConnections的空閑連接會(huì)被清理;
  • 超過(guò)keepAliveDuration的空閑連接會(huì)被關(guān)閉;
  • 應(yīng)用退出時(shí),建議調(diào)用connectionPool.evictAll()釋放所有連接。

最佳實(shí)踐:使用單例OkHttpClient(避免創(chuàng)建多個(gè)實(shí)例導(dǎo)致多個(gè)連接池),并合理設(shè)置maxIdleConnections(通常默認(rèn)值即可)。

4. 連接池和緩存機(jī)制(CacheInterceptor)的關(guān)系是什么?

  • 連接池優(yōu)化的是 “網(wǎng)絡(luò)連接層” 的性能(減少連接建立開(kāi)銷(xiāo));
  • 緩存機(jī)制優(yōu)化的是 “應(yīng)用層” 的性能(直接返回本地緩存,避免網(wǎng)絡(luò)請(qǐng)求);
  • 兩者可同時(shí)使用,共同提升性能。

五、最佳實(shí)踐

單例模式:全局共享一個(gè)OkHttpClient實(shí)例,避免重復(fù)創(chuàng)建連接池:

public class OkHttpSingleton {
    private static OkHttpClient client;
    public static OkHttpClient getInstance() {
        if (client == null) {
            synchronized (OkHttpSingleton.class) {
                if (client == null) {
                    client = new OkHttpClient.Builder()
                        .connectionPool(new ConnectionPool(5, 5, TimeUnit.MINUTES))
                        .build();
                }
            }
        }
        return client;
    }
}
  • 結(jié)合 HTTPS 優(yōu)化:復(fù)用連接時(shí),TLS 握手僅在首次建立連接時(shí)執(zhí)行,后續(xù)請(qǐng)求直接復(fù)用已建立的加密通道。
  • 監(jiān)控與調(diào)試:通過(guò)EventListener監(jiān)聽(tīng)連接池事件(如連接創(chuàng)建、復(fù)用、釋放),排查性能問(wèn)題。

總結(jié)

OkHttp 的連接池復(fù)用通過(guò)ConnectionPool自動(dòng)管理空閑連接,顯著提升網(wǎng)絡(luò)請(qǐng)求效率。使用時(shí)無(wú)需手動(dòng)干預(yù),只需合理配置參數(shù)(或使用默認(rèn)值),并遵循單例模式共享OkHttpClient實(shí)例即可。

擴(kuò)展追問(wèn):

1. OkHttp 如何實(shí)現(xiàn)連接池復(fù)用?(高頻題)

核心回答點(diǎn):

  • ConnectionPool 組件:OkHttp 通過(guò) ConnectionPool 管理連接,默認(rèn)維護(hù) 5 個(gè)空閑連接(maxIdleConnections),存活時(shí)間 5 分鐘(keepAliveDuration)。
  • 復(fù)用邏輯:新請(qǐng)求優(yōu)先從連接池中查找匹配的空閑連接(同主機(jī)、端口、協(xié)議),避免重復(fù)創(chuàng)建 TCP 連接和 TLS 握手,減少延遲。
  • 連接回收:請(qǐng)求完成后,連接不會(huì)立即關(guān)閉,而是放入池中等待復(fù)用;若空閑時(shí)間超過(guò)閾值或連接數(shù)超過(guò)上限,通過(guò)后臺(tái)線程(cleanupRunnable)定期清理過(guò)期連接。
  • 面試加分項(xiàng):對(duì)比 HTTP/1.1 的 Connection: Keep-Alive,OkHttp 實(shí)現(xiàn)更高效,支持自動(dòng)管理連接生命周期,降低資源消耗。

2. OkHttp 緩存機(jī)制的核心策略是什么?如何配置?(必問(wèn)題)

核心回答點(diǎn):

兩層緩存

  • 內(nèi)存緩存CacheInterceptor 管理):存儲(chǔ)響應(yīng)數(shù)據(jù),快速響應(yīng)重復(fù)請(qǐng)求,減少 CPU 和內(nèi)存開(kāi)銷(xiāo)。
  • 磁盤(pán)緩存Cache 類(lèi),需手動(dòng)創(chuàng)建):持久化存儲(chǔ),應(yīng)對(duì) APP 重啟或長(zhǎng)時(shí)間未請(qǐng)求的場(chǎng)景。
  • 緩存策略:通過(guò) CacheControl 頭配置,如 FORCE_CACHE(優(yōu)先讀緩存)、FORCE_NETWORK(強(qiáng)制走網(wǎng)絡(luò))、CACHE_ELSE_NETWORK(緩存失效后走網(wǎng)絡(luò))。
  • 面試陷阱:需區(qū)分 強(qiáng)緩存304 Not Modified)和 協(xié)商緩存(服務(wù)端驗(yàn)證緩存有效性),OkHttp 內(nèi)置攔截器自動(dòng)處理緩存響應(yīng)碼。
  • 配置示例:創(chuàng)建 Cache 對(duì)象并設(shè)置大?。ㄈ?nbsp;new Cache(cacheDir, 10 * 1024 * 1024)),通過(guò) OkHttpClient.Builder().cache(cache) 綁定。

3. 攔截器鏈(Interceptor Chain)的作用是什么?自定義攔截器如何實(shí)現(xiàn)?(原理題)

核心回答點(diǎn):

  • 責(zé)任鏈模式:OkHttp 通過(guò)攔截器鏈處理請(qǐng)求和響應(yīng),包括 重試與重定向、橋接(添加請(qǐng)求頭 / 處理響應(yīng)體)、緩存連接建立、網(wǎng)絡(luò)請(qǐng)求 等內(nèi)置攔截器。
  • 執(zhí)行順序:用戶自定義攔截器 → 內(nèi)置重試攔截器 → 橋接攔截器 → 緩存攔截器 → 連接攔截器 → 網(wǎng)絡(luò)攔截器 → 調(diào)用服務(wù)器攔截器。
  • 自定義場(chǎng)景:用于添加公共請(qǐng)求頭(如 Token)、日志打印、響應(yīng)數(shù)據(jù)解析 / 修改,通過(guò)實(shí)現(xiàn) Interceptor 接口的 intercept 方法,調(diào)用 chain.proceed(request) 傳遞請(qǐng)求。
  • 面試關(guān)鍵:強(qiáng)調(diào)攔截器的 “中間件” 特性,可在不修改核心代碼的前提下擴(kuò)展功能,符合開(kāi)閉原則。

4. 同步請(qǐng)求(execute)和異步請(qǐng)求(enqueue)的區(qū)別是什么?如何實(shí)現(xiàn)線程切換?(線程題)

核心回答點(diǎn):

  • 執(zhí)行方式同步:阻塞當(dāng)前線程,在主線程調(diào)用會(huì)導(dǎo)致 ANR,需在子線程執(zhí)行,直接返回 Response
  • 異步:通過(guò) Dispatcher 調(diào)度到線程池(默認(rèn) ExecutorService),回調(diào) Callback 在子線程,需手動(dòng)通過(guò) Handler 切回主線程。
  • 線程管理Dispatcher 控制最大并發(fā)請(qǐng)求數(shù)(默認(rèn) 64 個(gè),同一主機(jī) 5 個(gè)),異步請(qǐng)求通過(guò) AsyncCall 封裝,放入隊(duì)列或直接執(zhí)行。
  • 面試陷阱:避免混淆 “異步回調(diào)是否在主線程”,OkHttp 不負(fù)責(zé)線程切換,需開(kāi)發(fā)者自行處理(如 runOnUiThread)。

5. OkHttp 相比 Volley 或 HttpURLConnection 的優(yōu)勢(shì)是什么?(對(duì)比題)

核心回答點(diǎn):

  • 性能優(yōu)化:連接池復(fù)用、緩存策略、SPDY/HTTP/2 支持(減少 TCP 連接數(shù)),網(wǎng)絡(luò)請(qǐng)求效率更高。
  • 擴(kuò)展性:攔截器機(jī)制靈活,方便添加日志、重試、加密等功能,而 Volley 更適合小量短連接請(qǐng)求。
  • 穩(wěn)定性:內(nèi)置重試機(jī)制(自動(dòng)處理連接超時(shí)、重定向),支持流式響應(yīng)處理(大文件下載),適合復(fù)雜網(wǎng)絡(luò)場(chǎng)景。
  • 面試加分:結(jié)合實(shí)際項(xiàng)目,說(shuō)明 OkHttp 在處理高并發(fā)、大文件、復(fù)雜網(wǎng)絡(luò)環(huán)境下的優(yōu)勢(shì)。

6. 如何優(yōu)化 OkHttp 的網(wǎng)絡(luò)請(qǐng)求性能?(實(shí)戰(zhàn)題)

核心回答點(diǎn):

  • 連接池調(diào)優(yōu):根據(jù)業(yè)務(wù)場(chǎng)景調(diào)整 maxIdleConnections 和 keepAliveDuration(如高頻接口增大連接數(shù))。
  • 緩存策略:合理設(shè)置緩存有效期(CacheControl.maxAge),減少無(wú)效網(wǎng)絡(luò)請(qǐng)求。
  • HTTPS 優(yōu)化:使用 CertificatePinner 固定證書(shū),避免 SSL 握手耗時(shí);啟用 HTTP/2(需服務(wù)端支持)。
  • 并發(fā)控制:通過(guò) Dispatcher.setMaxRequests 和 setMaxRequestsPerHost 限制并發(fā),避免資源耗盡。

到此這篇關(guān)于Android學(xué)習(xí)總結(jié)之OKHttp攔截器和緩存的文章就介紹到這了,更多相關(guān)Android OKHttp攔截器內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • android內(nèi)存及內(nèi)存溢出分析詳解

    android內(nèi)存及內(nèi)存溢出分析詳解

    這篇文章詳細(xì)介紹了android內(nèi)存及內(nèi)存溢出的問(wèn)題,有需要的朋友可以參考一下
    2013-09-09
  • Android畢業(yè)設(shè)計(jì)記事本APP

    Android畢業(yè)設(shè)計(jì)記事本APP

    這篇文章主要介紹了一個(gè)Android畢業(yè)設(shè)計(jì)記事本APP,它是一款輕量級(jí)的便簽工具,使用Java語(yǔ)言開(kāi)發(fā),風(fēng)格簡(jiǎn)練,可實(shí)現(xiàn)便簽的添加、刪除、修改、查看功能
    2021-08-08
  • Android自定義View仿大眾點(diǎn)評(píng)星星評(píng)分控件

    Android自定義View仿大眾點(diǎn)評(píng)星星評(píng)分控件

    這篇文章主要為大家詳細(xì)介紹了Android自定義View仿大眾點(diǎn)評(píng)星星評(píng)分控件,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2021-03-03
  • Android 文件夾顯示紅色嘆號(hào)的解決方法(必看)

    Android 文件夾顯示紅色嘆號(hào)的解決方法(必看)

    下面小編就為大家?guī)?lái)一篇Android 文件夾顯示紅色嘆號(hào)的解決方法(必看)。小編覺(jué)得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧
    2017-04-04
  • Android單選按鈕RadioButton的使用詳解

    Android單選按鈕RadioButton的使用詳解

    今天小編就為大家分享一篇關(guān)于Android單選按鈕RadioButton的使用詳解,小編覺(jué)得內(nèi)容挺不錯(cuò)的,現(xiàn)在分享給大家,具有很好的參考價(jià)值,需要的朋友一起跟隨小編來(lái)看看吧
    2019-03-03
  • Android下如何使用百度地圖sdk

    Android下如何使用百度地圖sdk

    百度地圖 Android SDK是一套基于Android 2.1(v1.3.5及以前版本支持android 1.5以上系統(tǒng))及以上版本設(shè)備的應(yīng)用程序接口
    2013-07-07
  • Android實(shí)現(xiàn)滑塊拼圖驗(yàn)證碼功能

    Android實(shí)現(xiàn)滑塊拼圖驗(yàn)證碼功能

    這篇文章主要介紹了Android實(shí)現(xiàn)滑塊拼圖驗(yàn)證碼功能,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2021-02-02
  • Android編程處理窗口控件大小,形狀,像素等UI元素工具類(lèi)

    Android編程處理窗口控件大小,形狀,像素等UI元素工具類(lèi)

    這篇文章主要介紹了Android編程處理窗口控件大小,形狀,像素等UI元素工具類(lèi),可實(shí)現(xiàn)像素與dp的轉(zhuǎn)換、窗口寬度設(shè)置、彈出窗口中l(wèi)istview高度設(shè)置等功能,需要的朋友可以參考下
    2017-12-12
  • Android獲取窗體信息的Util方法

    Android獲取窗體信息的Util方法

    這篇文章主要介紹了Android獲取窗體信息的Util方法的相關(guān)資料,需要的朋友可以參考下
    2015-07-07
  • Android設(shè)計(jì)模式之單例模式解析

    Android設(shè)計(jì)模式之單例模式解析

    這篇文章主要為大家詳細(xì)介紹了Android設(shè)計(jì)模式之單例模式的相關(guān)資料,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2017-10-10

最新評(píng)論