OkHttp 3.12.0源码分析

主要参考文章:
1.Okhttp的基本使用
2.Okhttp主流程源码分析

Okhttp 3.12.0 使用例子

String url = "http://wwww.baidu.com";
OkHttpClient okHttpClient = new OkHttpClient();
final Request request = new Request.Builder()
        .url(url)
        .get()//默认就是GET请求,可以不写
        .build();
Call call = okHttpClient.newCall(request);
call.enqueue(new Callback() {
    @Override
    public void onFailure(Call call, IOException e) {
        Log.d("ziq", "onFailure: ");
    }

    @Override
    public void onResponse(Call call, Response response) throws IOException {
        Log.d("ziq", "onResponse: " + response.body().string());
    }
});

1、初始化

建造者模式,public OkHttpClient() { this(new Builder());} 初始化了一些配置信息:支持协议、任务分发器(其内部包含一个线程池,执行异步请求)、连接池(其内部包含一个线程池,维护connection)、连接/读/写超时时长等信息。

public Builder() {
    dispatcher = new Dispatcher();//任务调度器
    protocols = DEFAULT_PROTOCOLS;//支持的协议 Protocol.HTTP_2, Protocol.HTTP_1_1
    connectionSpecs = DEFAULT_CONNECTION_SPECS;
    eventListenerFactory = EventListener.factory(EventListener.NONE);
    proxySelector = ProxySelector.getDefault();
    if (proxySelector == null) {
        proxySelector = new NullProxySelector();
    }
    cookieJar = CookieJar.NO_COOKIES;
    socketFactory = SocketFactory.getDefault();
    hostnameVerifier = OkHostnameVerifier.INSTANCE;//根据地址与证书 进行校验X509Certificate
    certificatePinner = CertificatePinner.DEFAULT;
    proxyAuthenticator = Authenticator.NONE;//身份验证
    authenticator = Authenticator.NONE;//身份验证
    connectionPool = new ConnectionPool();//连接池
    dns = Dns.SYSTEM;
    followSslRedirects = true;
    followRedirects = true;
    retryOnConnectionFailure = true;
    callTimeout = 0;
    connectTimeout = 10_000;
    readTimeout = 10_000;
    writeTimeout = 10_000;
    pingInterval = 0;
}

下面列出 Dispatcher 的主要分析部分

//Dispatcher.java
public final class Dispatcher {
private int maxRequests = 64;//最大请求数量
private int maxRequestsPerHost = 5;//每台主机最大的请求数量
private @Nullable Runnable idleCallback;
private @Nullable ExecutorService executorService;//线程池
private final Deque<AsyncCall> readyAsyncCalls = new ArrayDeque<>();//等待队列
private final Deque<AsyncCall> runningAsyncCalls = new ArrayDeque<>();//异步运行中队列
private final Deque<RealCall> runningSyncCalls = new ArrayDeque<>();//同步运行中队列

public synchronized ExecutorService executorService() {
    if (executorService == null) {
        executorService = new ThreadPoolExecutor(0, Integer.MAX_VALUE, 60, TimeUnit.SECONDS,
        new SynchronousQueue<Runnable>(), Util.threadFactory("OkHttp Dispatcher", false));
    }
    return executorService;
}

。。。
void enqueue(AsyncCall call) {
    synchronized (this) {
        readyAsyncCalls.add(call);//添加到异步运行队列中
    }
    promoteAndExecute();//将 readyAsyncCalls中的call 添加到 runningAsyncCalls并运行
}

private boolean promoteAndExecute() {
    assert (!Thread.holdsLock(this));
    List<AsyncCall> executableCalls = new ArrayList<>();
    boolean isRunning;
    synchronized (this) {
        for (Iterator<AsyncCall> i = readyAsyncCalls.iterator(); i.hasNext(); ) {
            AsyncCall asyncCall = i.next();
        //正在运行的任务数是否大于 64
            if (runningAsyncCalls.size() >= maxRequests) break; // Max capacity.
        //对应主机 的 正在运行的任务数是否大于 5
            if (runningCallsForHost(asyncCall) >= maxRequestsPerHost) continue; // Host max capacity.
            i.remove();
            executableCalls.add(asyncCall);
            runningAsyncCalls.add(asyncCall);
        }
        isRunning = runningCallsCount() > 0;
    }
    for (int i = 0, size = executableCalls.size(); i < size; i++) {
        AsyncCall asyncCall = executableCalls.get(i);
        asyncCall.executeOn(executorService());
    }
    return isRunning;
}

。。。
}

