OkHttp 3.0源碼解析:從設計理念到核心實現

本文通過深入分析OkHttp 3.0源碼,揭示其高效HTTP客戶端的實現奧秘,包含核心設計理念、關鍵組件解析、完整工作流程及實用技巧。

一、引言:為什么選擇OkHttp?

在Android和Java生態中,OkHttp已成為HTTP客戶端的標準選擇。相較于其他HTTP庫,OkHttp具有以下優勢:

特性OkHttpHttpURLConnectionApache HttpClient
連接池? 自動復用連接? 需手動管理? 支持
攔截器? 強大可擴展? 不支持?? 有限支持
HTTP/2? 完整支持?? Android 5+支持? 不支持
透明壓縮? 自動處理? 需手動實現? 需手動實現
緩存機制? 符合RFC規范?? 基礎支持? 支持
API設計? 簡潔現代?? 冗長復雜?? 冗長復雜

二、環境搭建與基礎使用

1. 添加依賴

dependencies {implementation 'com.squareup.okhttp3:okhttp:3.14.9' // 3.x最終穩定版
}

2. 同步請求示例

// 1. 創建客戶端(推薦復用實例)
OkHttpClient client = new OkHttpClient();// 2. 構建請求
Request request = new Request.Builder().url("https://api.example.com/data").build();// 3. 執行請求并處理響應
try (Response response = client.newCall(request).execute()) {if (!response.isSuccessful()) {throw new IOException("Unexpected code: " + response);}// 獲取響應頭Headers headers = response.headers();for (int i = 0; i < headers.size(); i++) {System.out.println(headers.name(i) + ": " + headers.value(i));}// 獲取響應體String body = response.body().string();System.out.println(body);
}

3. 異步請求示例

// 1. 創建請求
Request request = new Request.Builder().url("https://api.example.com/async").build();// 2. 異步執行
client.newCall(request).enqueue(new Callback() {@Overridepublic void onFailure(Call call, IOException e) {e.printStackTrace();}@Overridepublic void onResponse(Call call, Response response) throws IOException {try (ResponseBody body = response.body()) {if (!response.isSuccessful()) {throw new IOException("Unexpected code: " + response);}System.out.println(body.string());}}
});

三、核心設計理念解析

1. 分層架構設計

OkHttp采用清晰的分層結構,各層職責分明:

+---------------------+
|      Application    |  ← 用戶代碼
+---------------------+
|      OkHttpClient   |  ← 配置中心
+---------------------+
|      Interceptors   |  ← 功能擴展點(核心!)
+---------------------+
|       Connection    |  ← 連接管理層
+---------------------+
|   Network Protocol  |  ← HTTP/1.1或HTTP/2實現
+---------------------+
|        Socket       |  ← 底層I/O
+---------------------+

2. 攔截器機制(責任鏈模式)

攔截器是OkHttp最核心的創新,將HTTP請求處理分解為可插拔的步驟:

public interface Interceptor {// 關鍵方法:處理請求并返回響應Response intercept(Chain chain) throws IOException;interface Chain {Request request();Response proceed(Request request) throws IOException;}
}

OkHttp 是一個廣受歡迎的 Java/Android HTTP 客戶端庫,以其高效、靈活和易用性著稱。分析 OkHttp 3.0 源碼(雖然 3.0 已是較老版本,但其核心架構與后續版本基本一致)是理解其強大功能的關鍵。以下是對核心組件和流程的深入分析:

