OkHttp源码解析(一)

一、使用流程

二、分发器

复制代码
import androidx.annotation.Nullable;

import java.util.ArrayDeque;
import java.util.Deque;
import java.util.concurrent.ExecutorService;

// 可根据需要替换/补充 AsyncCall 与 RealCall 的定义
public final class DispatcherLike {

    // 异步请求同时存在的最大请求
    private int maxRequests = 64;

    // 异步请求同一域名同时存在的最大请求
    private int maxRequestsPerHost = 5;

    // 闲置任务(没有请求可执行时执行一些任务,由使用者设置)
    @Nullable
    private Runnable idleCallback;

    // 异步请求使用的线程池
    @Nullable
    private ExecutorService executorService;

    // 异步请求等待执行队列
    private final Deque<AsyncCall> readyAsyncCalls = new ArrayDeque<>();

    // 异步请求正在执行队列
    private final Deque<AsyncCall> runningAsyncCalls = new ArrayDeque<>();

    // 同步请求正在执行队列
    private final Deque<RealCall> runningSyncCalls = new ArrayDeque<>();

    // 根据实际需求添加构造方法、setter/getter、调度逻辑等
    // ...
}

三、同步请求

四、异步请求

复制代码
// 异步请求调用
public void finished(AsyncCall call) {
    finished(runningAsyncCalls, call, true);
}

// 同步请求调用
public void finished(RealCall call) {
    finished(runningSyncCalls, call, false);
}

private <T> void finished(Deque<T> calls, T call, boolean promoteCalls) {
    int runningCallsCount;
    Runnable idleCallback;

    synchronized (this) {
        // 不管异步还是同步,执行完后都要从队列移除
        if (!calls.remove(call)) {
            throw new AssertionError("Call wasn't in-flight!");
        }
        if (promoteCalls) {
            promoteCalls();
        }

        // 异步任务和同步任务正在执行的和
        runningCallsCount = runningCallsCount();
        idleCallback = this.idleCallback;
    }

    // 没有任务执行时执行闲置回调
    if (runningCallsCount == 0 && idleCallback != null) {
        idleCallback.run();
    }
}

五、请求流程

用户是不需要直接操作任务分发器的,获得的 RealCall 中就分别提供了 execute 与 enqueue 来开始同步请求或异 步请求。

复制代码
@Override
public Response execute() throws IOException {
    synchronized (this) {
        if (executed) throw new IllegalStateException("Already Executed");
        executed = true;
    }
    captureCallStackTrace();
    eventListener.callStart(this);
    try {
        // 调用分发器
        client.dispatcher().executed(this);
        // 执行请求
        Response result = getResponseWithInterceptorChain();
        if (result == null) throw new IOException("Canceled");
        return result;
    } catch (IOException e) {
        eventListener.callFailed(this, e);
        throw e;
    } finally {
        // 请求完成
        client.dispatcher().finished(this);
    }
}
复制代码
import java.io.IOException;

final class AsyncCall extends NamedRunnable {
    private final Callback responseCallback;

    AsyncCall(Callback responseCallback) {
        super("OkHttp %s", redactedUrl());
        this.responseCallback = responseCallback;
    }

    // 线程池执行
    @Override
    protected void execute() {
        boolean signalledCallback = false;
        try {
            Response response = getResponseWithInterceptorChain();
            // ......
        } catch (IOException e) {
            // ......
        } finally {
            // 请求完成
            client.dispatcher().finished(this);
        }
    }

    private static String redactedUrl() { return "url"; }
    private Response getResponseWithInterceptorChain() throws IOException { return new Response(); }
    private final OkHttpClient client = new OkHttpClient();
}

public abstract class NamedRunnable implements Runnable {
    protected final String name;

    public NamedRunnable(String format, Object... args) {
        this.name = Util.format(format, args);
    }

    @Override
    public final void run() {
        String oldName = Thread.currentThread().getName();
        Thread.currentThread().setName(name);
        try {
            execute();
        } finally {
            Thread.currentThread().setName(oldName);
        }
    }

    protected abstract void execute();
}

六、分发器线程池


前面我们提过,分发器就是来调配请求任务的,内部会包含一个线程池。当异步请求时,会将请求任务交给线程池 来执行。那分发器中默认的线程池是如何定义的呢?为什么要这么定义?

复制代码
import java.util.concurrent.ExecutorService;
import java.util.concurrent.SynchronousQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

private ExecutorService executorService;

public synchronized ExecutorService executorService() {
    if (executorService == null) {
        executorService = new ThreadPoolExecutor(
            0,                              // 核心线程
            Integer.MAX_VALUE,              // 最大线程
            60L,                            // 空闲线程闲置时间
            TimeUnit.SECONDS,               // 闲置时间单位
            new SynchronousQueue<Runnable>(), // 线程等待队列
            Util.threadFactory("OkHttp Dispatcher", false) // 线程创建工厂
        );
    }
    return executorService;
}

七、拦截器责任链

OkHttp最核心的工作是在 getResponseWithInterceptorChain() 中进行,在进入这个方法分析之前,我们先来了 解什么是责任链模式,因为此方法就是利用的责任链模式完成一步步的请求。

责任链模式

为请求创建了一个接收者对象的链。这种模式给予请求的类型,对请求的发送者和接收者进行解耦。在这种模式 中,通常每个接收者都包含对另一个接收者的引用。如果一个对象不能处理该请求,那么它会把相同的请求传给下 一个接收者,依此类推。比如:

八、拦截器流程

请求会被交给责任链中的一个个拦截器。默认情况下有五大拦截器:

  1. RetryAndFollowUpInterceptor
    第一个接触到请求,最后接触到响应;负责判断是否需要重新发起整个请求
  2. BridgeInterceptor
    补全请求,并对响应进行额外处理
  3. CacheInterceptor
    请求前查询缓存,获得响应并判断是否需要缓存
  4. ConnectInterceptor
    与服务器完成TCP连接
  5. CallServerInterceptor
    与服务器通信;封装请求数据与解析响应数据(如:HTTP报文)
相关推荐
allk553 小时前
OkHttp源码解析(二)
android·okhttp
aFakeProgramer3 小时前
拆分PDF.html 办公小工具
okhttp
2501_915909066 小时前
原生 iOS 开发全流程实战,Swift 技术栈、工程结构、自动化上传与上架发布指南
android·ios·小程序·uni-app·自动化·iphone·swift
2501_915909066 小时前
苹果软件混淆与 iOS 代码加固趋势,IPA 加密、应用防反编译与无源码保护的工程化演进
android·ios·小程序·https·uni-app·iphone·webview
2501_916007476 小时前
苹果软件混淆与 iOS 应用加固实录,从被逆向到 IPA 文件防反编译与无源码混淆解决方案
android·ios·小程序·https·uni-app·iphone·webview
介一安全6 小时前
【Frida Android】基础篇6:Java层Hook基础——创建类实例、方法重载、搜索运行时实例
android·java·网络安全·逆向·安全性测试·frida
沐怡旸9 小时前
【底层机制】【Android】深入理解UI体系与绘制机制
android·面试
啊森要自信9 小时前
【GUI自动化测试】YAML 配置文件应用:从语法解析到 Python 读写
android·python·缓存·pytest·pip·dash
下位子11 小时前
『AI 编程』用 Codex 开发识字小帮手应用
android·openai·ai编程