当前位置: 首页 > article >正文

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网络处理机制的关键所在。

相关文章:

OkHttp 3.0源码解析:从设计理念到核心实现

本文通过深入分析OkHttp 3.0源码&#xff0c;揭示其高效HTTP客户端的实现奥秘&#xff0c;包含核心设计理念、关键组件解析、完整工作流程及实用技巧。 一、引言&#xff1a;为什么选择OkHttp&#xff1f; 在Android和Java生态中&#xff0c;OkHttp已成为HTTP客户端的标准选择…...

【storage】

文章目录 1、RAM and ROM2、DRAM and SRAM2、Flash Memory&#xff08;闪存&#xff09;4、DDR and SPI NOR Flash5、eMMC6、SPI NOR vs SPI NAND vs eMMC vs SD附录——prototype and demo board附录——U盘、SD卡、TF卡、SSD参考 1、RAM and ROM RAM&#xff08;Random Acce…...

微信小程序带参分享、链接功能

分享链接的功能是右上角点...然后复制链接&#xff0c;可以直接点击 #小程序://**商城/p5XqHti******* 这种链接直接从其他地方跳转到小程序 wx.onCopyUrl(() > {return {query: "shareCode" this.shareCode,}; }); query就是参数&#xff0c;直接在onload里…...

JVM 垃圾回收器 详解

垃圾收集器 SerialSerial Old&#xff1a;单线程回收&#xff0c;适用于单核CPU场景ParNewCMS&#xff1a;暂停时间较短&#xff0c;适用于大型互联网应用中与用户交互的部分Paraller ScavengeParallel Old&#xff1a;吞吐量高&#xff0c;适用于后台进行大量数据操作G1&#…...

FreeRTOS任务之深入篇

目录 1.Tick1.1 Tick的概念1.2 Tick与任务调度1.3 Tick与延时函数 2.任务状态2.1 运行状态 (Running)2.2 就绪状态 (Ready)2.3 阻塞状态 (Blocked)5.4 暂停状态 (Suspended)2.5 特殊状态&#xff1a;删除状态 (Deleted)5.6 任务状态转换2.7 实验 3.Delay函数3.1 两个函数3.2 实…...

Linux 系统、代码与服务器进阶知识深度解析

在数字化时代&#xff0c;Linux 系统凭借其开源、稳定、安全的特性&#xff0c;成为服务器领域和软件开发的核心支柱。除了算法优化技巧&#xff0c;Linux 系统在网络服务、容器化技术、服务器安全等方面也蕴含着丰富的知识和实用技术。接下来&#xff0c;我们将深入探讨这些领…...

人工智能--AI换脸

本文实现了一个简易的人脸交换程序&#xff0c;主要功能包括&#xff1a;1)检查所需的模型文件是否存在&#xff1b;2)使用预训练的Caffe模型检测图像中的人脸&#xff1b;3)将源图像的人脸区域通过泊松融合无缝地替换到目标图像上。程序通过OpenCV的DNN模块加载人脸检测模型&a…...

NLP学习路线图(二十七):Transformer编码器/解码器

一、Transformer概览&#xff1a;抛弃循环&#xff0c;拥抱注意力 传统RNN及其变体&#xff08;如LSTM、GRU&#xff09;处理序列数据时存在顺序依赖的瓶颈&#xff1a;必须逐个处理序列元素&#xff0c;难以并行计算&#xff0c;且对长程依赖建模能力较弱。Transformer的革命…...

【机器学习】支持向量机实验报告——基于SVM进行分类预测

目录 一、实验题目描述 二、实验步骤 三、Python代码实现基于SVM进行分类预测 四、我的收获 五、我的感受 一、实验题目描述 实验题目&#xff1a;基于SVM进行分类预测 实验要求&#xff1a;通过给定数据&#xff0c;使用支持向量机算法&#xff08;SVM&#xff09;实现分…...

策略模式实战:Spring中动态选择商品处理策略的实现

概念 可以在运行时期动态的选择需要的具体策略类&#xff0c;处理具体的问题 组成元素 策略接口 public interface GoodsStrategy {void handleGoods(); } 具体策略类 Service(Constants.BEAN_GOODS) public class BeanGoodsStrategy implements GoodsStrategy {Override…...