主要組件分析

  1. OkHttpClient:

    • 角色: 工廠和配置中心。通常作為單例使用。
    • 職責:
      • 持有所有全局配置:連接超時、讀取超時、寫入超時、攔截器列表 (interceptors, networkInterceptors)、連接池 (connectionPool)、代理、緩存 (cache)、認證器 (authenticator)、Cookie 管理器 (cookieJar)、DNS 解析器 (dns)、是否遵循重定向、是否重試連接失敗等。
      • 通過 newCall(Request request) 方法,根據給定的 Request 創建一個 Call 對象。這是發起請求的入口。
  2. Request:

    • 角色: 描述一個 HTTP 請求。
    • 內容: URL (HttpUrl)、方法 (GET, POST 等)、Headers (Headers)、Body (RequestBody)、標簽 (Tag) 等。
    • 構建: 通常使用 Request.Builder 模式構建。
  3. Call:

    • 角色: 表示一個準備執行的請求。它是連接 Request 和最終 Response 的橋梁。一個 Call 實例代表一次請求嘗試(可能包含重試和重定向)。
    • 實現: RealCall (OkHttp 內部的真實實現)。
    • 關鍵方法:
      • execute(): 同步執行請求,阻塞當前線程直到響應返回(或出錯),返回 Response
      • enqueue(Callback responseCallback): 異步執行請求。將請求放入隊列,在后臺線程執行,結果通過 Callback 回調到調用者線程(通常是主線程)。
      • cancel(): 取消請求(如果可能)。
    • 核心流程 (以 RealCall.execute() 為例):
      1. 檢查是否已執行或已取消。
      2. 調用 client.dispatcher().executed(this) 通知分發器這是一個同步請求(用于統計和取消)。
      3. 關鍵: 調用 getResponseWithInterceptorChain() 方法。這是攔截器鏈執行的入口點。
  4. Dispatcher:

    • 角色: 請求的分發管理器,主要用于管理異步請求的線程池和運行狀態。
    • 職責:
      • 維護兩個線程池:executorService (用于執行異步網絡請求) 和 executorServiceOrNull (內部實現細節)。
      • 維護三個隊列:
        • readyAsyncCalls: 等待執行的異步請求隊列(當正在運行的請求達到最大值時)。
        • runningAsyncCalls: 正在運行的異步請求隊列。
        • runningSyncCalls: 正在運行的同步請求隊列(僅用于統計和取消)。
      • 控制并發請求的最大數量(默認為 64)和單個主機最大并發數(默認為 5)。
      • 當異步請求完成或條件變化時,將 readyAsyncCalls 中的請求移入 runningAsyncCalls 并執行。
  5. Interceptor.ChainRealInterceptorChain:

    • Interceptor 接口: 定義單個攔截器的行為,核心方法是 Response intercept(Chain chain) throws IOException
    • Chain 接口: 代表攔截器鏈中的當前位置,提供:
      • request(): 獲取當前請求。
      • proceed(Request request): 核心! 將(可能被當前攔截器修改后的)請求傳遞給鏈中的下一個攔截器,并接收其返回的響應。
    • RealInterceptorChain: Chain 的具體實現。它持有:
      • 當前攔截器列表。
      • 當前攔截器的索引 index
      • 原始的 Request
      • 其他必要上下文(如 StreamAllocation, HttpCodec, RealConnection - 這些通常在連接攔截器中創建并傳遞)。
    • 鏈的執行 (RealInterceptorChain.proceed()):
      1. 檢查索引是否越界。
      2. 獲取下一個攔截器 (interceptors.get(index))。
      3. 創建新的 RealInterceptorChain 實例,index 加 1,其他上下文復制或傳遞。
      4. 調用 nextInterceptor.intercept(nextChain)
      5. 返回 intercept 方法返回的 Response
      • 本質: 這是一個遞歸調用過程,每個攔截器在調用 chain.proceed(request) 時,就將控制權交給了下一個攔截器。當最后一個攔截器(通常是 CallServerInterceptor)執行完畢并返回響應時,這個響應會逐層向上(逆序)返回到前面的攔截器,每個攔截器都有機會修改最終的響應。
  6. 內置攔截器 (按鏈中順序):

    • RetryAndFollowUpInterceptor: 處理失敗重試和 HTTP 重定向 (3xx 響應碼)。它會根據響應創建新的請求并重新發起調用(通過創建新的 RealInterceptorChain)。
    • BridgeInterceptor: 橋接應用層和網絡層。
      • 請求方向: 添加必要的默認 Headers (如 User-Agent, Host, Connection: keep-alive, Accept-Encoding: gzip)。如果請求有 Body,添加 Content-TypeContent-Length。處理 Cookie。
      • 響應方向: 處理 Content-Encoding: gzip,自動解壓縮響應體。保存 Cookie。
    • CacheInterceptor: 處理 HTTP 緩存。
      • 根據請求和緩存策略 (CacheControl) 查找可用的緩存響應。
      • 如果找到有效緩存且請求滿足條件 (如 if-Modified-Since, if-None-Match),可能直接返回緩存或發送條件請求。
      • 處理網絡響應的緩存寫入(如果響應可緩存)。
    • ConnectInterceptor: 建立到目標服務器的連接。
      • 使用 StreamAllocation 對象(由 RetryAndFollowUpInterceptor 創建)從連接池 (ConnectionPool) 獲取或新建一個到目標地址的 RealConnection
      • 建立 TCP/TLS 連接(如果必要),進行協議協商 (HTTP/1.1, HTTP/2)。
      • 獲取一個 HttpCodec 對象 (用于實際讀寫 HTTP 數據的抽象,如 Http1CodecHttp2Codec)。
      • 將這個 RealConnectionHttpCodec 傳遞給后續的攔截器鏈。
    • CallServerInterceptor: 鏈的末端,執行實際的網絡 I/O。
      • 使用 HttpCodec 將請求頭和請求體寫入網絡。
      • 讀取響應頭和響應體。
      • 構造最終的 Response 對象并返回。
      • 這是唯一真正進行網絡讀寫的攔截器。
  7. ConnectionPool:

    • 角色: 管理空閑的 HTTP 和 HTTP/2 連接以供重用。
    • 實現: 內部使用一個線程池 (Executor) 運行清理任務。
    • 核心方法:
      • put(RealConnection connection): 將空閑連接放入池中。
      • get(Address address, StreamAllocation streamAllocation): 根據地址 (Address - 包含 URL、代理、SSL 配置等) 查找匹配的空閑連接。找到后關聯到 StreamAllocation
    • 清理機制:
      • 最大空閑連接數: 默認 5 個。
      • 最長空閑時間: 默認 5 分鐘。清理線程定期掃描,移除空閑時間超過限制或空閑連接數超過限制的連接。
      • 對于 HTTP/2 連接,即使空閑連接數為 0,只要其關聯的 StreamAllocation 計數為 0,也會被清理。
  8. RealConnection:

    • 角色: 表示一個到目標服務器的物理 Socket 連接。
    • 內容:
      • Socket / SSLSocket
      • 底層輸入/輸出流 (Source, Sink)。
      • 握手信息 (Handshake)。
      • 使用的協議 (Protocol: HTTP/1.1, HTTP/2, SPDY)。
      • HTTP/2 相關的 Http2Connection 對象(如果使用 HTTP/2)。
      • 一個 List<Reference<StreamAllocation>> (allocations),記錄當前使用此連接的活躍請求 (StreamAllocation) 的弱引用列表。
    • 連接建立流程 (connect 方法):
      1. 解析 IP 地址(可能涉及 DNS)。
      2. 建立 TCP Socket 連接。
      3. 如果需要 TLS (HTTPS),進行 SSL/TLS 握手 (SSLSocket)。
      4. 如果是 HTTP/2,進行協議協商 (ALPN 或 NPN),建立 Http2Connection
      5. 將連接標記為成功。
  9. StreamAllocation:

    • 角色: 協調請求流 (Call)、連接 (Connection) 和流 (Stream / HttpCodec) 之間的關系。一個 Call 對應一個 StreamAllocation(即使在重試/重定向過程中)。
    • 職責:
      • 通過 ConnectionPool 查找或創建 RealConnection
      • 在找到的 RealConnection 上創建 HttpCodec (通過 newCodec 方法)。
      • 跟蹤關聯的 RealConnectionHttpCodec
      • 管理連接的生命周期引用計數(通過 acquirerelease 方法,更新 RealConnection.allocations 列表)。當計數降為 0 且連接空閑時,連接可能被放回連接池或關閉。
      • 處理連接失敗后的清理和重試邏輯(與 RetryAndFollowUpInterceptor 協作)。
  10. HttpCodec:

    • 角色: 抽象層,定義了讀寫 HTTP 請求和響應消息的接口。
    • 實現:
      • Http1Codec: 處理 HTTP/1.1 協議。封裝了 BufferedSourceBufferedSink,實現請求行、狀態行、頭部的讀寫以及 body 的流式讀寫。
      • Http2Codec: 處理 HTTP/2 協議。將 HTTP 語義映射到 HTTP/2 流。利用 Http2Connection 創建流 (FramingSource, FramingSink),讀寫幀數據。
  11. Response:

    • 角色: 表示 HTTP 響應。
    • 內容: 協議 (Protocol)、狀態碼 (int)、狀態信息 (String)、Headers (Headers)、響應體 (ResponseBody)、網絡響應 (networkResponse - 用于重定向/緩存)、緩存響應 (cacheResponse)、請求 (Request)、握手信息 (Handshake) 等。
    • ResponseBody:
      • 封裝響應體內容。
      • 提供 source() 方法獲取 BufferedSource 進行流式讀取。
      • 提供 bytes(), string(), charStream(), byteStream() 等方法方便讀取整個內容(注意大響應可能導致 OOM)。
      • 自動處理 GZIP 解壓縮(如果響應頭包含 Content-Encoding: gzipBridgeInterceptor 已處理)。

