导语
学过Android开发的同学都知道,我们使用网络请求获取服务器的数据时,通常使用的是封装好的Retrofit框架,这个框架很好的帮助我们对网络的发起,以及返回的数据进行操作,我们使用起来十分方便,对于Retrofit来说,我们仅仅看到了它的表面,如何正确使用等,其内部还是要借助OkHtttp来请求网络的,Retrofit只是对OkHttp进行了再次的封装,而且Retrofit不具备网络请求功能,只是在OkHtttp的外表又套了一层,对返回的数据支持RxJava转换和Gson解析。真正起到网络请求的还是OkHttp,所以要了解里面的实质,我们还是着手从OkHttp出发,来探索它对网络的认知和对数据的传递。
OkHttp使用方式
要想了解其原理,首先得学会使用它,OkHttp的使用也非常简单。
从请求方式来讲,分为 get 和 post
get请求的同步和异步
// 构建一个OkHttpClient对象
OkHttpClient client = new OkHttpClient.Builder()
.build();
// 创建一个Request请求对象
Request request = new Request.Builder()
.get()
.url("https://www.baidu.com/")
.build();
// 把request对象 通过 newCall 转换成call
Call call = client.newCall(request);
try {
// 通过call来发起网络请求
// 同步请求
Response response = call.execute();
//返回响应体
ResponseBody body = response.body();
System.out.println(body.string());
} catch (IOException e) {
e.printStackTrace();
}
// 通过call来发起网络请求
// 异步请求
call.enqueue(new Callback() {
@Override
public void onFailure(Call call, IOException e) {
}
@Override
public void onResponse(Call call, Response response) throws IOException {
// 返回响应体
ResponseBody body = response.body();
System.out.println(body.string());
}
});
上面的步骤也非常清楚:
- 构建一个OkHttpClient对象,通过Builder来构建我们的client,我们自由配置client。(添加拦截器,超时时间等)
- 创建一个Request请求对象,Request对象通过构建者模式创建,我们可以自由地配置Request对象。
- 把request对象 通过 newCall 转换成call。
- 通过call来发起网络请求,同步请求使用execute,异步请求使用enqueue。
- 通过response.body()来获取响应体。
post请求的同步和异步
OkHttpClient client = new OkHttpClient.Builder()
.build();
// 表单格式构建 RequestBody
RequestBody requestBody = new FormBody.Builder()
.add("username", "admin")
.add("password", "123456")
.build();
Request request = new Request.Builder()
.post(requestBody)
.url("https://www.baidu.com/")
.build();
Call call = client.newCall(request);
try {
Response response = call.execute();
ResponseBody responseBody = response.body();
System.out.println(responseBody.string());
} catch (IOException e) {
e.printStackTrace();
}
call.enqueue(new Callback() {
@Override
public void onFailure(Call call, IOException e) {
}
@Override
public void onResponse(Call call, Response response) throws IOException {
ResponseBody responseBody = response.body();
System.out.println(responseBody.string());
}
});
post请求与get请求的唯一区别就是:post请求通过RequestBody来构建一个请求体,Body里面带上我们要请求的参数;而get请求的参数是拼接在url后面。
了解了OkHttp的使用,我们梳理下整个OkHttp的调用过程。

不管我们通过execute还是enqueue,都会经过Dispatcher(分发器)和 Interceptors(拦截器)来获得Response。
分发器到底做了什么事情,接下来我们深入地了解分发器内部的原理。
分发器—Dispatcher
我们从上面的代码中知道,真正请求网络的是一个call对象,call是一个接口,通过RealCall实现,我们通过newCall(request)构建的这个call,通过execute或enqueue就能获得response,为何,进入execute看下:
@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);
Response result = getResponseWithInterceptorChain();
if (result == null) throw new IOException("Canceled");
return result;
} catch (IOException e) {
eventListener.callFailed(this, e);
throw e;
} finally {
client.dispatcher().finished(this);
}
}
可以看到,调用了分发器的executed,然后通过getResponseWithInterceptorChain()获得响应。
synchronized void executed(RealCall call) {
runningSyncCalls.add(call);
}
将call加入running队列。
对于同步请求,分发器并没有做什么事情。我们分析下异步请求的分发器干了什么事情。
@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));
}
调用了分发器的enqueue,注意这里:分发器将Callback包装成AsyncCall来传给了enqueue。AsyncCall继承自NamedRunnable,而NamedRunnable又实现了Runnable,所以AsyncCall本质上就是一个Runnable。AsyncCall交给了分发器:
synchronized void enqueue(AsyncCall call) {
if (runningAsyncCalls.size() < maxRequests && runningCallsForHost(call) < maxRequestsPerHost) {
runningAsyncCalls.add(call);
executorService().execute(call);
} else {
readyAsyncCalls.add(call);
}
}
private int maxRequests = 64;
private int maxRequestsPerHost = 5;
这里通过两个条件判断:
-
正在执行的running队列里面的任务数小于最大请求数(64)以及同一Host的请求数小于最大同一Host请求数(5)时,将call加入running队列,然后交给线程池处理。否则,将AsyncCall加入ready队列。
分别解释下这两个条件:
- 第一个条件:充分考虑到客户端的压力,如果没有这个64的限制,客户端不停的进行网络请求,这样会让客户端的压力特别大。
- 第二个条件:充分考虑到服务器的压力,如果同一个Host的服务器,没有这个限制,对于一个客户端就建立64次连接,如果有多个客户端同时建立连接的话会撑爆服务器。
-
如果把任务放在ready队列后,这个队列里的任务怎么执行,什么时候执行?
前面说到,正在执行的任务会交给线程池处理,当线程池处理完之后,会finish掉这个任务。由于AsyncCall本质上就是一个Runnable,所以会调用run方法,而run方法里面又调用了execute方法,execute方法是一个抽象方法,所以在分发器里实现如下:
public abstract class NamedRunnable implements Runnable {
protected final String name;
public NamedRunnable(String format, Object... args) {
this.name = Util.format(format, args);
}
@Override public final void run() {
String oldName = Thread.currentThread().getName();
Thread.currentThread().setName(name);
try {
execute();
} finally {
Thread.currentThread().setName(oldName);
}
}
protected abstract void execute();
}
@Override protected void execute() {
boolean signalledCallback = false;
try {
// 执行请求(拦截器)
Response response = getResponseWithInterceptorChain

本文详细解析了OkHttp的使用方式,包括get和post请求的同步异步处理,重点剖析了分发器Dispatcher和拦截器Interceptors的工作原理,以及责任链模式在重试、重定向和默认拦截器中的应用。
最低0.47元/天 解锁文章
&spm=1001.2101.3001.5002&articleId=121680507&d=1&t=3&u=471b9f10fad44b43a18a85a52910ecb5)
1379

被折叠的 条评论
为什么被折叠?



