Android之OkHttp网络架构源码深入分析(揭开神秘的面纱)
Posted 涂程
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Android之OkHttp网络架构源码深入分析(揭开神秘的面纱)相关的知识,希望对你有一定的参考价值。
okhttp是谁?做什么的?
一个处理网络请求的开源项目,是安卓端最火热的轻量级框架,
用于替代HttpUrlConnection和Apache HttpClient(android API23 6.0里已移除HttpClient,现在已经打不出来)
【OkHttp网络架构源码深入】
功能:
get,post请求
文件的上传下载
加载图片(内部会图片大小自动压缩)
支持请求回调,直接返回对象、对象集合
支持session的保持
优点:
允许连接到同一个主机地址的所有请求,提高请求效率
共享Socket,减少对服务器的请求次数
通过连接池,减少了请求延迟
缓存响应数据来减少重复的网络请求
减少了对数据流量的消耗
自动处理GZip压缩
googleplay推荐使用的网络框架
那这篇文章就分析OkHttp源码和简单用法
一、基本使用
同步请求
jason
// (1)创建 OkHttpClient 对象OkHttpClient client = new OkHttpClient();
// (2)创建 Request 对象
Request request = new Request.Builder()
.url(url)
.build();
// (3)创建 Call 对象。
Call call = client.newCall(request);
// (4)发送请求并获取服务器返回的数据
Response response = call.execute();
// (5)取出相应的数据
String data = response.body().string();
异步请求
jason
// (1)创建 OkHttpClient 对象OkHttpClient client = new OkHttpClient();
// (2)创建 Request 对象
Request request = new Request.Builder()
.url(url)
.build();
// (3)创建 Call 对象。
Call call = client.newCall(request);
// (4)发送请求并获取服务器返回的数据
call.enqueue(new Callback() {
@Override
public void onFailure(Call call, IOException e) {
}
@Override
public void onResponse(Call call, Response response) throws IOException {
// (5)取出相应的数据
String data = response.body().string();
}
});
二、源码分析
简易图
2.1OkHttpClient 对象
OkHttpClient(Builder builder) {
this.dispatcher = builder.dispatcher;
this.proxy = builder.proxy;
this.protocols = builder.protocols;
this.connectionSpecs = builder.connectionSpecs;
this.interceptors = Util.immutableList(builder.interceptors);
this.networkInterceptors = Util.immutableList(builder.networkInterceptors);
this.eventListenerFactory = builder.eventListenerFactory;
this.proxySelector = builder.proxySelector;
this.cookieJar = builder.cookieJar;
this.cache = builder.cache;
this.internalCache = builder.internalCache;
this.socketFactory = builder.socketFactory;
boolean isTLS = false;
for (ConnectionSpec spec : connectionSpecs) {
isTLS = isTLS || spec.isTls();
}
if (builder.sslSocketFactory != null || !isTLS) {
this.sslSocketFactory = builder.sslSocketFactory;
this.certificateChainCleaner = builder.certificateChainCleaner;
} else {
X509TrustManager trustManager = Util.platformTrustManager();
this.sslSocketFactory = newSslSocketFactory(trustManager);
this.certificateChainCleaner = CertificateChainCleaner.get(trustManager);
}
if (sslSocketFactory != null) {
Platform.get().configureSslSocketFactory(sslSocketFactory);
}
this.hostnameVerifier = builder.hostnameVerifier;
this.certificatePinner = builder.certificatePinner.withCertificateChainCleaner(
certificateChainCleaner);
this.proxyAuthenticator = builder.proxyAuthenticator;
this.authenticator = builder.authenticator;
this.connectionPool = builder.connectionPool;
this.dns = builder.dns;
this.followSslRedirects = builder.followSslRedirects;
this.followRedirects = builder.followRedirects;
this.retryOnConnectionFailure = builder.retryOnConnectionFailure;
this.connectTimeout = builder.connectTimeout;
this.readTimeout = builder.readTimeout;
this.writeTimeout = builder.writeTimeout;
this.pingInterval = builder.pingInterval;
if (interceptors.contains(null)) {
throw new IllegalStateException("Null interceptor: " + interceptors);
}
if (networkInterceptors.contains(null)) {
throw new IllegalStateException("Null network interceptor: " + networkInterceptors);
}
}
在new OkHttpClient()
内部使用构造器模式初始化了一些配置信息:支持协议、任务分发器(其内部包含一个线程池,执行异步请求)、连接池(其内部包含一个线程池,维护connection)、连接/读/写超时时长等信息。
public Builder() {
dispatcher = new Dispatcher();// 分发器
protocols = DEFAULT_PROTOCOLS;// HTTP 协议
connectionSpecs = DEFAULT_CONNECTION_SPECS;// 传输层版本和连接协议
eventListenerFactory = EventListener.factory(EventListener.NONE);// 事件监听工厂
proxySelector = ProxySelector.getDefault();// 代理选择器
cookieJar = CookieJar.NO_COOKIES;// cookie
socketFactory = SocketFactory.getDefault();// socket 工厂
hostnameVerifier = OkHostnameVerifier.INSTANCE;// 主机名字确认
certificatePinner = CertificatePinner.DEFAULT;// 证书链
proxyAuthenticator = Authenticator.NONE;// 代理服务器身份验证
authenticator = Authenticator.NONE;// 源服务器身份验证
connectionPool = new ConnectionPool();// 连接池
dns = Dns.SYSTEM;// 域名
followSslRedirects = true;// 是否遵循 ssl 重定向
followRedirects = true;// 是否遵循重定向
retryOnConnectionFailure = true;// 连接失败的时候是否重试
connectTimeout = 10_000;// 连接超时
readTimeout = 10_000;// 读超时
writeTimeout = 10_000;// 写超时
pingInterval = 0;// HTTP / 2 和 Web 套接字 ping 之间的时间间隔
}
2.2 Request 对象
Request request = new Request.Builder()
.url(url)
.build();
/*Request*/
//...
final HttpUrl url;
final String method;
final Headers headers;
final @Nullable RequestBody body;
final Map<Class<?>, Object> tags;
//...
每一次网络请求都是一个Request,Request是对url,method,header,body的封装,也是对Http协议中请求行,请求头,实体内容的封装
通常我们通过构建折模式来构建一个Request对象来来设置一些请求链接(url)、请求方法(method)、请求头(headers)、请求体(body)、标签(tag,可作为取消请求的标记)
2.3 Call对象
Call call = client.newCall(request);
@Override public Call newCall(Request request) {
return RealCall.newRealCall(this, request, false /* for web socket */);
}
static RealCall newRealCall(OkHttpClient client, Request originalRequest, boolean forWebSocket) {
// Safely publish the Call instance to the EventListener.
RealCall call = new RealCall(client, originalRequest, forWebSocket);
call.eventListener = client.eventListenerFactory().create(call);
return call;
}
RealCall 是 Call 的实现类,Call 定义了请求相关的操作,例如同步异步、取消请求等方法。所以后续的请求相关操作基本都是在调用 Call 定义的方法,而这些方法真正的执行是它的实现类 RealCall
2.4请求数据
请求的整个预览图
同步请求 代码如下:
@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);// (1)
Response result = getResponseWithInterceptorChain();// (2)
if (result == null) throw new IOException("Canceled");
return result;
} catch (IOException e) {
eventListener.callFailed(this, e);
throw e;
} finally {
client.dispatcher().finished(this);// (3)
}
}
/*Dispatcher*/
synchronized void executed(RealCall call) {
runningSyncCalls.add(call);
}
// 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));
// originalRequest:我们写的 request
Interceptor.Chain chain = new RealInterceptorChain(interceptors, null, null, null, 0,
originalRequest, this, eventListener, client.connectTimeoutMillis(),
client.readTimeoutMillis(), client.writeTimeoutMillis());
return chain.proceed(originalRequest);
}
①根据上面源码我们可以知道getResponseWithInterceptorChain()返回了 Response ,由此可见访问网络从服务器获取数据的操作都在getResponseWithInterceptorChain()内
②这里引入了拦截器链,Request 需要通过拦截器链接收相应的处理最后才会发送到服务器并获取服务器返回的响应
拦截器执行顺序
-
应用拦截器:开发者添加的拦截器
-
retryAndFollowUpInterceptor:负责失败重连操作,以及重定向,如果 call 被取消会抛出 IOException
-
BridgeInterceptor:作为网络层与应用层之间的桥梁,把(应用层请求)user request转化为(网络层请求)network request,然后向服务器发送network request,得到(网络层响应)network reseponse后转化为(应用层响应) user response
-
CacheInterceptor:处理缓存中的 requests 以及把 responses 写到缓存
-
ConnectInterceptor:负责与目标服务器建立连接
-
网络拦截器:开发者添加的拦截器
-
CallServerInterceptor:拦截器链的最后一个拦截器,负责向服务器发送请求和从服务器获取响应数据
异步请求代码如下
// RealCall.java
@Override public void enqueue(Callback responseCallback) {
synchronized (this) { // 如果这个 call 已经被执行过,抛异常
if (executed) throw new IllegalStateException("Already Executed");
executed = true;
}
captureCallStackTrace();
eventListener.callStart(this);
client.dispatcher().enqueue(new AsyncCall(responseCallback));
}
AsyncCall call = new AsyncCall(responseCallback);
Dispatcher dispatcher = client.dispatcher();
dispatcher.enqueue(call);
把 responseCallback 封装成 AsyncCall
返回了一个 Dispatcher
调用任务调度器 Dispatcher 的 enqueue() 异步执行 call
解一下 Dispatcher
// Dispatcher.java
public final class Dispatcher {
// 同步请求和异步请求之和最大值
private int maxRequests = 64;
// 同一个 host 请求数最大值
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<AsyncCall> readyAsyncCalls = new ArrayDeque<>();
/** Running asynchronous calls. Includes canceled calls that haven't finished yet. */
/** 正在执行的异步请求,包括已经被取消但未完成的请求 */
private final Deque<AsyncCall> runningAsyncCalls = new ArrayDeque<>();
/** Running synchronous calls. Includes canceled calls that haven't finished yet. */
/** 正在执行的同步请求,包括已经被取消但未完成的请求 */
private final Deque<RealCall> runningSyncCalls = new ArrayDeque<>();
...
public synchronized ExecutorService executorService() {
if (executorService == null) {
// 核心线程数为0,最大线程数为 Integer.MAX_VALUE ,空闲线程最多存活60秒
executorService = new ThreadPoolExecutor(0, Integer.MAX_VALUE, 60, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>(), Util.threadFactory("OkHttp Dispatcher", false));
}
return executorService;
}
synchronized void enqueue(AsyncCall call) {
if (runningAsyncCalls.size() < maxRequests
&& runningCallsForHost(call) < maxRequestsPerHost) {
// 如果正在执行的异步请求数没有达到最大值
// 就放到 runningAsyncCalls 标记为正在执行
runningAsyncCalls.add(call);
// 用 executorService 执行这个异步请求
executorService().execute(call);
} else {
// 如果正在执行的异步请求数达到最大值
// 就放到 readyAsyncCalls 标记为等待执行
readyAsyncCalls.add(call);
}
}
...
}
Dispatcher 是任务调度器,内部建立了一个线程池 ExecutorService ,而且维护了三个集合:
-
readyAsyncCalls : 等待被执行的异步请求集合
-
runningAsyncCalls : 正在执行的异步请求集合,包括已经被取消但未完成的请求
-
runningSyncCalls : 正在执行的同步请求集合,包括已经被取消但未完成的请求
所有异步请求都交由线程池 ExecutorService 来执行。
线程池其实是 ThreadPoolExecutor ,且核心线程数为 0 、最大线程数为Integer.MAX_VALUE、空闲线程存活最大时间为60秒,即所有线程执行完之后空闲60秒就会被销毁,而且存在线程过多导致内存溢出问题等问题,但是在 Dispatcher 的调度下是不会发生线程过多情况的,因为 Dispatcher 限制了正在执行的请求数(同步和异步之和)最大为64,同一个host下请求同时存在数最大值为 5 。
线程池会调用线程执行 AsyncCall 的 execute()
// RealCall.java
final class AsyncCall extends NamedRunnable {
...
@Override protected void execute() {
boolean signalledCallback = false;
try {
// 通过拦截器链得到从服务器获取的响应 Response
Response response = getResponseWithInterceptorChain();
// 如果 retryAndFollowUpInterceptor.cancel() 被调用过就报异常
if (retryAndFollowUpInterceptor.isCanceled()) {
signalledCallback = true; // 标记 callback 回调函数已被调用
responseCallback.onFailure(RealCall.this, new IOException("Canceled"));
} else {
// 到这里表示获取响应成功
signalledCallback = true; // 标记 callback 回调函数已被调用
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 {
eventListener.callFailed(RealCall.this, e);
responseCallback.onFailure(RealCall.this, e);
}
} finally {
// 最后要通知 dispatcher 标记该任务已完成
client.dispatcher().finished(this);
}
}
}
AsyncCall 的 execute() 逻辑很简单,getResponseWithInterceptorChain() 我们已经在上篇文章中了解过了,获取 Response 之后只需要判断是回调 responseCallback 的 onFailure() 还是 onResponse(),所以 enqueue() 中的回调方法是在子线程中被调用的,当然最后还要调用 finished() 通知 Dispatcher 该任务已经完成了,需要从runningAsyncCalls中移除该任务。
Dispatcher 的判断就在每个异步任务结束时调用的 finish(call) 内
// Dispatcher.java
// 异步请求
void finished(AsyncCall call) {
finished(runningAsyncCalls, call, true);
}
// 同步请求
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!");
// 异步请求 promoteCalls 为 true,同步请求为 false
if (promoteCalls) promoteCalls();
runningCallsCount = runningCallsCount();
idleCallback = this.idleCallback;
}
if (runningCallsCount == 0 && idleCallback != null) {
idleCallback.run();
}
}
public synchronized int runningCallsCount() {
// 返回所有正在运行的同步异步请求总数
return runningAsyncCalls.size() + runningSyncCalls.size();
}
private void promoteCalls() {
// 1.当正在运行的同步请求异步请求数大于64时直接 return
if (runningAsyncCalls.size() >= maxRequests) return; // Already running max capacity.
// 2.没有等待执行的异步请求的时候 return
if (readyAsyncCalls.isEmpty()) return; // No ready calls to promote.
// 3.可以从 readyAsyncCalls 中取任务放到 runningAsyncCalls 中并执行
for (Iterator<AsyncCall> i = readyAsyncCalls.iterator(); i.hasNext(); ) {
// 从迭代器获取下一个等待的异步任务
AsyncCall call = i.next();
// 对同一个 host 请求数不能超过 5
if (runningCallsForHost(call) < maxRequestsPerHost) {
// 从 readyAsyncCalls 中删除 call
i.remove();
// 把 call 添加到 runningAsyncCalls
runningAsyncCalls.add(call);
// 使用线程池执行 call
executorService().execute(call);
}
// 一直执行 for 循环直到 runningAsyncCalls 数达到 64 个
if (runningAsyncCalls.size() >= maxRequests) return; // Reached max capacity.
}
}
复制代码
同步请求和异步请求执行后最终都会调用 dispatcher.finish(),内部执行的区别是异步请求会调用 promoteCalls(),目的就是对 readyAsyncCalls 和 runningAsyncCalls 进行调度
2.5拦截器
拦截器代码如下:
/*RealCall*/
Response getResponseWithInterceptorChain() throws IOException {
// 创建一个拦截器集合
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));
// (1) 构建责任链
Interceptor.Chain chain = new RealInterceptorChain(interceptors, null, null, null, 0,
originalRequest, this, eventListener, client.connectTimeoutMillis(),
client.readTimeoutMillis(), client.writeTimeoutMillis());
// (2) 处理责任链中的拦截器
return chain.proceed(originalRequest);
}
可以看到,这里用到了很多拦截器,将这些拦截器构建成一条责任链,然后再一个个处理。这里用到了责任链模式,每个拦截器负责相应的功能,上一个拦截器完成会传给下一个拦截器,直到最后一个拦截器执行完再一层层向上返回 Response。
其实拦截器的知识点很复杂,以后会单独出一篇关于拦截器的文章。
总结
看完源码,发现 OkHttp 是一个设计得非常优秀的框架。该框架运用了很多设计模式,例如建造者模式、责任链模式等等。知道了 OkHttp 的核心是拦截器,这里采用的就是责任链模式,每个拦截器负责相应的功能,发起请求的时候由上往下依次执行每个拦截器,响应的数据则层层往上传递。
最后
笔者在面试前,从网上收集了一些 Android 开发相关的学习文档、面试题、Android 核心笔记等等文档,进行了复习,在此分享给大家,希望能帮助到大家学习提升,如有需要参考的可以直接去我 GitHub地址:https://codechina.csdn.net/u012165769/Android-T3 访问查阅。
以上是关于Android之OkHttp网络架构源码深入分析(揭开神秘的面纱)的主要内容,如果未能解决你的问题,请参考以下文章
Android 图形架构 之七——Choreographer 源码分析