核心流程總結 (同步請求)

  1. 創建請求: Request request = new Request.Builder().url(...).build();
  2. 創建調用: Call call = okHttpClient.newCall(request);
  3. 執行調用: Response response = call.execute();
  4. 分發器登記: Dispatcher 記錄此同步調用 (runningSyncCalls.add(call))。
  5. 啟動攔截器鏈: RealCall.getResponseWithInterceptorChain()
    • 創建初始的攔截器列表 (包含應用攔截器、內置攔截器、網絡攔截器)。
    • 創建初始的 RealInterceptorChain (index=0)。
    • 調用 chain.proceed(initialRequest)
  6. 攔截器鏈逐級執行:
    • 每個攔截器接收 Request,可以選擇修改它,然后調用 chain.proceed(request) 交給下一個攔截器。
    • RetryAndFollowUpInterceptor: 處理重試/重定向(可能創建新鏈)。
    • BridgeInterceptor: 添加請求頭、處理響應 GZIP。
    • CacheInterceptor: 檢查緩存,可能直接返回緩存響應或發出條件請求。
    • ConnectInterceptor: 通過 StreamAllocationConnectionPool 獲取/創建 RealConnectionHttpCodec,傳遞給下一級。
    • CallServerInterceptor: 使用 HttpCodec 發送請求數據,接收響應數據,構建 Response 對象。
  7. 響應逐級返回: Response 對象從 CallServerInterceptor 開始,逆序向上返回,經過每個攔截器(攔截器有機會修改響應)。
  8. 最終響應返回: 最外層的 getResponseWithInterceptorChain() 返回最終的 Response
  9. 分發器清理: Dispatcher 移除已完成的同步調用 (runningSyncCalls.remove(call))。
  10. 資源處理: 使用者讀取 ResponseBody 后,需要關閉它 (response.close()) 或消費完所有內容以釋放底層資源(連接引用計數減少,可能回池或關閉)。StreamAllocationrelease 方法會被調用。