2、生成call 并 请求

//OkHttpClient.java
@Override public Call newCall(Request request) {
    return RealCall.newRealCall(this, request, false /* for web socket */);
}

//RealCall.java
new RealCall(client, originalRequest, forWebSocket);

@Override public void enqueue(Callback responseCallback) {
    synchronized (this) {
        if (executed) throw new IllegalStateException("Already Executed");
            executed = true;
    }
    captureCallStackTrace();
    eventListener.callStart(this);
    client.dispatcher().enqueue(new AsyncCall(responseCallback));
}

那么从dispatcher的代码中可知道,call直接加入到 readyAsyncCalls队列中,然后 在promoteAndExecute方法中判断是否 可以执行 readyAsyncCalls中的任务,如果可以则 把任务加入到runningAsyncCalls ,并 到线程池中执行。

3、执行call

asyncCall.executeOn(executorService());
AsyncCall extends NamedRunnable implements Runnable
,所以运行run() ->execute() ;

final class AsyncCall extends NamedRunnable {
。。。
@Override protected void execute() {
    boolean signalledCallback = false;
    timeout.enter();
    try {
//1、进过一系列拦截器后,返回结果
        Response response = getResponseWithInterceptorChain();
        if (retryAndFollowUpInterceptor.isCanceled()) {
            signalledCallback = true;
            responseCallback.onFailure(RealCall.this, new IOException("Canceled"));
        } else {
            signalledCallback = true;
            responseCallback.onResponse(RealCall.this, response);
        }
    } catch (IOException e) {
        e = timeoutExit(e);
        if (signalledCallback) {
            // Do not signal the callback twice!
            Platform.get().log(INFO, "Callback failure for " + toLoggableString(), e);
        } else {
            eventListener.callFailed(RealCall.this, e);
            responseCallback.onFailure(RealCall.this, e);
        }
    } finally {
//2、结束当前call,运行未执行且符合条件的call
        client.dispatcher().finished(this);
    }
}

}

4、Interceptor拦截器

到了这里,到达了okhttp最核心的部分, Interceptor,采用了责任链的设计模式

4.1client.dispatcher().finished(this);
//Dispatcher.java
private <T> void finished(Deque<T> calls, T call) {
    Runnable idleCallback;
    synchronized (this) {
    if (!calls.remove(call)) throw new AssertionError("Call wasn't in-flight!");
        idleCallback = this.idleCallback;
    }
    boolean isRunning = promoteAndExecute();
    if (!isRunning && idleCallback != null) {
        idleCallback.run();
    }
}

可以看到,移除call后,又回到 一开始 的promoteAndExecute() 方法,去把readyAsyncCalls中的call 拿出来运行。

4.2 责任链

Response response = getResponseWithInterceptorChain();进过一系列拦截器后,返回结果

//RealCall.java
Response getResponseWithInterceptorChain() throws IOException {
// Build a full stack of interceptors.
    List<Interceptor> interceptors = new ArrayList<>();
    interceptors.addAll(client.interceptors());
    interceptors.add(retryAndFollowUpInterceptor);
    interceptors.add(new BridgeInterceptor(client.cookieJar()));
    interceptors.add(new CacheInterceptor(client.internalCache()));
    interceptors.add(new ConnectInterceptor(client));
    if (!forWebSocket) {
        interceptors.addAll(client.networkInterceptors());
    }
    interceptors.add(new CallServerInterceptor(forWebSocket));

    Interceptor.Chain chain = new RealInterceptorChain(interceptors, null, null, null, 0,
    originalRequest, this, eventListener, client.connectTimeoutMillis(),
    client.readTimeoutMillis(), client.writeTimeoutMillis());
    return chain.proceed(originalRequest);
}

