本文也作了一次标题党,哈哈,其实写的仍是很水,各位原谅我O(∩_∩)O。html
介于本身的网络方面知识烂的一塌糊涂,因此准备写相关网络的文章,可是考虑所有写在一篇太长了,因此分开写,但愿你们能仔细看,最好能够指出个人错误,让我也能纠正。java
1.讲解相关的整个网络体系结构:web
2.讲解相关网络的重要知识点,好比不少人都听过相关网络方面的名词,可是仅限于听过而已,什么tcp ,udp ,socket ,websocket, http ,https ,而后webservice是啥,跟websocket很像,socket和websocket啥关系长的也很像,session,token,cookie又是啥。api
Android技能树 — 网络小结(2)之TCP/UDP缓存
Android技能树 — 网络小结(3)之HTTP/HTTPSbash
Android技能树 — 网络小结(4)之socket/websocket/webservice服务器
相关网络知识点小结- cookie/session/token(待写)websocket
3.相关的第三方框架的源码解析,毕竟如今面试个大点的公司,okhttp和retrofit源码是必问的。cookie
Android技能树 — 网络小结(6)之 OkHttp超超超超超超超详细解析
Android技能树 — 网络小结(7)之 Retrofit源码详细解析
这里提一个本文无关的小知识点,不少文章开头都会提到,咱们以okhttp3.xxx版原本讲解,那怎么看当前最新的已是几了呢?(主要之前也有人问过我在哪里查看xxx第三方库最新的版本,因此想到提一下这个)其实很简单,咱们以okhttp为例:
看不清楚的,能够右键,选择新标签页中打开,而后点击图片放大
首先咱们来肯定整体大纲:
因此咱们大概就知道了okhttp通常的主要内容为这三大块。
讲解源码前,先写上okhttp基本使用,这样才更方便讲解源码:
String url = "http://www.baidu.com";
//'1. 生成OkHttpClient实例对象'
OkHttpClient okHttpClient = new OkHttpClient();
//'2. 生成Request对象'
Request request = new Request.Builder().url(url).build();
//'3. 生成Call对象'
Call call = okHttpClient.newCall(request);
//'4. 若是要执行同步请求:'
try {
call.execute();
} catch (IOException e) {
e.printStackTrace();
}
//'5. 若是要执行异步请求:'
call.enqueue(new Callback() {
@Override
public void onFailure(Call call, IOException e) {
}
@Override
public void onResponse(Call call, Response response) throws IOException {
}
});
复制代码
咱们来看咱们最刚开始的完整流程图:
而后配合上面第一步的okhttp基本使用,发如今执行同步和异步前,咱们要先准备好OkhttpClient
、Request
、Call
对象。咱们一步步来看相关源码:
咱们上面的代码实例化OkHttpClient对象的代码是:
OkHttpClient okHttpClient = new OkHttpClient();
复制代码
咱们进入查看:
OkHttpClient
除了空参数的构造函数,还有一个传入
Builder
的构造函数,而咱们的
new OkHttpClient()
最终也是调用了传入
Builder
的构造函数,只不过传入默认的
Builder
对象值,以下图所示:
咱们能够看到最后几个值:
......
connectTimeout = 10_000;
readTimeout = 10_000;
writeTimeout = 10_000;
......
复制代码
默认的链接超时,读取超时,写入超时,都为10秒,而后还有其余等默认属性,那咱们加入想要改变这些属性值呢,好比超时时间改成20秒,很简单。咱们不使用默认的Builder
对象便可:
OkHttpClient.Builder builder = new OkHttpClient.Builder();
builder.connectTimeout(20,TimeUnit.SECONDS);
builder.readTimeout(20,TimeUnit.SECONDS);
builder.writeTimeout(20,TimeUnit.SECONDS);
OkHttpClient okHttpClient = builder.build();
//这里不能直接使用那个传入Builder对象的OkHttpClient的构造函数,由于该构造函数的方法不是public的
OkHttpClient okHttpClient = new OkHttpClient(builder);//这样是错误的
builder.build();的源码是:
public OkHttpClient build() {
return new OkHttpClient(this);
}
复制代码
咱们再回过头来看看OkHttpClient里面设置的属性值都有什么用:
Dispatch:分发器,后面会提到
Proxy:设置代理,一般为类型(http、socks)和套接字地址。参考文章:直接使用Proxy建立链接
ProxySelector: 设置全局的代理,经过继承该类,设置具体代理的类型、地址和端口。参考文章:Java代理 经过ProxySelector设置全局代理
Protocol: 网络协议类,好比咱们常常听到的http1.0、http1.一、http2.0协议等。
ConnectionSpec: 指定HTTP流量经过的套接字链接的配置。咱们直接能够翻译该类头部的英文介绍,具体的内容原谅我也不是很懂:
Interceptor:拦截器,后面会提到
EventListener:指标事件的监听器。扩展此类以监视应用程序的HTTP调用的数量,大小和持续时间。全部start/connect/acquire事件最终都会收到匹配的end /release事件,要么成功(非null参数)要么失败(非null throwable)。每一个事件对的第一个公共参数用于在并发或重复事件的状况下连接事件,例如dnsStart(call,domainName);和dnsEnd(call,domainName,inetAddressList); 咱们能够看到一系列的xxxStart和xxxEnd方法:
CookieJar:向传出的HTTP请求添加cookie,收到的HTTP返回数据的cookie处理。
Cache:网络缓存,okhttp默认只能设置缓存GET请求,不缓存POST请求,毕竟POST请求不少都是交互的,缓存下来也没有什么意义。
InternalCache:Okhttp内部缓存的接口,咱们直接使用的时候不须要去实现这个接口,而是直接去使用上面的Cache类。
SocketFactory:从字面意思就看的出来,Android 自带的Socket的工厂类。
参考文章: 类SocketFactory
SSLSocketFactory:Android自带的SSLSocket的工厂类。
参考文章:Java SSLSocket的使用
用SSLSocketFactory 链接https的地址
CertificateChainCleaner:不是很了解,因此仍是老样子,经过谷歌翻译,翻译该类的顶部备注说明:
HostnameVerifier:字面意思,Host name 验证,这个一个基础接口,并且只有一个方法:
/**
* Verify that the host name is an acceptable match with
* the server ‘s authentication scheme.
*
* @param hostname the host name
* @param session SSLSession used on the connection to host
* @return true if the host name is acceptable
*/
public boolean verify(String hostname, SSLSession session);
复制代码
咱们查看Request代码:
public final class Request {
final HttpUrl url; //网络请求路径
final String method; //get、post.....
final Headers headers;//请求头
final @Nullable RequestBody body;//请求体
/**
你能够经过tags来同时取消多个请求。
当你构建一请求时,使用RequestBuilder.tag(tag)来分配一个标签。
以后你就能够用OkHttpClient.cancel(tag)来取消全部带有这个tag的call。.
*/
final Map<Class<?>, Object> tags;
.......
.......
.......
}
复制代码
这个估计不少人都清楚,若是对请求头请求体等不清楚的,能够看下之前咱们这个系列的文章:Android技能树 — 网络小结(3)之HTTP/HTTPS
咱们能够看到咱们生成的Request实例,会传给OkHttpClient实例的newÇall方法:
Request request = new Request.Builder().url(url).build();
Call call = okHttpClient.newCall(request);
call.execute();或者 call.enqueue(....);
复制代码
咱们Request和OkHttpClient大体都了解过了,咱们来具体看下newCall执行了什么和Call的具体内容。
Call类代码:
@Override public Call newCall(Request request) {
return RealCall.newRealCall(this, request, false /* for web socket */);
}
RealCall类代码:
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的实例,同时把咱们各类参数都配置好的OkHttpClient和Request都传入了。
因此后面call.execute()/call.enqueue()
都是执行的RealCall的相对应的方法。但目前位置咱们上面的图已经讲解好了,我这里再贴一次:
恭喜你,下次别人考你Okhttp前面的相关参数配置方面的代码你已经都理解了。
咱们继续看咱们的流程图下面的内容:
咱们先来说同步执行:
@Override
public Response execute() throws IOException {
synchronized (this) {
if (executed) throw new IllegalStateException("Already Executed");
executed = true;
}
captureCallStackTrace();
eventListener.callStart(this);
try {
//'1. 执行了dispatcher的executed方法'
client.dispatcher().executed(this);
//'2. 调用了getResponseWithInterceptorChain方法'
Response result = getResponseWithInterceptorChain();
if (result == null) throw new IOException("Canceled");
return result;
} catch (IOException e) {
eventListener.callFailed(this, e);
throw e;
} finally {
//'3. 最后必定会执行dispatcher的finished方法'
client.dispatcher().finished(this);
}
}
复制代码
咱们一步步来具体看,第一步看Dispatcher类中的executed方法了:
/** Used by {@code Call#execute} to signal it is in-flight. */
synchronized void executed(RealCall call) {
runningSyncCalls.add(call);
}
复制代码
能够看到把咱们的RealCall加入到了一个同步线程runningSyncCalls
中,而后中间调用了getResponseWithInterceptorChain
方法*(这个第二个操做咱们会放在后面很具体的讲解),咱们既然加入到了一个同步线程中,确定用完了要移除,而后第三步finished方法会作处理:
/** Used by {@code Call#execute} to signal completion. */
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语句里面咱们能够看到这里把咱们的队列中移除了call对象'
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
里面的enqueue
代码:
@Override public void enqueue(Callback responseCallback) {
//'1. 这里有个同步锁的抛异常操做'
synchronized (this) {
if (executed) throw new IllegalStateException("Already Executed");
executed = true;
}
captureCallStackTrace();
eventListener.callStart(this);
//'2. 调用Dispatcher里面的enqueue方法'
client.dispatcher().enqueue(new AsyncCall(responseCallback));
}
复制代码
咱们一步步来看,第一个同步锁抛异常的操做,咱们知道一个Call应对一个网络请求,加入你这么写是错误的:
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对象再次发起请求'
call.enqueue(new Callback() {
@Override
public void onFailure(Call call, IOException e) {}
@Override
public void onResponse(Call call, Response response) throws IOException {}
});
复制代码
同一个Call对象,同时请求了二次。这时候就会进入咱们的同步锁判断,只要一个执行过了,里面 executed会为true
,也就会抛出异常。
咱们再来看第二步操做:
咱们知道异步请求,确定会表明不少请求都在各自的线程中去执行,那么咱们在不看OkHttp源码前,让你去实现,你怎么实现,是否是第一个反应是使用线程池。
Java/Android线程池框架的结构主要包括3个部分
1.任务:包括被执行任务须要实现的接口类:Runnable 或 Callable
2.任务的执行器:包括任务执行机制的核心接口类Executor,以及继承自Executor的EexcutorService接口。
3.执行器的建立者,工厂类Executors
复制代码
具体能够参考:Android 线程池框架、Executor、ThreadPoolExecutor详解
client.dispatcher().enqueue(new AsyncCall(responseCallback));
,再也不是像同步操做同样,直接把RealCall传入,而是传入一个AsyncCall
对象。没错,按照咱们上面提到的线程池架构,任务是使用Runnable 或 Callable接口
,咱们查看AsyncCall的代码:
final class AsyncCall extends NamedRunnable {
......
......
}
public abstract class NamedRunnable implements Runnable {
.......
.......
}
复制代码
果真如咱们预计,是使用了Runnable接口。
client.dispatcher().enqueue(new AsyncCall(responseCallback));
,再也不是像同步操做同样,直接把RealCall传入,而是传入一个AsyncCall
对象。
调用Dispatcher里面的enqueue方法:
synchronized void enqueue(AsyncCall call) {
//'1. 判断当前异步队列里面的数量是否小于最大值,当前请求数是否小于最大值'
if (runningAsyncCalls.size() < maxRequests && runningCallsForHost(call) < maxRequestsPerHost) {
//'2. 若是没有大于最大值,则将call加入到异步请求队列中'
runningAsyncCalls.add(call);
//'3. 而且运行call的任务'
executorService().execute(call);
} else {
readyAsyncCalls.add(call);
}
}
复制代码
我么直接看第三步,按照咱们上面提到过的Java/Android线程池框架的结构主要包括3个部分,能够看到执行咱们的Runnable对象的,说明他是一个任务执行器,也就是Executor的继承类。说明executorService()返回了一个Executor的实现类,咱们点进去查看:
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;
}
复制代码
果真建立一个可缓存线程池,线程池的最大长度无限制,但若是线程池长度超过处理须要,可灵活回收空闲线程,若无可回收,则新建线程。
那咱们知道是线程池执行了Runnable的任务,那咱们只要具体看咱们的okhttp的Runnable到底执行了什么便可:
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 {
//'1. 咱们能够发现最后线程池执行的任务就是getResponseWithInterceptorChain方法'
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 {
eventListener.callFailed(RealCall.this, e);
responseCallback.onFailure(RealCall.this, e);
}
} finally {
//'2. 最后再从Dispatcher里面的异步队列中移除'
client.dispatcher().finished(this);
}
}
}
复制代码
咱们发现无论是异步仍是同步,都是同样的三部曲:1.加入到Dispatcher里面的同步(或异步)队列,2.执行getResponseWithInterceptorChain方法,3.从Dispatcher里面的同步(或异步)队列移除。(只不过同步操做是直接运行了getResponseWithInterceptorChain方法,而异步是经过线程池执行Runnable再去执行getResponseWithInterceptorChain方法)
咱们在前面已经知道了无论是异步请求仍是同步请求,都会去执行 RealCall
的getResponseWithInterceptorChain
操做:
Response getResponseWithInterceptorChain() throws IOException {
// Build a full stack of interceptors.
//'1. 建立一个拦截器List'
List<Interceptor> interceptors = new ArrayList<>();
//'2. 添加用户本身建立的应用拦截器'
interceptors.addAll(client.interceptors());
//'3. 添加剧试与重定向拦截器'
interceptors.add(retryAndFollowUpInterceptor);
//'4. 添加内容拦截器'
interceptors.add(new BridgeInterceptor(client.cookieJar()));
//'4. 添加缓存拦截器'
interceptors.add(new CacheInterceptor(client.internalCache()));
/'5. 添加链接拦截器'
interceptors.add(new ConnectInterceptor(client));
if (!forWebSocket) {
//'6. 添加用户本身建立的网络拦截器'
interceptors.addAll(client.networkInterceptors());
}
//'7. 添加请求服务拦截器'
interceptors.add(new CallServerInterceptor(forWebSocket));
//'8.把这些拦截器们一块儿封装在一个拦截器链条上面(RealInterceptorChain)'
Interceptor.Chain chain = new RealInterceptorChain(interceptors, null, null, null, 0,
originalRequest, this, eventListener, client.connectTimeoutMillis(),
client.readTimeoutMillis(), client.writeTimeoutMillis());
//'9.而后执行链条的proceed方法'
return chain.proceed(originalRequest);
}
复制代码
咱们先无论具体的拦截器的功能,咱们先来看总体的执行方式,因此咱们直接来看拦截器链条的工做模式:
public final class RealInterceptorChain implements Interceptor.Chain {
//'咱们刚才创建的放拦截器的队列'
private final List<Interceptor> interceptors;
//'当前执行的第几个拦截器序号'
private final int index;
......
......
......
public Response proceed(Request request, StreamAllocation streamAllocation, HttpCodec httpCodec,
RealConnection connection) throws IOException {
if (index >= interceptors.size()) throw new AssertionError();
......
......
......
//'实例化了一个新的RealInterceptorChain对象,而且传入相同的拦截器List,只不过传入的index值+1'
RealInterceptorChain next = new RealInterceptorChain(interceptors, streamAllocation, httpCodec,
connection, index + 1, request, call, eventListener, connectTimeout, readTimeout,
writeTimeout);
//'获取当前index对应的拦截器里面的具体的某个拦截器, Interceptor interceptor = interceptors.get(index); //而后执行拦截器的intercept方法,同时传入新的RealInterceptorChain对象(主要的区别在于index+1了)'
Response response = interceptor.intercept(next);
......
......
......
return response;
}
}
复制代码
咱们能够看到在RealInterceptorChain类的proceed的方法里面,又去实例化了一个RealInterceptorChain类。不少人可能看着比较绕,不要紧,咱们举个例子简单说下就能够了:
个人写法仍是按照它的写法,写了二个Interceptor,一个用来填充地址AddAddressInterceptor,一个中来填充电话AddTelephoneInterceptor,而后也创建一个拦截链条InterceptorChain。这样我只须要传进去一个字符串,而后会自动按照每一个拦截器的功能,自动帮我填充了地址和电话号码。
Interceptor只负责处理本身的业务功能,好比咱们这里是填充地址和手机号码,而后本身的任务结束就会调用拦截器链条,执行链条接下去的任务,其余跟Interceptor无关。
咱们来看咱们的拦截器和拦截器链条:
电话拦截器:
地址拦截器:
拦截器链:
Activity.java:
最后咱们能够看到咱们的result结果为:
这里额外提下: 里面的拦截器里面的二个大步骤是能够交换顺序的,我先执行拦截链的方法,让它提早去执行下一个拦截器的操做,再拿相应的返回值作我这个拦截器的操做。好比仍是刚才那个电话拦截器,咱们调换了二个的顺序:
这样就会去先执行地址拦截器,而后拿到结果后再去处理电话拦截器的逻辑,因此最后的输出结果为:
这里咱们懂了之后,咱们再去看Okhttp前面提到的拦截器添加,拦截链的相关代码,是否是简单的一比,它的连接链的操做跟咱们的基本架构一致,而后各自的拦截器无非就是处理各自的逻辑,对参数进行更改,发起请求等。因此咱们的核心变成了OkHttp的各个拦截器到底作了什么逻辑。(也就是咱们提到的拦截器中的二个大操做的其中一步,本身的处理逻辑。)
原本想一步步的来写每一个单独的拦截器的做用,后来想了下,单独拦截器的代码分析的文章真的太多太多了。并且每一个拦截器写的很简单,其实没啥大的意义,写的仔细,一个拦截器就能够是一篇文章,而咱们本文也侧重于整体的源码架构,因此我后面若是能够的,都直接引用别人的文章了。
看名字就知道这个拦截器的做用是重试和重定向的。
你们能够参考本文:
OKhttp源码解析---拦截器之RetryAndFollowUpInterceptor
咱们来看BridgeInterceptor
类的说明备注:
什么?看不懂英文,谷歌翻译走起:
简单来讲,咱们本身在Okhttp里面创建了一个Request请求对象,可是这个对象并非直接就能够用来立刻发送网络请求的,毕竟咱们刚开始实例化Request的时候就简单的放入了Url,body等,不少参数都是没有设置的,因此咱们还须要补充不少参数,而后发起网络请求,而后网络返回的参数,咱们再把它封装成Okhttp能够直接使用的对象。
一句话归纳: 将客户端构建的Request对象信息构建成真正的网络请求;而后发起网络请求,最后就是将服务器返回的消息封装成一个Response对象
参考文章:
缓存拦截器,简单来讲就是有缓存就使用缓存。
参考文章:
链接拦截器,顾名思义打开了与服务器的连接,正式开启了网络请求。
由于之前在文章: Android技能树 — 网络小结(4)之socket/websocket/webservice 提到过,咱们的请求是经过Socket去访问的。
因此最终这个ConnectInterceptor也会去发起一个Socket链接请求。
参考文章:
咱们曾经在文章 Android技能树 — 网络小结(2)之TCP/UDP 提过:
TCP要先创建通道,而后再发送数据。
上面的拦截器ConnectInterceptor已经帮咱们把通道创建好了,因此在这个CallServerInterceptor拦截器里面,咱们的任务就是发送相关的数据,
参考文章:
Okhttp之CallServerInterceptor简单分析
咱们在流程图中看到了,除了OKHttp源码里面自带的拦截器,还有二种自定义拦截器,应用拦截器和网络拦截器。
使用代码:
okHttpClient = new OkHttpClient
.Builder()
.addInterceptor(appInterceptor)//Application拦截器
.addNetworkInterceptor(networkInterceptor)//Network拦截器
.build();
复制代码
咱们知道网络请求中间必定要通过一系列的拦截器,咱们也能够本身写拦截器,而后对里面的参数作处理,好比咱们对Request在拦截器中作某个写参数变动,而后再交给下一个拦截器。
而这二个自定义拦截器的位置,在咱们前面分析获取拦截链的方法getResponseWithInterceptorChain中就提过了,如今再拿出来从新说一遍:
Response getResponseWithInterceptorChain() throws IOException {
// Build a full stack of interceptors.
List<Interceptor> interceptors = new ArrayList<>();
'最早添加用户的自定义APPlication拦截器'
interceptors.addAll(client.interceptors());
'而后是一系列的Okhttp自带的拦截器'
interceptors.add(retryAndFollowUpInterceptor);
interceptors.add(new BridgeInterceptor(client.cookieJar()));
interceptors.add(new CacheInterceptor(client.internalCache()));
interceptors.add(new ConnectInterceptor(client));
'在最终跟服务器交互数据的CallServerInterceptor前,添加用户自定义的NetWork拦截器'
'由于若是放在最后就没什么意义了。'
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);
}
复制代码
参考文章:
OkHttp源码写的也比较仓促,特别后面的各个拦截器的源码分析就偷懒了,由于否则会引出一大段一大段的内容,就直接引用其余大佬的文章。若是哪里不对,欢迎你们指出。