關鍵點理解

  • 攔截器鏈是靈魂: 理解 Chain.proceed() 的遞歸/責任鏈調用機制是理解 OkHttp 擴展性和功能模塊化的核心。
  • 連接復用是性能關鍵: ConnectionPoolStreamAllocation 協同工作,通過復用 TCP 連接大幅減少延遲。
  • 分層抽象: Call -> StreamAllocation -> RealConnection/HttpCodec 的分層管理清晰隔離了請求邏輯、連接管理和協議實現。
  • 資源管理: 正確關閉 ResponseBody 至關重要,以確保連接能被及時回收。StreamAllocation 的引用計數機制是連接生命周期管理的核心。

四、核心組件源碼深度解析

1. 攔截器鏈執行流程

// RealCall.java
Response getResponseWithInterceptorChain() throws IOException {// 構建完整攔截器鏈(按順序)List<Interceptor> interceptors = new ArrayList<>();interceptors.addAll(client.interceptors());          // 應用攔截器interceptors.add(retryAndFollowUpInterceptor);      // 重試攔截器interceptors.add(new BridgeInterceptor(...));       // 橋接攔截器interceptors.add(new CacheInterceptor(...));        // 緩存攔截器interceptors.add(new ConnectInterceptor(...));      // 連接攔截器interceptors.addAll(client.networkInterceptors());  // 網絡攔截器interceptors.add(new CallServerInterceptor(...));   // 服務調用攔截器// 創建初始責任鏈Interceptor.Chain chain = new RealInterceptorChain(interceptors, null, null, null, 0, originalRequest);// 啟動攔截器鏈return chain.proceed(originalRequest);
}

2. 連接復用機制(關鍵源碼)