主流信创数据库对向量功能的支持对比

主流信创数据库对向量功能的支持对比 版本支持对比向量索引支持对比距离函数支持对比使用限制对比OceanBase向量数据库GaussDB向量数据库TiDB向量数据库VastBase向量数据库 ⭐️ 本文章引用数据截止于2025年5月31日。 版本支持对比 数据库产品支持向量功能的版本OceanBaseOce…...

Matlab | matlab中的画图工具详解

二维图形到高级三维可视化 **一、基础二维绘图****二、三维可视化****三、图形修饰工具****四、高级功能****五、交互式工具****六、面向对象绘图(推荐)****七、常用技巧****学习资源**在MATLAB中,画图工具(绘图功能)是其核心优势之一,涵盖从基础二维图形到高级三维可视化…...

HA: Wordy靶场

HA: Wordy 来自 <HA: Wordy ~ VulnHub> 1&#xff0c;将两台虚拟机网络连接都改为NAT模式 2&#xff0c;攻击机上做namp局域网扫描发现靶机 nmap -sn 192.168.23.0/24 那么攻击机IP为192.168.23.128&#xff0c;靶场IP192.168.23.130 3&#xff0c;对靶机进行端口服务探…...

6.7本日总结

一、英语 复习默写list10list19&#xff0c;07年第3篇阅读 二、数学 学习线代第一讲&#xff0c;写15讲课后题 三、408 学习计组第二章&#xff0c;写计组习题 四、总结 本周结束线代第一讲和计组第二章&#xff0c;之后学习计网4.4&#xff0c;学完计网4.4之后开操作系…...

中国移动6周年!

基站超过250万个 网络规模全球最大、质量最优 覆盖全国96%人口 在全国率先实现乡乡双千兆 服务用户超5.7亿 网络上下行均值接入速率均居行业首位 行业应用快速推广&#xff0c;数量超5万个 3CC、RedCap、通感一体、 无线AI改造等技术成熟商用 客户品牌持续升级&#x…...

Svelte 核心语法详解:Vue/React 开发者如何快速上手?

在很多地方早就听到过svelte的大名了&#xff0c;不少工具都有针对svelte的配置插件&#xff0c;比如vite \ unocss \ svelte. 虽然还没使用过&#xff0c;但是发现它的star82.9k数很高哦&#xff0c;学习一下它与众不同的魔法。 这名字有点别扭&#xff0c;好几次都写错。 sve…...

Fullstack 面试复习笔记:HTML / CSS 基础梳理

Fullstack 面试复习笔记&#xff1a;HTML / CSS 基础梳理 之前的笔记&#xff1a; Fullstack 面试复习笔记&#xff1a;操作系统 / 网络 / HTTP / 设计模式梳理Fullstack 面试复习笔记&#xff1a;Java 基础语法 / 核心特性体系化总结Fullstack 面试复习笔记&#xff1a;项目…...

408第一季 - 数据结构 - 树与二叉树II

二叉树的先中后序遍历 理解 那主播&#xff0c;请问你有没有更快的遍历方式呢 有的&#xff0c;兄弟有的 以中序遍历为例啊 找左边有没有东西&#xff0c;左边没东西那它就自由了&#xff0c;就按上面的图举例子 A左边有东西&#xff0c;是B&#xff0c;B左边没东西&#xf…...

打卡第47天

作业&#xff1a;对比不同卷积层热图可视化的结果 核心差异总结 浅层卷积层&#xff08;如第 1-3 层&#xff09; 关注细节&#xff1a;聚焦输入图像的边缘、纹理、颜色块等基础特征&#xff08;例&#xff1a;猫脸的胡须边缘、树叶的脉络&#xff09;。热图特点&#xff1a;区…...

从上下文学习和微调看语言模型的泛化:一项对照研究

大型语言模型表现出令人兴奋的能力&#xff0c;但也可以从微调中表现出令人惊讶的狭窄泛化。例如&#xff0c;他们可能无法概括为简单的关系反转&#xff0c;或者无法根据训练信息进行简单的逻辑推理。这些未能从微调中概括出来的失败可能会阻碍这些模型的实际应用。另一方面&a…...

智慧城市建设方案

