第一章 Okhttp是如何创建请求、响应一个请求
第二章 Okhttp中的设计模式及其应用场景、优缺点
第三章 Okhttp中的数据结构
第四章 Okhttp中的其他相关
提示:写完文章后,目录可以自动生成,如何生成可参考右边的帮助文档
目录
Okhttp系列文章目录
文章目录
前言
一、Okhttp是如何创建请求?
1、创建同步请求
2、创建异步请求:
二、Okhttp如何响应一个请求——源码分析
1.同步请求
2.异步请求
总结
提示:这里可以添加本文要记录的大概内容:
项目中经常使用OkHttp+retrofit+rxjava进行网络框架的搭建,而Okhttp详细的运行流程及源码却基本很少分析,本文将从基础不同角度去分析Okhttp中的知识。
提示:以下是本篇文章正文内容,下面案例可供参考
OkHttp官方地址:
https://github.com/square/okhttp
引入库:
implementation("com.squareup.okhttp3:okhttp:4.9.0")
现在的版本都是基于kotlin的,如果习惯java的同学可用低版本查看java版本的源码,本文也是基于java版本源码分析的。
implementation 'com.squareup.okhttp3:okhttp:3.9.0'
copy官方代码:
OkHttpClient client = new OkHttpClient(); String run(String url) throws IOException { Request request = new Request.Builder() .url(url) .build(); try (Response response = client.newCall(request).execute()) { return response.body().string(); } }
从各种技术文章看到的写入,大概如下:
OkHttpClient okHttpClient = new OkHttpClient.Builder() .readTimeout(5000, TimeUnit.MILLISECONDS) .addInterceptor(new HttpLoggingInterceptor()) //log拦截器,暂时不用管 .connectTimeout(15,TimeUnit.SECONDS) .build(); Request request = new Request. Builder(). url("http://m.baidu.com"). addHeader("",""). build(); Call call = okHttpClient.newCall(request); try { Response response = call.execute(); } catch (IOException e) { e.printStackTrace(); }
OkHttpClient okHttpClient = new OkHttpClient.Builder() .readTimeout(5000, TimeUnit.MILLISECONDS) .addInterceptor(new HttpLoggingInterceptor())//log拦截器,暂时不用管 .connectTimeout(15,TimeUnit.SECONDS) .build(); Request request = new Request. Builder(). url("http://m.baidu.com"). addHeader("",""). build(); Call call = okHttpClient.newCall(request); call.enqueue(new Callback() { @Override public void onFailure(Call call, IOException e) { } @Override public void onResponse(Call call, Response response) throws IOException { } });
上述可以看到 同步和异步的区别:
//同步请求
call.execute() ;
//异步请求
call.enqueue(new Callback() { @Override public void onFailure(Call call, IOException e) { } @Override public void onResponse(Call call, Response response) throws IOException { } });
从call.execute() 分析,查看源码,发现是个接口:
package okhttp3; import java.io.IOException; public interface Call extends Cloneable { /** * @throws IOException if the request could not be executed due to cancellation, a connectivity * problem or timeout. Because networks can fail during an exchange, it is possible that the * remote server accepted the request before the failure. * @throws IllegalStateException when the call has already been executed. */ Response execute() throws IOException; /** * @throws IllegalStateException when the call has already been executed. */ void enqueue(Callback responseCallback); /** Cancels the request, if possible. Requests that are already complete cannot be canceled. */ void cancel(); /** * Returns true if this call has been either {@linkplain #execute() executed} or {@linkplain * #enqueue(Callback) enqueued}. It is an error to execute a call more than once. */ boolean isExecuted(); boolean isCanceled(); /** * Create a new, identical call to this one which can be enqueued or executed even if this call * has already been. */ Call clone(); interface Factory { Call newCall(Request request); } }
既然是接口,那肯定有实现,代码往前定位,发现返回的是RealCall类:
okHttpClient.newCall(request)
@Override public Call newCall(Request request) { return RealCall.newRealCall(this, request, false /* for web socket */); }
现在在RealCall中分析处理call.execute()了,看源码;
@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); } }
看这句 client.dispatcher().executed(this);
/** Used by {@code Call#execute} to signal it is in-flight. */ synchronized void executed(RealCall call) { runningSyncCalls.add(call); //队列中加入请求,后续详解 }
1.通过分发器将请求分发处理——Dispatcher分发器,有没有想到事件分发?
2.分发器怎么来的?没看到有定义?——client.dispatcher()这句看出是在OkhttpClient创建时创建的,具体怎么创建的,后续分析OkhttpClient中的设计模式(构建者模式builder)会提到。
继续分析 Dispatcher,看到我们获取的Response 已经得到了。
Response result = getResponseWithInterceptorChain(); //拦截器相关处理,后续设计模式中会分析,暂时不用管 if (result == null) throw new IOException("Canceled"); return result; //返回结果
从这句出发call.enqueue(new Callback()),why? 这里是最终执行请求的地方,从这里切入是最好的。相关RealCall的流程和上面的同步请求一样,省略。重点分析异步请求:
@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)); //重点这句 }
client.dispatcher().enqueue(new AsyncCall(responseCallback)); 也是调用Dispatcher进行分发,继续跟入Dispatcher中相关代码:
synchronized void enqueue(AsyncCall call) { //相关条件判断:满足即进入运行队列,并运行,否则 进入等待序列 // 条件:maxRequests =64 最大请求数,maxRequestsPerHost =5 最大同一host请求数 if (runningAsyncCalls.size() < maxRequests && runningCallsForHost(call) < maxRequestsPerHost) { runningAsyncCalls.add(call); //请求加入运行队列 executorService().execute(call);// 运行线程池,开始请求 } else { readyAsyncCalls.add(call); //加入等待序列 } }
这里
AsyncCall 显然是一个Runnable,继承 NamedRunnable,一个带命名的Runnable。感兴趣可以去查看源码。
至此这个同步、异步分析已完成,相关差异也是可以从代码中体现。
提示:这里对文章进行总结:
这里只是对OkHttp的请求过程进行简单的分析,并未深入插入其他知识点,相关详细的分知识点会在其他章节讲解。
附上 Dispatcher相关源码:
/* * Copyright (C) 2013 Square, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package okhttp3; import java.util.ArrayDeque; import java.util.ArrayList; import java.util.Collections; import java.util.Deque; import java.util.Iterator; import java.util.List; import java.util.concurrent.ExecutorService; import java.util.concurrent.SynchronousQueue; import java.util.concurrent.ThreadPoolExecutor; import java.util.concurrent.TimeUnit; import javax.annotation.Nullable; import okhttp3.RealCall.AsyncCall; import okhttp3.internal.Util; /** * Policy on when async requests are executed. * *Each dispatcher uses an {@link ExecutorService} to run calls internally. If you supply your * own executor, it should be able to run {@linkplain #getMaxRequests the configured maximum} number * of calls concurrently. */ public final class Dispatcher { private int maxRequests = 64; private int maxRequestsPerHost = 5; private @Nullable Runnable idleCallback; /** Executes calls. Created lazily. */ private @Nullable ExecutorService executorService; /** Ready async calls in the order they'll be run. */ private final Deque
readyAsyncCalls = new ArrayDeque<>(); /** Running asynchronous calls. Includes canceled calls that haven't finished yet. */ private final Deque runningAsyncCalls = new ArrayDeque<>(); /** Running synchronous calls. Includes canceled calls that haven't finished yet. */ private final Deque runningSyncCalls = new ArrayDeque<>(); public Dispatcher(ExecutorService executorService) { this.executorService = executorService; } public Dispatcher() { } public synchronized ExecutorService executorService() { if (executorService == null) { executorService = new ThreadPoolExecutor(0, Integer.MAX_VALUE, 60, TimeUnit.SECONDS, new SynchronousQueue (), Util.threadFactory("OkHttp Dispatcher", false)); } return executorService; } /** * Set the maximum number of requests to execute concurrently. Above this requests queue in * memory, waiting for the running calls to complete. * * If more than {@code maxRequests} requests are in flight when this is invoked, those requests * will remain in flight. */ public synchronized void setMaxRequests(int maxRequests) { if (maxRequests < 1) { throw new IllegalArgumentException("max < 1: " + maxRequests); } this.maxRequests = maxRequests; promoteCalls(); } public synchronized int getMaxRequests() { return maxRequests; } /** * Set the maximum number of requests for each host to execute concurrently. This limits requests * by the URL's host name. Note that concurrent requests to a single IP address may still exceed * this limit: multiple hostnames may share an IP address or be routed through the same HTTP * proxy. * *
If more than {@code maxRequestsPerHost} requests are in flight when this is invoked, those * requests will remain in flight. */ public synchronized void setMaxRequestsPerHost(int maxRequestsPerHost) { if (maxRequestsPerHost < 1) { throw new IllegalArgumentException("max < 1: " + maxRequestsPerHost); } this.maxRequestsPerHost = maxRequestsPerHost; promoteCalls(); } public synchronized int getMaxRequestsPerHost() { return maxRequestsPerHost; } /** * Set a callback to be invoked each time the dispatcher becomes idle (when the number of running * calls returns to zero). * *
Note: The time at which a {@linkplain Call call} is considered idle is different depending * on whether it was run {@linkplain Call#enqueue(Callback) asynchronously} or * {@linkplain Call#execute() synchronously}. Asynchronous calls become idle after the * {@link Callback#onResponse onResponse} or {@link Callback#onFailure onFailure} callback has * returned. Synchronous calls become idle once {@link Call#execute() execute()} returns. This * means that if you are doing synchronous calls the network layer will not truly be idle until * every returned {@link Response} has been closed. */ public synchronized void setIdleCallback(@Nullable Runnable idleCallback) { this.idleCallback = idleCallback; } synchronized void enqueue(AsyncCall call) { if (runningAsyncCalls.size() < maxRequests && runningCallsForHost(call) < maxRequestsPerHost) { runningAsyncCalls.add(call); executorService().execute(call); } else { readyAsyncCalls.add(call); } } /** * Cancel all calls currently enqueued or executing. Includes calls executed both {@linkplain * Call#execute() synchronously} and {@linkplain Call#enqueue asynchronously}. */ public synchronized void cancelAll() { for (AsyncCall call : readyAsyncCalls) { call.get().cancel(); } for (AsyncCall call : runningAsyncCalls) { call.get().cancel(); } for (RealCall call : runningSyncCalls) { call.cancel(); } } private void promoteCalls() { if (runningAsyncCalls.size() >= maxRequests) return; // Already running max capacity. if (readyAsyncCalls.isEmpty()) return; // No ready calls to promote. for (Iterator
i = readyAsyncCalls.iterator(); i.hasNext(); ) { AsyncCall call = i.next(); if (runningCallsForHost(call) < maxRequestsPerHost) { i.remove(); runningAsyncCalls.add(call); executorService().execute(call); } if (runningAsyncCalls.size() >= maxRequests) return; // Reached max capacity. } } /** Returns the number of running calls that share a host with {@code call}. */ private int runningCallsForHost(AsyncCall call) { int result = 0; for (AsyncCall c : runningAsyncCalls) { if (c.host().equals(call.host())) result++; } return result; } /** Used by {@code Call#execute} to signal it is in-flight. */ synchronized void executed(RealCall call) { runningSyncCalls.add(call); } /** Used by {@code AsyncCall#run} to signal completion. */ void finished(AsyncCall call) { finished(runningAsyncCalls, call, true); } /** Used by {@code Call#execute} to signal completion. */ void finished(RealCall call) { finished(runningSyncCalls, call, false); } private void finished(Deque 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(); } } /** Returns a snapshot of the calls currently awaiting execution. */ public synchronized List queuedCalls() { List result = new ArrayList<>(); for (AsyncCall asyncCall : readyAsyncCalls) { result.add(asyncCall.get()); } return Collections.unmodifiableList(result); } /** Returns a snapshot of the calls currently being executed. */ public synchronized List runningCalls() { List result = new ArrayList<>(); result.addAll(runningSyncCalls); for (AsyncCall asyncCall : runningAsyncCalls) { result.add(asyncCall.get()); } return Collections.unmodifiableList(result); } public synchronized int queuedCallsCount() { return readyAsyncCalls.size(); } public synchronized int runningCallsCount() { return runningAsyncCalls.size() + runningSyncCalls.size(); } }