如果您对这一系列的文章感兴趣,可以关注此专栏: android网络框架OkHttp+Retrofit源码分析
此节我们深入OkHttp源码了解一下OkHttp是如何执行同步/异步请求的
同步请求:
1、获取OkHttpClient
2、获取Request请求对象
3、获取okhttp3.Call对象
1、获取OkHttpClient:
OkHttp给我们提供了一个门面类OkHttpClient,我们可以使用new OkHttpClient.Builder().build()来进行各种设置:
网络复杂(连接超时/读取超时…)
需要设置其他参数(拦截器/分发器等…)
new OkHttpClient.Builder()
.connectTimeout(10000, TimeUnit.MILLISECONDS)
.readTimeout(10000, TimeUnit.MILLISECONDS)
.build()
2、获取Request请求对象
Request request = new Request.Builder().url("http://www.baidu.com").get().build();
Request 代表着请求的报文信息,比如请求的方法、请求头、请求地址,这个Request也是通过构建者模式来创建
3、获取okhttp3.Call对象
Call requestCall = getOkHttpClient().newCall(request);
这个Call对象,就相当于实际的OkHttp请求,也就是可以把他理解为是Request请求和Response响应的一个桥梁,通过client的newCall()方法,传入我们之前创建好的request对象。
要注意,到这里为止同步请求和异步请求没什么区别,接下来的步骤就是实现的同步或者异步请求的逻辑了。也就是前三步骤只是为了获取实际的请求Call。
4、Call对象的execute()同步请求方法
Response response = requestCall.execute();
//响应成功
if (response.isSuccessful()) {
String json = response.body().string();
}
通过Response的body().string()方法获取返回回来的json数据(也可以是其他类型的数据(XML类型) 这个需要和服务器端商量好)
异步请求:
requestCall.enqueue(new Callback() {
public void onFailure(Call call, IOException e) {}
public void onResponse(Call call, Response response){
}
同步和异步请求最大的区别就是这个第四步骤,因为异步请求调用的则是enqueue()方法,传入CallBack进行数据的成功和失败逻辑
同步请求总结
1、创建OkHttpClient和Requet对象
2、将Request封装成Call对象
3、调用Call的execute()方法同步请求
同步请求发送之后,就会进入阻塞状态,直到收到响应。而且也只有同步请求会阻塞,异步请求是新开的线程做网络请求
需要注意的的是:
异步请求的onResponse方法的返回仍然是子线程,okhttp并没有给我们把响应的数据转换到主线程中,因此我们在这个地方更新UI的时候是会报错的 需要我们手动的转换到主线程 然后才进行数据的解析和UI更新
当然如果使用的是Retrofit,那么这里的线程切换其实Retrofit已经帮我们完成了,完成这部分工作的主要是Executorer这个回调执行器
OkHttp源码解读—>OkHttp同步请求源码解析
1、创建OkHttpClient客户端
这个OkHttpClient对象的创建是使用了建造者模式来构建的,主要有Http请求分发器、连接池等还可以设置超时时间
2、创建Request请求报文信息类
这个Request对象的创建也是一个建造者模式来构建的。通过链式调用指定请求方法,指定头部,请求参数等等设置
3、创建Http请求的实际Call对象
接收okhttpclient request等参数,创建了实现类的对象,并且创建一个重定向拦截器 ,而且不管是同步还是异步,都是通过newCall()这个方法创建的RealCall对象来进行相应的操作
4、execute()方法进行同步请求
Response response = requestCall.execute();
RealCall的实现类覆写的这个execute()方法,因此实际上是RealCall对象执行的这个方法,下面来看一下这个方法里面的核心代码:
//捕捉异常堆栈信息
captureCallStackTrace();
//监听事件开启
eventListener.callStart(this);
try {
//核心代码
client.dispatcher().executed(this);
Response result = getResponseWithInterceptorChain();
if (result == null) throw new IOException("Canceled");
return result;
}
上面的核心代码里面,RealCall会调用分发器分发请求
当把请求call放置到同步请求队列当中 进行请求之后 我们通过getResponseWithInterceptorChain()这个方法来获取相应Response
如上图:Dispatcher会维持Call请求发送过来的状态,并且维护了一个线程池,用于执行网络请求,当Call在执行一个任务的时候,会通过Dispatcher这个分发器推到我们的执行队列中,依次执行任务
Dispatcher分发器在同步请求中做的很简单,就是保存和移除同步请求。对于异步请求,Dispatcher就需要做的好多工作了
OkHttp源码解读—>OkHttp异步请求源码总结
异步和同步的差别只在于Call之后的execute()和enqueue()方法,因此前三步骤这里不再赘述
第四步call执行enqueue方法,实现类是RealCall,代码如下:
synchronized (this) {
//是否已经执行过
if (executed) throw new IllegalStateException("Already Executed");
executed = true;
}//捕捉堆栈信息
captureCallStackTrace();
//开启监听
eventListener.callStart(this);
client.dispatcher().enqueue(new AsyncCall(responseCallback));
同样经过加上同步锁等步骤,最后通过将传入的Callback对象传入AsyncCall()里面创建一个AsyncCall对象实例。
然后realCall通过client.dispatcher方法获取到Dispatcher分发器 传入到刚创建好的Runnable这个实例(AsyncCall对象),最后调用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) 并且共享主机的正在运行的调用的数量小于同时最大的相同Host的请求数(5)
如果都符合就把请求添加到正在执行的异步请求队列当中,然后通过线程池去执行这个请求call,否则的话在就绪(等待)异步请求队列当中添加
关键方法executorService:
public synchronized ExecutorService executorService() {
if (executorService == null) {
//虽然这个地方定义了最大的线程池的数量是Integer.MAX_VALUE 但是我们知道上面对请求数量有了限制(64个)
executorService = new ThreadPoolExecutor(0, Integer.MAX_VALUE,60,
TimeUnit.SECONDS,new SynchronousQueue<Runnable>(), Util.threadFactory("OkHttp Dispatcher", false));
}
return executorService;
}
当创建好ExecutorService这个线程池对象之后,就需要调用execute(call)执行方法执行Runnable,也就是执行AsyncCall里面的run()方法,我们去查找AsyncCall里面的run方法,可以看到没有这个方法,却有一个execute()方法,而这个方法是复写的父类NamedRunnable的方法
protected void execute() {
boolean signalledCallback = false;
try {//拦截器链
Response response = getResponseWithInterceptorChain();
//重定向/重试是否取消
if (retryAndFollowUpInterceptor.isCanceled()) {
signalledCallback = true;
//callback的onFailure()返回 在call.enqueue()里面传进去的
responseCallback.onFailure(RealCall.this, new IOException("Canceled"));
} else {//如果成功 返回结果
signalledCallback = true;
responseCallback.onResponse(RealCall.this, response);
}
} catch (IOException e) {
//网络请求失败的操作...
} finally {
client.dispatcher().finished(this);
}}
上面我们可以看到通过拦截器链得到Response,然后通过重定向拦截器判断是否取消,取消调用callBack的失败方法,没有取消就直接返回结果
最后无论是否取消,都会调用dispatcher的finish方法,其关键代码如下:
synchronized (this) {
//1、调用calls.remove()方法进行删除正在请求的异步线程
if (!calls.remove(call)) throw new AssertionError("Call wasn't in-flight!");
//2、调用promoteCalls()方法调整整个请求队列
if (promoteCalls) promoteCalls();
//3、重新计算正在执行的线程的数量
runningCallsCount = runningCallsCount();
idleCallback = this.idleCallback; }
异步请求总结:
1、判断当前call
这个请求只能被执行一次,如果已经请求过了,就会抛出异常
2、通过传递进来的Callback封装成一个AsyncCall(Runnable)对象
3、获取Dispatcher对象并执行enqueue()异步请求
如果这个AsyncCall请求符合条件(判断实际的运行请求数是否小于允许的最大的请求数量(64) 并且共享主机的正在运行的调用的数量小于同时最大的相同Host的请求数(5)) 才会添加到执行异步请求队列,然后通过线程池进行异步请求否则就把这个AsyncCall请求添加到就绪(等待)异步请求队列当中
这个Dispatcher持有一个正在执行的请求队列、一个等待执行的请求队列,一个线程池。这三个来完整的处理异步请求操作