第1章 总体说明 1.1 建设背景 1.2 建设目标 1.3 项目建设主要内容 1.4 设计原则 第2章 对项目的理解 2.1 现状分析 2.2 业务需求分析 2.3 功能需求分析 第3章 大数据平台建设方案 3.1 大数据平台总体设计 3.2 大数据平台功能设计 3.3 平台应用 第4章 政策标准保障…...

phosphobot开源程序是控制您的 SO-100 和 SO-101 机器人并训练 VLA AI 机器人开源模型

​一、软件介绍 文末提供程序和源码下载 phosphobot开源程序是控制您的 SO-100 和 SO-101 机器人并训练 VLA AI 机器人开源模型。 二、Overview 概述 &#x1f579;️ Control your robot with the keyboard, a leader arm, a Meta Quest headset or via API &#x1f579;️…...

pygame开发的坦克大战

使用Python和Pygame开发的精美坦克大战游戏。这个游戏包含玩家控制的坦克、敌方坦克、各种障碍物、爆炸效果和完整的游戏机制。 游戏说明 这个坦克大战游戏包含以下功能&#xff1a; 游戏特点 玩家控制&#xff1a;使用方向键移动坦克&#xff0c;空格键射击 敌人AI&#x…...

C++2025.6.7 C++五级考题

城市商业街主干道是一条笔直的道路&#xff0c;商业街里有 n 家店铺&#xff0c;现给定 n 个店铺的位置&#xff0c;请在这条道路上找到一个中心点&#xff0c;使得所有店铺到这个中心点的距离之和最小&#xff0c;并输出这个最小值。 #include <bits/stdc.h> using nam…...

【原神 × 二叉树】角色天赋树、任务分支和圣遗物强化路径的算法秘密!

【原神 二叉树】角色天赋树、任务分支和圣遗物强化路径的算法秘密! 作者:星之辰 标签:#原神 #二叉树 #天赋树 #任务分支 #圣遗物强化 #算法科普 发布时间:2025年6月 总字数:6000+ 一、引子:提瓦特大陆的“树型奥秘” 你是否曾留意过《原神》角色面板的天赋树? 升级技能…...

功能安全实战系列09-英飞凌TC3xx LBIST开发详解

本文框架 0. 前言1.What?1.1 基本原理1.1.1 检测范围1.1.2 LBIST与锁步核对比1.1.3 控制寄存器1.2 关联Alarm2. How?2.1 LBIST触发?2.1.1 SSW配置自动触发2.1.2 软件手动触发LBIST2.2 实现策略2.3 测试篇LBIST对启动时间的影响如何确定当前LBIST是否已使能?如何确定当前LBI…...

一个完整的日志收集方案:Elasticsearch + Logstash + Kibana+Filebeat (二)

&#x1f4c4; 本地 Windows 部署 Logstash 连接本地 Elasticsearch 指南 ✅ 目标 在本地 Windows 上安装并运行 Logstash配置 Logstash 将数据发送至本地 Elasticsearch测试数据采集与 ES 存储流程 &#x1f9f0; 前提条件 软件版本要求安装说明Java17Oracle JDK 下载 或 O…...

RT-Thread内核组成——内核移植

内核移植就是指将 RT-Thread 内核在不同的芯片架构、不同的板卡上运行起来&#xff0c;能够具备线程管理和调度&#xff0c;内存管理&#xff0c;线程间同步和通信、定时器管理等功能。移植可分为 CPU 架构移植和 BSP&#xff08;Board support package&#xff0c;板级支持包&…...

Docker_Desktop开启k8s

Docker_Desktop开启k8s 原文地址&#xff1a;在 Docker Desktop 中启用 K8s 服务 - 墨墨墨墨小宇 - 博客园 开启k8s服务 打开docker的设置界面&#xff0c;选择Docker Engine&#xff0c;修改如下&#xff1a; {"debug": false,"experimental": false,…...

MS2691 全频段、多模导航、射频低噪声放大器芯片,应用于导航仪 双频测量仪

MS2691 全频段、多模导航、射频低噪声放大器芯片&#xff0c;应用于导航仪 双频测量仪 产品简述 MS2691 是一款具有 1164MHz  1615MHz 全频段、低功耗的低噪声放大器芯片。该芯片通过对外围电路的简单配置&#xff0c;使得频带具有宽带或窄带特性。支持不同频段的各种导…...