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报文)
相关推荐
千里马学框架1 天前
一起学 Android 14:ShellTransition 屏幕旋转过程深度剖析
android·智能手机·性能优化·framework·性能·屏幕旋转·rotation
美狐美颜SDK开放平台1 天前
开发直播APP时如何接入视频美颜SDK?开发流程与注意事项
android·人工智能·计算机视觉·音视频·直播美颜sdk
AFinalStone1 天前
Android7 SystemUI源码解析(七)Keyguard锁屏模块深度解析
android·systemui
致远ccc1 天前
Google Play 上架前如何测试 App?多国家 Android 环境测试
android·app测试·googleplay·多国家应用测试
ttyyttemo1 天前
Kotlin 协程中的 Job 结构化并发与取消
android
sun0077001 天前
tbox 4g/5g切换,导致wan ip 改变,导致车机旧网络不可用。需要重启车机才行
android
其实防守也摸鱼1 天前
内网穿透与反向代理:原理、工具与实战指南
android·大数据·运维·安全·网络安全·自动化·渗透
AFinalStone2 天前
Android7 SystemUI 源码解析(四)NavigationBar 导航栏与 SystemBars
android·systemui
JMchen2 天前
属性动画原理与高级动画实现
android·kotlin·canvas
AFinalStone2 天前
Android7 SystemUI 源码解析(二)启动流程深度解析
android·systemui