android 网络请求中的同步和异步请求,由浅入深了解OkHttp二:OkHttp同步/异步请求-Go语言中文社区...

此节我们深入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()方法同步请求

同步请求发送之后,就会进入阻塞状态,直到收到响应。而且也只有同步请求会阻塞,异步请求是新开的线程做网络请求

1d822416bc7332066e483853288908f7.png

需要注意的的是:

异步请求的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

a91f2b3be53a7a7f446f0d7998b79362.png

如上图: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(), 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持有一个正在执行的请求队列、一个等待执行的请求队列,一个线程池。这三个来完整的处理异步请求操作

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值