添加的各Interceptor,分别负责功能:
client.interceptors() 用户自定义的Interceptor,能拦截到所有的请求
RetryAndFollowUpInterceptor 负责失败重连和重定向相关
BridgeInterceptor 负责配置请求的头信息,比如Keep-Alive、gzip、Cookie等可以优化请求
CacheInterceptor 负责缓存管理,使用DiskLruCache做本地缓存,CacheStrategy决定缓存策略
ConnectInterceptor 开始与目标服务器建立连接,获得RealConnection
client.networkInterceptors() 用户自定义的Interceptor,仅在生产网络请求时生效
CallServerInterceptor 实际网络请求的地方。

递归实现:
1、RealInterceptorChain.proceed(...) 为开头,会生成 下一步的RealInterceptorChain next1,与当前的RealInterceptorChain参数一样,区别是index 加1了,所以会调用 下一级 拦截器的interceptor1.intercept(next)
2、interceptor1.intercept(next)调用 下一个链 next1.proceed(...)方法
3、next1.proceed(...)调用下一个interceptor2.intercept(next) 形成 递归
4、最后的拦截器是 CallServerInterceptor,返回response,然后一级一级返回response。

//RealInterceptorChain.java
public Response proceed(Request request, StreamAllocation streamAllocation, HttpCodec httpCodec,RealConnection connection)throws IOException {
    if (index >= interceptors.size()) throw new AssertionError();
    calls++;
    if (this.httpCodec != null && !this.connection.supportsUrl(request.url())) {
        throw new IllegalStateException("network interceptor " + interceptors.get(index - 1)
            + " must retain the same host and port");
    }
    if (this.httpCodec != null && calls > 1) {
        throw new IllegalStateException("network interceptor " + interceptors.get(index - 1)
        + " must call proceed() exactly once");
    }
//1、生成 下一步的RealInterceptorChain,与当前的RealInterceptorChain参数一样,区别是index 加1 了
    RealInterceptorChain next = new RealInterceptorChain(interceptors, streamAllocation, httpCodec,
        connection, index + 1, request, call, eventListener, connectTimeout, readTimeout,
        writeTimeout);
    Interceptor interceptor = interceptors.get(index);
 //2、Interceptor interceptor内会调用 next.proceed(...) 又回到这里的第一步,再生成一个chain 形成递归
    Response response = interceptor.intercept(next);
//结束递归

    if (httpCodec != null && index + 1 < interceptors.size() && next.calls != 1) {
        throw new IllegalStateException("network interceptor " + interceptor
        + " must call proceed() exactly once");
    }
    if (response == null) {
        throw new NullPointerException("interceptor " + interceptor + " returned null");
    }
    if (response.body() == null) {
        throw new IllegalStateException(
        "interceptor " + interceptor + " returned a response with no body");
    }
    return response;
}

各个拦截器部分细节描述:

拦截器 描述
RetryAndFollowUpInterceptor 生成 StreamAllocation 实例,后面的拦截器使用
BridgeInterceptor 添加header参数
CacheInterceptor 默认是没有缓存的,全都是直接网络请求。
ConnectInterceptor 利用上面 的StreamAllocation 建立 HttpCodec、RealConnection实例从connectionPool 寻找是否有RealConnection,有就直接赋值给StreamAllocation 中的connection 找不到就result = new RealConnection(connectionPool, selectedRoute);新建,赋值给StreamAllocation并且缓存到connectionPool
CallServerInterceptor 使用前面其他拦截器生成的 StreamAllocation , RealConnection, HttpCodec;
而HttpCodec 中使用okio 通过Socket传输数据,网络请求。
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,839评论 6 482
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,543评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 153,116评论 0 344
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,371评论 1 279
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,384评论 5 374
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,111评论 1 285
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,416评论 3 400
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,053评论 0 259
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,558评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,007评论 2 325
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,117评论 1 334
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,756评论 4 324
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,324评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,315评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,539评论 1 262
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,578评论 2 355
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,877评论 2 345

推荐阅读更多精彩内容