OkHttp
是越来越火了,安卓4.4开始,谷歌在HttpUrlConnection
底层也开始使用OkHttp
实现。
public final class URL implements Serializable {
public URL(String spec) throws MalformedURLException {
this((URL) null, spec, null);
}
public URL(URL context, String spec, URLStreamHandler handler)
throws MalformedURLException {
······
setupStreamHandler();
······
}
void setupStreamHandler() {
......
if (protocol.equals("file")) {
streamHandler = new FileHandler();
} else if (protocol.equals("ftp")) {
streamHandler = new FtpHandler();
} else if (protocol.equals("http")) {
// http/https协议,使用的是okhttp.
String name = "com.android.okhttp.HttpHandler";
streamHandler = (URLStreamHandler) Class.forName(name).newInstance();
} else if (protocol.equals("https")) {
String name = "com.android.okhttp.HttpsHandler";
streamHandler = (URLStreamHandler) Class.forName(name).newInstance();
} ······
}
public URLConnection openConnection() throws IOException {
return streamHandler.openConnection(this);
}
now就从源码的角度来分析解剖下OkHttp的内部实现。
Request request = new Request.Builder().url(mUrl).build();
OkHttpClient client = new OkHttpClient();//demo构造代码
client.newCall(request).enqueue(new Callback() {
@Override //请求失败
public void onFailure(Call call, IOException e) {}
@Override//请求成功
public void onResponse(Call call, Response response) throws IOException {}
});
1.Request 构建
Request构建采用了Builder设计模式,以处理复杂的参数模型,包括url
,Header
,method
,RequestBody
,以及tag
。
在传入url后利用HttpUrl对象获取请求的属性,以如下url为例:"http://baidu.com/pathSegment/pathSegment2?ie=utf-8&wd=CSDN#fragment"
1.scheme
(HTTP) 通过url.regionMatches()匹配
2.port
(80) http默认80 https默认443,其它-1
3.host
(baidu.com)
4.queryNamesAndValues
[ie,utf-8, wd,CSDN],以key,value成对顺序存储的arrayList
5.pathSegments
[pathSegment,pathSegment2]
6.fragment
(fragment)。
注意点
添加head
和param
时不可传入null
,否则会直接抛出运行时异常,因此在实际使用中对request进行装饰设计是十分必要的。
2. OkHttpClient()的构造
static final List DEFAULT_PROTOCOLS = Util.immutableList(
Protocol.HTTP_2, Protocol.HTTP_1_1);
static final List DEFAULT_CONNECTION_SPECS = Util.immutableList(
ConnectionSpec.MODERN_TLS, ConnectionSpec.CLEARTEXT);
public OkHttpClient() {this(new Builder());}
public static final class Builder {
······
public Builder() {
dispatcher = new Dispatcher();
protocols = DEFAULT_PROTOCOLS;
connectionSpecs = DEFAULT_CONNECTION_SPECS;
proxySelector = ProxySelector.getDefault();
cookieJar = CookieJar.NO_COOKIES;
socketFactory = SocketFactory.getDefault();
hostnameVerifier = OkHostnameVerifier.INSTANCE;
certificatePinner = CertificatePinner.DEFAULT;
proxyAuthenticator = Authenticator.NONE;
authenticator = Authenticator.NONE;
connectionPool = new ConnectionPool();
dns = Dns.SYSTEM;
followSslRedirects = true;
followRedirects = true;
retryOnConnectionFailure = true;
connectTimeout = 10_000;
readTimeout = 10_000;
writeTimeout = 10_000;
pingInterval = 0;
}
- 通常不会使用这种写法,而是会通过
Builder
做个性配置后采用单例模式生成OkHttpClient
。Retrofit
在此暂不讨论
3.newCall() 与 enqueue()
@Override public Call newCall(Request request) {
return new RealCall(this, request, false /* for web socket */);
}
······
RealCall(OkHttpClient client, Request originalRequest, boolean forWebSocket) {
this.client = client;
this.originalRequest = originalRequest;
this.forWebSocket = forWebSocket;
this.retryAndFollowUpInterceptor = new RetryAndFollowUpInterceptor(client, forWebSocket);
}
newCall()的内部实现只简单构造了一个RealCall
对象
注意点
1.OK是支持webSocket
链接的
2.构造函数中定义了一个RetryAndFollowUpInterceptor
拦截器
异步请求enqueue()
@Override public void enqueue(Callback responseCallback) {
synchronized (this) {
if (executed) throw new IllegalStateException("Already Executed");
executed = true;
}
captureCallStackTrace();
client.dispatcher().enqueue(new AsyncCall(responseCallback));
}
- 异步请求只有一步流程,就是构建一个
AsyncCall
,并把它添加到dispatcher
的runningAsyncCalls
队列中,AsyncCall本质是一个Runnable的装饰类。动态为当前线程设置Name属性,在run
方法中调用execute()
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();
if (retryAndFollowUpInterceptor.isCanceled()) {
signalledCallback = true;
responseCallback.onFailure(RealCall.this, new IOException("Canceled"));
} else {
signalledCallback = true;
responseCallback.onResponse(RealCall.this, response);
}
} catch (IOException e) {
if (signalledCallback) {// Do not signal the callback twice!
Platform.get().log(INFO, "Callback failure for " + toLoggableString(), e);
} else {
responseCallback.onFailure(RealCall.this, e);
}
} finally {
client.dispatcher().finished(this);
}
}
注意点:
1.callBack
的onResponse()
和onFailure()
都是在子线程中执行
2.在开发中通常在Activity
结束时cancel
请求,则会在callback.onFailure()
回调给开发层,如果使用匿名内部类做接口回调需防范内存泄漏问题
同步请求execute()
@Override public Response execute() throws IOException {
······
try {
//在dispatcher的runningSyncCalls队列中添加这个请求
client.dispatcher().executed(this);
//处理请求,得到响应
Response result = getResponseWithInterceptorChain();
if (result == null) throw new IOException("Canceled");
return result;
} finally {
//从diapatcher的runningSyncCalls队列中移除这个请求
client.dispatcher().finished(this);
}
}
- 在同步请求流程中,
dispatcher
只负责了realCall
状态的记录,没有控制调度
4.dispatcher--OkHttp的流水线
public final class Dispatcher {
private int maxRequests = 64;
private int maxRequestsPerHost = 5;
private Runnable idleCallback;
private ExecutorService executorService;
//等待状态的异步队列
private final Deque readyAsyncCalls = new ArrayDeque<>();
//运行状态的异步队列
private final Deque runningAsyncCalls = new ArrayDeque<>();
//运行状态的同步队列
private final Deque runningSyncCalls = new ArrayDeque<>();
synchronized void enqueue(AsyncCall call) {
if (runningAsyncCalls.size() < maxRequests && runningCallsForHost(call) < maxRequestsPerHost) {
runningAsyncCalls.add(call);
executorService().execute(call);
} else {
readyAsyncCalls.add(call);
}
}
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;
}
- 最多支持64个异步请求并发
- 同一个host下最多支持5个连接
- 懒加载了一个线程池,常驻线程数量0,最大容量无限量。
- 当试图添加异步请求时有个双重条件判断,满足会立即执行,否则添加到等待队列
runningAsyncCalls.size() < maxRequests
运行中的异步请求数量小于MaxRequest
(默认64)
runningCallsForHost(call) < maxRequestsPerHost
指向的host当前连接请求数小于maxRequestsPerHost
(默认5)
request结束后的队列优化
- 异步请求结束时
void finished(AsyncCall call) {finished(runningAsyncCalls, call, true);}
- 同步请求结束时
void finished(RealCall call) {finished(runningSyncCalls, call, false);}
关注finished(Deque
方法, T, boolean)
private void finished(Deque calls, T call, boolean promoteCalls) {
······
synchronized (this) {
if (!calls.remove(call)) throw new AssertionError("Call wasn't in-flight!");
if (promoteCalls) promoteCalls(); //轮询等待队列中的异步请求
······
}
}
1.首先将结束的请求从相应的队列中移除
2.如果是异步请求,则会刷新异步等待队列,从备用的readyAsyncCalls
中取出AsyncCall
以使其获得执行机会。如下
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);
}
for循环的继续执行条件在这里添加
if (runningAsyncCalls.size() >= maxRequests) return; // Reached max capacity.
}
}
从
readyAsyncCalls
中取出一个AsyncCall
,并添加到runningAsyncCalls
中执行
无论在异步请求asyncCall
或者是同步请求execute
中我们都见到了具体的网络执行是在getResponseWithInterceptorChain
方法中去做的。这是一个标准的责任链:
Response getResponseWithInterceptorChain() throws IOException {
// Build a full stack of interceptors.
List 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);
return chain.proceed(originalRequest);
}
注意数据结构,这里采用了ArrayList
来记录所有的拦截器Interceptor
按序排列如下
-
client.interceptors()
→ 通过OkHttpClient#Builder.addInterceptor()
添加的拦截器,通常是做一些添加统一的token之类操作 -
retryAndFollowUpInterceptor
→ 在RealCall()
构造方法中实例化,负责错误重试和请求重定向 -
BridgeInterceptor
→添加网络请求相关的必要的一些请求头,比如Content-Type、Content-Length、Transfer-Encoding、Host、User-Agent等等 -
CacheInterceptor
→处理缓存相关操作,通过OkHttpClient#Builder.cache()
或者setInternalCache()
添加 -
ConnectInterceptor
→ 负责与服务器进行连接的操作 -
networkInterceptors
→ 通过OkHttpClient#Builder.addNetworkInterceptor()
添加的拦截器,注意它和interceptors的顺序不同 -
CallServerInterceptor
→这个拦截器中进行最终的网络请求
最终定义一个拦截器数据链Interceptor.Chain,通过proceed()
与intercept()
完成操作如下所示