吐槽
自己也要进入安卓全面学习状态了,这块网络请求之前自己用的一点也不好,写项目的时候发行自己各种问题和后台交互的时候,而且自己源码什么的还没开始看,就很尴尬,就拿这个okhttp当自己第一个阅读的源码吧。
本文思维导图
//以后写东西的时候都先拿思维导图构建好思路
本文前的准备什么的,我之前写的博客里面有
之前写的博客
Get请求
同步的方法
先看整体的代码
同步GET的意思是一直等待http请求, 直到返回了响应. 在这之间会阻塞进程, 所以通过get不能在Android的主线程中执行, 否则会报错.
private void initSyncData(){
new Thread(new Runnable() {
@Override
public void run() {
try {
Request request = new Request.Builder().url("http://www.baidu.com").build();
Response response = client.newCall(request).execute();
result = response.body().string();
runOnUiThread(new Runnable() {
@Override
public void run() {
textView.setText(result);
Log.d("233","sync");
}
});
} catch (IOException e) {
e.printStackTrace();
}
}
}).start();
}
1.先实例化okhttp,构建一个request,使用的是get方式,放入一个url地址就可以了,也可以通过Request.Builder设置更多的参数。
2.然后通过client发起一个请求,放入队列。等待任务完成,在Callback中取结果。
3.通过response.body().string()获取返回来的字符串。
这个body()其实就是ResponseBody的对象
先看
第一句:
OkHttpClient client=new OkHttpClient(); //创建一个okhttpClient实例
然后我进入OkHttpClient这个类的源码时候,,,,我直接懵逼了,都什么鬼啊啊啊啊,看的我一脸懵逼,然后我还是继续坚持看下去吧。
public class OkHttpClient implements Cloneable, Call.Factory, WebSocket.Factory {
static final List<Protocol> DEFAULT_PROTOCOLS = Util.immutableList(
Protocol.HTTP_2, Protocol.HTTP_1_1);
static final List<ConnectionSpec> DEFAULT_CONNECTION_SPECS = Util.immutableList(
ConnectionSpec.MODERN_TLS, ConnectionSpec.CLEARTEXT);
...........
final Dispatcher dispatcher;
final @Nullable Proxy proxy;
final List<Protocol> protocols;
final List<ConnectionSpec> connectionSpecs;
final List<Interceptor> interceptors;
final List<Interceptor> networkInterceptors;
final EventListener.Factory eventListenerFactory;
final ProxySelector proxySelector;
final CookieJar cookieJar;
final @Nullable Cache cache;
final @Nullable InternalCache internalCache;
final SocketFactory socketFactory;
final @Nullable SSLSocketFactory sslSocketFactory;
final @Nullable CertificateChainCleaner certificateChainCleaner;
final HostnameVerifier hostnameVerifier;
final CertificatePinner certificatePinner;
final Authenticator proxyAuthenticator;
final Authenticator authenticator;
final ConnectionPool connectionPool;
..........
第二步:
这个总算看到网址了,这块就和网络请求HTTP这块有关系了
Request request = new Request.Builder().url(url) .build(); //创建一个Request实例
先看下Request类的源码,这块终于能看懂了,这个不就是HTTP下的请求报文哈哈哈,啦啦啦
http协议请求消息体分为4部分请求行、请求头部、空行和请求数据
请求行又包含请求方法,请求地址,请求协议
public final class Request {
final HttpUrl url;//请求地址
final String method;//请求方法
final Headers headers;//请求头
final @Nullable RequestBody body;//请求体
final Object tag;//取消http请求的标志
private volatile CacheControl cacheControl; // Lazily initialized.
..........
但是我们发现为啥没有请求协议呢,,,难道图有毒吗
因为HTTP版本的问题
前, Http/1.1在全世界大范围的使用中, 直接废弃跳到http/2肯定不现实. 不是每个用户的浏览器都支持http/2的, 也不是每个服务器都打算支持http/2的, 如果我们直接发送http/2格式的协议, 服务器又不支持, 那不是挂掉了! 总不能维护一个全世界的网站列表, 表示哪些支持http/2, 哪些不支持?
为了解决这个问题, 从稍高层次上来说, 就是为了更方便地部署新协议, HTTP/1.1 引入了 Upgrade 机制. 这个机制在 RFC7230 的「6.7 Upgrade」这一节中有详细描述.
简单说来, 就是先问下你支持http/2么? 如果你支持, 那么接下来我就用http/2和你聊天. 如果你不支持, 那么我还是用原来的http/1.1和你聊天
所以OkHttp使用了请求协议的协商升级, 无论是1.1还是2, 都先只以1.1来发送, 并在发送的信息头里包含协议升级字段. 接下来就看服务器是否支持协议升级了. OkHttp使用的协议升级字段是ALPN, 如果有兴趣, 可以更深入的查阅相关资料.
第三步:
Response response= client.newCall(request).execute();
既然有请求的,肯定有返回响应的,进入Response这个类
public final class Response implements Closeable {
final Request request;//持有的请求
final Protocol protocol;//请求协议
final int code;//响应码
final String message;//描述的信息
final @Nullable Handshake handshake;//SSL/TLS握手协议验证时的信息
final Headers headers;//响应头
final @Nullable ResponseBody body;//响应体
final @Nullable Response networkResponse;
final @Nullable Response cacheResponse;
final @Nullable Response priorResponse;
final long sentRequestAtMillis;
final long receivedResponseAtMillis;
private volatile CacheControl cacheControl; // Lazily initialized.
.........
而且这块newCall(request)点进去发现它是OkHTTPClient.java里面的一个方法
然后继续进去看源码,进入Call这个,发现这个是个接口
public interface Call extends Cloneable {
/** Returns the original request that initiated this call. */
Request request();
.........
void enqueue(Callback responseCallback);
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);
}
}
第四步:
String message=response.body().string();//得到返回的内容
然后我们进入这个方法里面看下,就很简单的返回
然后我们整体看下同步请求的过程
- OkHttpClient实现了Call.Factory接口, 是Call的工厂类
- Call负责发送执行请求和读取响应.
- Request代表Http请求, 通过Request.Builder辅助类来构建.
- client.newCall(request)通过传入一个http request, 返回一个Call调用. 然后执行execute()方法, 同步获得
- Response代表Http请求的响应. response.body()是ResponseBody类, 代表响应体, 可以通过responseBody.string()获得字符串的表达形式, 或responseBody.bytes()获得字节数组的表达形式, 这两种形式都会把文档加入到内存. 也可以通过responseBody.charStream()responseBody.byteStream()返回流来处理.
注意点:
响应体的string()方法对于小文档来说十分方便高效. 但是如果响应体太大(超过1MB), 应避免使用 string()方法, 因为它会将把整个文档加载到内存中.
对于超过1MB的响应body, 应使用流的方式来处理响应body. 这和我们处理xml文档的逻辑是一致的, 小文件可以载入内存树状解析, 大文件就必须流式解析.
异步请求
异步GET是指在另外的工作线程中执行http请求, 请求时不会阻塞当前的线程, 所以可以在Android主线程中使用.
当响应可读时回调Callback接口. 当响应头准备好后, 就会调用Callback接口, 所以读取响应体时可能会阻塞. OkHttp现阶段不提供异步api来接收响应体
先看下源写的异步请求的代码
/**
* 异步请求
*/
private void initAsyncGet(){
new Thread(new Runnable() {
@Override
public void run() {
Request request = new Request.Builder().url("http://www.baidu.com").build();
client.newCall(request).enqueue(new Callback() {
@Override
public void onFailure(Call call, IOException e) {
// call 是一个接口, 是一个准备好的可以执行的request 可以取消,对位一个请求对象,只能单个请求
Log.d("233","请求失败");
}
@Override
public void onResponse(Call call, Response response) throws IOException {
/**
* 通过拿到response这个响应请求,然后通过body().string(),拿到请求到的数据
*这里最好用string() 而不要用toString()
* toString()每个类都有的,是把对象转换为字符串
* string()是把流转为字符串
*/
result = response.body().string();
runOnUiThread(new Runnable() {
@Override
public void run() {
}
});
}
});
}
}).start();
}
然后我们又不断的进去看源码瞅瞅,发现了一些同步请求和异步请求的相同的地方和不同的地方,然后我们根据这个不同就进去看下源码分析下。
从请求开始处理的分析
上面我们刚看来下同步和异步的请求的方式,我们看下他们直接的请求的时候写的方式有点不一样,所以我们就从这块进去看下
先看下同步请求的方式:
Request request = new Request.Builder().url("http://www.baidu.com").build();
Response response = client.newCall(request).execute();
进入client.newClall()源方法发现:
/**
* Prepares the {@code request} to be executed at some point in the future.
*/
@Override public Call newCall(Request request) {
return RealCall.newRealCall(this, request, false /* for web socket */);
}
返回的是RealCall类的一个方法
然后我们看下异步请求的方法:
Request request = new Request.Builder().url("http://www.baidu.com").build();
client.newCall(request).enqueue(new Callback()
然后我们看下还是会调用newCall()这个方法emmmm
所以,其实返回的时候,是返回一个RealCall类
所以,综上可得,RealCall:真正的请求执行者。
然后我们进入RealCall的源代码:
//里面好多都删了了
final class RealCall implements Call {
final OkHttpClient client;
final RetryAndFollowUpInterceptor retryAndFollowUpInterceptor;
private boolean executed;
private RealCall(OkHttpClient client, Request originalRequest, boolean forWebSocket) {
this.client = client;
this.originalRequest = originalRequest;
this.forWebSocket = forWebSocket;
this.retryAndFollowUpInterceptor = new RetryAndFollowUpInterceptor(client, forWebSocket);
}
@Override public Response execute() throws IOException {
synchronized (this) {
..............
}
@Override protected void execute() {
.......................
}
仔细发现这个类里面有execute()方法和enqueue()方法
从它的构造方法可得:传过来一个OkHttpClient对象和一个originalRequest(我们创建的Request)
然后观察execute()方法和enqueue()方法里面发现一个神奇的共同点
先看enqueue()方法的源码
@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));
}
再看execute()这个方法
@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 {
eventListener.callFailed(RealCall.this, e);
responseCallback.onFailure(RealCall.this, e);
}
} finally {
client.dispatcher().finished(this);
}
}
}
然后我们发现这两个方法里面client.dispatcher().enqueue(new AsyncCall(responseCallback));和 client.dispatcher().finished(this);这两句很显眼啊
所以,发现最终的请求控制是由dispatcher()进行的
- 利用 client.dispatcher().executed(this) 来进行实际执行,dispatcher 是刚才看到的 OkHttpClient.Builder 的成员之一,它的文档说自己是异步 HTTP请求的执行策略,现在看来,同步请求它也有掺和。
- 调用 getResponseWithInterceptorChain() 函数获取 HTTP 返回结果,从函数名可以看出,这一步还会进行一系列“拦截”操作
- 最后还要通知 dispatcher 自己已经执行完毕。
dispatcher 这里我们不过度关注,在同步执行的流程中,涉及到 dispatcher 的内容只不过是告知它我们的执行状态,比如开始执行了(调用 executed),比如执行完毕了(调用 finished),在异步执行流程中它会有更多的参与。
真正发出网络请求,解析返回结果的,还是 getResponseWithInterceptorChain
Dispather任务调度
点Dispather这个类进去发现有好多的英语emmmmmm还是先看下吧
主要变量:
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<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<>();//正在运行的同步请求
然后再看下Dispatcher的构造方法
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<Runnable>(), Util.threadFactory("OkHttp Dispatcher", false));
}
return executorService;
}
- Dispatcher有两个构造方法,可以自己设定的线程池,如果没有,就用默认创建的线程池
- 当RealCall调用enqueue其实是调用Dispatcher类的enqueue方法
然后我们再看下Dispatcher类的enqueue方法的源代码
synchronized void enqueue(AsyncCall call) {
if (runningAsyncCalls.size() < maxRequests && runningCallsForHost(call) < maxRequestsPerHost) {
runningAsyncCalls.add(call);
executorService().execute(call);
} else {
readyAsyncCalls.add(call);
}
}
然后发现这段代码很有意思
- 当正在运行的异步请求数量小于64并且正在运行的请求主机数小于5的时候,把请求加入 runningAsyncCalls中并在线程池里面,否则就加入等待队列里面
- 这个函数传进来的是一个AsyncCall类对象,它是RealCall的内部类,内部也实现了execute方法
然后我们继续进去看下AsyncCall类里面的execute方法
@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 {
eventListener.callFailed(RealCall.this, e);
responseCallback.onFailure(RealCall.this, e);
}
} finally {
client.dispatcher().finished(this);
}
}
}
反正不论怎么样都会调用 client.dispatcher().finished(this);这句代码
感觉这句代码看到很多次了
然后我们点进去看下finished(this)这个方法
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!");
if (promoteCalls) promoteCalls();
runningCallsCount = runningCallsCount();
idleCallback = this.idleCallback;
}
if (runningCallsCount == 0 && idleCallback != null) {
idleCallback.run();
}
}
发现finished()方法将这次请求从ruanningAsyncCalls移除这个请求
然后执行promoteCalls();方法,我们进去看下这个方法
private void promoteCalls() {
if (runningAsyncCalls.size() >= maxRequests) return; // Already running max capacity.
if (readyAsyncCalls.isEmpty()) return; // No ready calls to promote.
for (Iterator<AsyncCall> 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.
}
}
- 如果正在运行的异步线程队列大于最大并发请求数的时候,返回
- 如果准备进行异步请求的队列是空的话也返回
- 最关键的一点是readyAsyncCalls取出一个请求加入runningAsyncCalls队列里面,然后线程池进行处理
然后我们再返回再看下AsyncCall的execute()方法
@Override protected void execute() {
boolean signalledCallback = false;
try {
Response response = getResponseWithInterceptorChain();
if (retryAndFollowUpInterceptor.isCanceled()) {
。。。。。。。。。。。。
}
getResponseWithInterceptorChain()返回了Response,这块在请求网络 获得response。
然后,我们综上可得,,,,getResponseWithInterceptorChain()才是真正发送网络请求和解析返回结果的emmmmm
然后,这一篇先到这,写不动了写不动了,看的人脑壳疼
总结
先把上面总结的流程整理下:
1先是把异步和同步的请求的时候的过程走了一遍,然后发现这个RealCall这个类
2然后再这个类里面发现请求又是由Dispatcher类进行调度的
3然后在这个Dispatcher类里面有准备请求队列和正在请求队列,发行传入的是AsyncCall这个类的对象
4AsyncCall这个类发现它也有execute()方法,不论怎样都会执行 client.dispatcher().finished(this)这段代码
5然后进去看了下finished()方法,又从这个方法进入promoteCalls方法,在promoteCalls方法里面,会把准备请求队列的取出一个请求扔进正在请求的队列,然后交给线程池处理
6最后重新返回AsyncCall类的execute方法。发现其实getResponseWithInterceptorChain方法返回了Response,它才是真正的请求者
所以,综上可得,getResponseWithInterceptorChain()才是真正发送网络请求和解析返回结果的emmmmm
真的是脑壳痛啊啊啊啊