// ConnectionPool.java
public final class ConnectionPool {// 空閑連接的最大數量private final int maxIdleConnections;// 連接的最大空閑時間(秒)private final long keepAliveDurationNs;// 連接池實際存儲private final Deque<RealConnection> connections = new ArrayDeque<>();// 清理任務private Runnable cleanupRunnable = new Runnable() {@Override public void run() {while (true) {// 計算下次清理等待時間long waitNanos = cleanup(System.nanoTime());if (waitNanos == -1) return;if (waitNanos > 0) {synchronized (ConnectionPool.this) {try {// 等待指定時間或被喚醒ConnectionPool.this.wait(waitNanos);} catch (InterruptedException ignored) {}}}}}};// 獲取可用連接RealConnection get(Address address, StreamAllocation streamAllocation) {for (RealConnection connection : connections) {// 1. 檢查連接是否可用// 2. 檢查地址是否匹配// 3. 檢查協議兼容性if (connection.isEligible(address)) {streamAllocation.acquire(connection);return connection;}}return null;}
}

3. HTTP/2多路復用實現

// Http2Codec.java
public final class Http2Codec implements HttpCodec {@Override public void writeRequestHeaders(Request request) throws IOException {// 創建HTTP/2流stream = http2Connection.newStream(requestHeaders, hasRequestBody);// 發送請求頭幀stream.getSink().headers(requestHeaders);}@Override public Response.Builder readResponseHeaders() throws IOException {// 讀取響應頭幀Headers headers = stream.takeHeaders();return new Response.Builder().protocol(Protocol.HTTP_2).code(parseStatus(headers.get(":status"))).message("").headers(headers);}
}

五、關鍵流程剖析

1. 完整請求生命周期

應用程序 分發器 攔截器鏈 服務器 RetryInterceptor BridgeInterceptor CacheInterceptor ConnectInterceptor CallServerInterceptor newCall(request) execute() 重試邏輯 添加頭信息 緩存檢查 獲取連接 網絡I/O loop [攔截器處理] 發送請求 返回響應 返回Response 返回結果 應用程序 分發器 攔截器鏈 服務器 RetryInterceptor BridgeInterceptor CacheInterceptor ConnectInterceptor CallServerInterceptor

2. 連接復用流程

請求發起
連接池中
是否有可用連接?
復用現有連接
創建新連接
執行TCP握手
是否為HTTPS?
執行TLS握手
發送HTTP請求
接收響應
連接是否
可復用?
歸還連接池
關閉連接

六、高級特性實現

1. 自定義攔截器示例

public class LoggingInterceptor implements Interceptor {@Overridepublic Response intercept(Chain chain) throws IOException {Request request = chain.request();// 1. 請求前記錄long startNs = System.nanoTime();logger.info(String.format("Sending request %s%n%s",request.url(), request.headers()));// 2. 繼續處理請求Response response = chain.proceed(request);// 3. 響應后記錄long tookMs = TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - startNs);ResponseBody body = response.body();logger.info(String.format("Received response in %dms%n%s",tookMs, response.headers()));return response;}
}// 使用自定義攔截器
OkHttpClient client = new OkHttpClient.Builder().addInterceptor(new LoggingInterceptor()).build();

2. 連接調優參數

// 創建高性能調優的客戶端
OkHttpClient client = new OkHttpClient.Builder().connectionPool(new ConnectionPool(100,                 // 最大空閑連接數5, TimeUnit.MINUTES  // 連接存活時間)).connectTimeout(10, TimeUnit.SECONDS)  // 連接超時.readTimeout(30, TimeUnit.SECONDS)     // 讀取超時.writeTimeout(30, TimeUnit.SECONDS)    // 寫入超時.retryOnConnectionFailure(true)        // 自動重試.protocols(Arrays.asList(Protocol.HTTP_2, Protocol.HTTP_1_1)) // 協議優先級.build();

七、關鍵點總結

  1. 攔截器鏈是核心機制

    • 采用責任鏈模式處理請求
    • 支持自定義攔截器擴展功能
    • 內置攔截器各司其職(重試、橋接、緩存、連接、網絡)
  2. 連接池提升性能

    • 默認最大空閑連接數:5
    • 默認連接存活時間:5分鐘
    • 支持HTTP/1.1 Keep-Alive和HTTP/2多路復用
  3. 高效的緩存策略

    • 遵循RFC 7234規范
    • 支持磁盤緩存(默認10MB)
    • 自動處理緩存驗證(If-Modified-Since等)
  4. 智能的錯誤恢復

    • 自動重試連接失敗
    • 自動處理重定向(最多20次)
    • 自動處理身份驗證挑戰
  5. 協議支持策略

    • 自動協商最佳協議(HTTP/2優先)
    • 透明支持TLS(SNI和ALPN擴展)
    • 自動回退到HTTP/1.1

八、性能優化建議

  1. 客戶端復用

    // 錯誤做法:每次請求創建新客戶端
    for (int i = 0; i < 100; i++) {OkHttpClient client = new OkHttpClient(); // 創建100個客戶端!client.newCall(request).execute();
    }// 正確做法:復用客戶端實例
    OkHttpClient client = new OkHttpClient(); // 單例
    for (int i = 0; i < 100; i++) {client.newCall(request).execute(); // 復用連接池
    }
    
  2. 響應體及時關閉

    // 危險做法:可能造成連接泄漏
    Response response = client.newCall(request).execute();
    String result = response.body().string();
    // 忘記關閉response!// 推薦做法1:try-with-resources
    try (Response response = client.newCall(request).execute()) {String result = response.body().string();
    }// 推薦做法2:手動關閉
    Response response = client.newCall(request).execute();
    try {String result = response.body().string();
    } finally {response.close(); // 確保關閉
    }
    
  3. 合理設置超時時間

    new OkHttpClient.Builder().connectTimeout(15, TimeUnit.SECONDS)  // 連接超時.readTimeout(30, TimeUnit.SECONDS)     // 讀取超時.writeTimeout(30, TimeUnit.SECONDS)    // 寫入超時.callTimeout(60, TimeUnit.SECONDS)     // 整個調用超時.build();
    

九、結語

分析建議

  1. 從入口開始: OkHttpClient.newCall().execute()enqueue() -> RealCall
  2. 深入攔截器鏈: 重點追蹤 getResponseWithInterceptorChain() 方法,單步調試每個內置攔截器的 intercept() 方法,觀察 Chain.proceed() 的調用和返回。理解每個攔截器的職責。
  3. 理解連接獲取:ConnectInterceptor 中,跟蹤 StreamAllocation.connection() -> ConnectionPool.get() 以及新建連接的流程 (RealConnection.connect())。
  4. 跟蹤網絡讀寫:CallServerInterceptor 中,看 HttpCodec (特別是 Http1Codec) 如何讀寫請求行、頭部、body 和響應行、頭部、body。
  5. 觀察緩存:CacheInterceptor 中,設置緩存目錄后,觀察緩存查找 (Cache.get()) 和存儲 (Cache.put()) 的觸發條件。
  6. 查看連接池清理: 查看 ConnectionPoolexecutor 運行的清理任務 (cleanupRunnable),理解其清理邏輯。

通過深入分析OkHttp 3.0源碼,我們可以清晰地看到其卓越設計的實現細節:

  1. 攔截器鏈作為核心架構,實現了功能模塊的高度解耦和靈活擴展
  2. 連接池機制通過TCP連接復用,大幅提升網絡性能
  3. 分層設計使得各組件職責清晰,便于維護和擴展
  4. 嚴謹的資源管理確保系統穩定性和可靠性

OkHttp不僅是一個功能強大的HTTP客戶端,其設計理念和實現方式更值得開發者深入學習。掌握這些底層原理,將有助于我們編寫更高效、穩定的網絡請求代碼,并在復雜場景下進行有效的問題診斷和性能優化。

源碼學習建議:從RealCall.execute()入口開始,逐步跟蹤攔截器鏈的執行過程,重點關注ConnectInterceptor和CallServerInterceptor的實現細節,這是理解OkHttp網絡處理機制的關鍵所在。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/diannao/86544.shtml
繁體地址,請注明出處:http://hk.pswp.cn/diannao/86544.shtml
英文地址,請注明出處:http://en.pswp.cn/diannao/86544.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

洛谷P12170 [藍橋杯 2025 省 Python B] 攻擊次數

題目傳送門 思路 首先定義一個數 n n n ,初值為 2025 2025 2025,從第一回合開始,三個英雄持續攻擊,攻擊方式為: 第一個英雄: 每回合攻擊 5 5 5

百度之星2021——BD202104 萌新

輸入格式&#xff1a; 本題有多組測試數據。 第一行一個數 T (1 ≤ T ≤ 1000) 表示一共有 T 組數據。對于每一組數據&#xff0c;輸入一行兩個數 a,b (1 ≤ a,b ≤ 1000000000)。 輸出格式&#xff1a; 對每組數據&#xff0c;輸出一行兩個數分別表示最小與最大的 c&#xff0…

R語言ICU患者死亡率預測實戰

【圖書推薦】《R語言醫學數據分析實踐》-CSDN博客 《R語言醫學數據分析實踐 李丹 宋立桓 蔡偉祺 清華大學出版社9787302673484》【摘要 書評 試讀】- 京東圖書 (jd.com) 預測ICU患者死亡率對比較藥物的療效、比較護理的有效性、比較手術的有效性有重要意義&#xff0c;利用機…

leetcode240-搜索二維矩陣

leetcode 240 思路 1. 矩陣特性 首先&#xff0c;利用矩陣的特性是解題的關鍵&#xff1a; 每行的元素遞增每列的元素遞增 這意味著&#xff0c;如果我們在矩陣中從右上角或左下角開始搜索&#xff0c;可以有效縮小搜索范圍 2. 從右上角開始搜索 將搜索的起點定位在矩陣…

Linux相關概念和易錯知識點(42)(TCP的連接管理、可靠性、面臨復雜網絡的處理)

目錄 1.TCP的連接管理機制&#xff08;1&#xff09;三次握手①握手過程②對握手過程的理解 &#xff08;2&#xff09;四次揮手&#xff08;3&#xff09;握手和揮手的觸發&#xff08;4&#xff09;狀態切換①揮手過程中狀態的切換②握手過程中狀態的切換 2.TCP的可靠性&…

【web應用】若依框架:若依框架中的頁面跳轉簡介

文章目錄 ?前言1. 后端控制器跳轉2. 前端路由跳轉3. 菜單配置跳轉4. 權限控制跳轉5. 常見跳轉路徑 ?一、主目錄頁面跳轉?二、菜單目錄跳轉?總結 標題詳情作者JosieBook頭銜CSDN博客專家資格、阿里云社區專家博主、軟件設計工程師博客內容開源、框架、軟件工程、全棧&#x…

MS9292+MS9332 HD/DVI轉VGA轉換器+HD環出帶音頻

MS9292MS9332是一款HD/DVI轉VGA轉換器HD環出帶音頻的視頻轉換方案。支持HD/DVI輸入&#xff0c;VGA輸出和HD環出&#xff0c;HD/DVI輸入最高支持1920120060Hz&#xff0c;VGA輸出最高支持1920120060Hz&#xff0c;HD環出最高支持4K30Hz。該方案已實現量產&#xff0c;并提供完善…

C++初階-list的模擬實現(難度較高)

1.list&#xff08;容器&#xff0c;不是類&#xff09;模擬實現基本結構 這個實現是和源碼的實現不同的&#xff0c;不要進行強制類型轉換了&#xff0c;很麻煩。我們把定義放在.h文件中&#xff0c;在.cpp中放測試代碼。 注&#xff1a;這個基本結構之后會改變&#xff0c;…

【ROS】Nav2源碼之nav2_behavior_tree-行為樹節點列表

1、行為樹節點分類 在 Nav2(Navigation2)的行為樹框架中,行為樹節點插件按照功能分為 Action(動作節點)、Condition(條件節點)、Control(控制節點) 和 Decorator(裝飾節點) 四類。 1.1 動作節點 Action 執行具體的機器人操作或任務,直接與硬件、傳感器或外部系統…

【iOS】cell的復用以及自定義cell

【iOS】cell的復用以及自定義cell 文章目錄 【iOS】cell的復用以及自定義cell前言cell的復用手動&#xff08;非注冊&#xff09;自動&#xff08;注冊&#xff09; 自定義cell 前言 cell的復用及自定義cell是UITableView或UICollectionView的一個重要優化機制,當用戶滾動視圖…

深度學習之模型壓縮三駕馬車:基于ResNet18的模型剪枝實戰(2)

前言 《深度學習之模型壓縮三駕馬車&#xff1a;基于ResNet18的模型剪枝實戰&#xff08;1&#xff09;》里面我只是提到了對conv1層進行剪枝&#xff0c;只是為了驗證這個剪枝的整個過程&#xff0c;但是后面也有提到&#xff1a;僅裁剪 conv1層的影響極大&#xff0c;原因如…

傳輸層協議:UDP

目錄 1、概念 2、報文結構 3、核心特性 3.1 無連接 3.2 不可靠交付 3.3 面向數據報 3.4 輕量級&高效 3.5 支持廣播和組播 4、典型應用場景 5、優缺點分析 6、與TCP的區別 1、概念 UDP&#xff08;User Datagram Protocol&#xff0c;用戶數據報協議&#xff09…

JVM虛擬機:內存結構、垃圾回收、性能優化

1、JVM虛擬機的簡介 Java 虛擬機(Java Virtual Machine 簡稱:JVM)是運行所有 Java 程序的抽象計算機,是 Java 語言的運行環境,實現了 Java 程序的跨平臺特性。JVM 屏蔽了與具體操作系統平臺相關的信息,使得 Java 程序只需生成在 JVM 上運行的目標代碼(字節碼),就可以…

c++ 面試題(1)-----深度優先搜索(DFS)實現

操作系統&#xff1a;ubuntu22.04 IDE:Visual Studio Code 編程語言&#xff1a;C11 題目描述 地上有一個 m 行 n 列的方格&#xff0c;從坐標 [0,0] 起始。一個機器人可以從某一格移動到上下左右四個格子&#xff0c;但不能進入行坐標和列坐標的數位之和大于 k 的格子。 例…

【匯編逆向系列】七、函數調用包含多個參數之浮點型- XMM0-3寄存器

目錄 1. 匯編代碼 1.1 debug編譯 1.2 release編譯 2. 匯編分析 2.1 浮點參數傳遞規則 2.2 棧幀rsp的變化時序 2.3 參數的訪問邏輯 2.4 返回值XMM0寄存器 3. 匯編轉化 3.1 Debug編譯 3.2 Release 編譯 3.3 C語言轉化 1. 匯編代碼 上一節介紹了整型的函數傳參&#x…

華為云Flexus+DeepSeek征文 | 從零到一:用Flexus云服務打造低延遲聯網搜索Agent

作者簡介 我是摘星&#xff0c;一名專注于云計算和AI技術的開發者。本次通過華為云MaaS平臺體驗DeepSeek系列模型&#xff0c;將實際使用經驗分享給大家&#xff0c;希望能幫助開發者快速掌握華為云AI服務的核心能力。 目錄 作者簡介 前言 1. 項目背景與技術選型 1.1 項目…

【多智能體】受木偶戲啟發實現多智能體協作編排

&#x1f60a;你好&#xff0c;我是小航&#xff0c;一個正在變禿、變強的文藝傾年。 &#x1f514;本專欄《人工智能》旨在記錄最新的科研前沿&#xff0c;包括大模型、具身智能、智能體等相關領域&#xff0c;期待與你一同探索、學習、進步&#xff0c;一起卷起來叭&#xff…

Java八股文——Spring篇

文章目錄 Java八股文專欄其它文章Java八股文——Spring篇SpringSpring的IoC和AOPSpring IoC實現機制Spring AOP實現機制 動態代理JDK ProxyCGLIBByteBuddy Spring框架中的單例Bean是線程安全的嗎&#xff1f;什么是AOP&#xff0c;你們項目中有沒有使用到AOPSpring中的事務是如…

NineData數據庫DevOps功能全面支持百度智能云向量數據庫 VectorDB,助力企業 AI 應用高效落地

NineData 的數據庫 DevOps 解決方案已完成對百度智能云向量數據庫 VectorDB 的全鏈路適配&#xff0c;成為國內首批提供 VectorDB 原生操作能力的服務商。此次合作聚焦 AI 開發核心場景&#xff0c;通過標準化 SQL 工作臺與細粒度權限管控兩大能力&#xff0c;助力企業安全高效…

開源技術驅動下的上市公司財務主數據管理實踐

開源技術驅動下的上市公司財務主數據管理實踐 —— 以人造板制造業為例 引言&#xff1a;財務主數據的戰略價值與行業挑戰 在資本市場監管日益嚴格與企業數字化轉型的雙重驅動下&#xff0c;財務主數據已成為上市公司財務治理的核心基礎設施。對于人造板制造業而言&#xff0…