1okhttp的核心类
首先写一个简单的demo,用于分析:
1.添加网络权限(androidmanifest.xml)
<uses-permission android:name="android.permission.INTERNET"/>
2.添加okhttp库(build.gradle)
implementation "com.squareup.okhttp3:okhttp:3.9.0"
3.修改主页面
public class MainActivity extends AppCompatActivity {
private static String TAG = "wxy_MA";
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
okhttpuse();
}
private String url = "http://www.baidu.com";
private void okhttpuse() {
OkHttpClient okHttpClient = new OkHttpClient.Builder().build();
Request request = new Request.Builder().url(url).build();
Call call = okHttpClient.newCall(request);
call.enqueue(new Callback() {
@Override
public void onFailure(@NonNull Call call, @NonNull IOException e) {
Log.i(TAG, "onFailure");
}
@Override
public void onResponse(@NonNull Call call, @NonNull Response response) throws IOException {
Log.i(TAG, "onResponse");
}
});
}
}
tips:我这里使用的是夜神模拟器,保证模拟器有网络连接。
核心类:OkhttpClient、Request、Call-RealCall.enqueue、Response
?通过上述核心类,我们通过走读okhttp的源码来了解okhttp的执行流程。
2okhttp的执行流程
okhttp的作用,就是将请求封装,去获取响应。首先需要一个OkhttpClient对象作为请求的发送者,然后创建一个Request对象封装好请求,发送者通过newCall这个渠道将封装的请求发送给系统处理,这时候,我们就需要对处理的机制有个大概的了解,比如怎么保证请求的唯一性重复报错、使用什么处理请求,有没有大小限制、以及最后获取响应。
创建OkhttpClient和Request对象的过程在后面阐述,涉及设计模式,先说明发送的流程:
获取call对象
@Override public Call newCall(Request request) {
return RealCall.newRealCall(this, request, false /* for web socket */);
}
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;
}
点击newCall方法,可以看到这里是通过RealCall的newRealCall返回一个call对象。
将请求加入队列
@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));
}
- ?通过RealCall重写的enqueue,对同一发送者重复调用同一请求的行为进行异常捕获。会出现以下报错:
synchronized void enqueue(AsyncCall call) {
//maxRequests 64 / maxRequestsPerHost 5
if (runningAsyncCalls.size() < maxRequests && runningCallsForHost(call) < maxRequestsPerHost) {
runningAsyncCalls.add(call);
executorService().execute(call);
} else {
readyAsyncCalls.add(call);
}
}
- ?实际正常调用的是dispatcher().enqueue()方法,这时候会将请求入队,加入运行时队列(runningAsyncCalls)的条件是 队列中的请求数量小于64并且连接同一台服务器的数量小于5,不然就加入等待队列。
/** Running asynchronous calls. Includes canceled calls that haven't finished yet. */
private final Deque<AsyncCall> runningAsyncCalls = new ArrayDeque<>();
- ?运行时队列是一个双端队列,两边都可以获取请求,而不是传统的先进先出。
运行请求获取响应
@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);
}
}
在RealCall里对execute方法进行了重写,所以执行的是上述代码,获取响应。
总结:okhttp的执行流程只是请求的发送和响应,发送的时候通过RealCall的newRealCall()去返回一个call对象,然后将请求通过RealCall重写的enqueue()方法加入Deque双端队列,加入队列时会判断是否满足入队的条件,队列中请求数小于64和同一时刻连接数小于5,然后通过线程池中线程执行请求,RealCall重写了execute,返回了Response,具体的实现略过。
OkHttp中的构建者模式
在创建OkhttpClient对象和Request对象的时候,都使用了构建者模式来构建对象。构建者模式的衍生过程:
1.最初理解的样子
创建Designer、WPaint、House、Worker
//设计师
public class Designer {
//图纸
private WPaint wPaint;
//工人
private Worker worker;
public Designer(){
wPaint = new WPaint();
worker = new Worker();
}
//图纸的设计
//1.设计高度
public void addHeight(int height){
wPaint.setHeight(height);
}
//2.设计宽度
public void addWidth(int width){
wPaint.getWidth();
}
//3.设计颜色
public void addColor(String color){
wPaint.setColor(color);
}
//交付图纸
//获取设计稿,开始建造,交给woker建造
public House build(){
//工人建造房子
worker.setHouse(wPaint);
return worker.builderHouse();
}
}
//房子,与图纸保持一致
public class House {
private int height;
private int width;
private String color;
public int getHeight() {
return height;
}
public void setHeight(int height) {
this.height = height;
}
public int getWidth() {
return width;
}
public void setWidth(int width) {
this.width = width;
}
public String getColor() {
return color;
}
public void setColor(String color) {
this.color = color;
}
@Override
public String toString() {
return "真实的房子{" +
"height=" + height +
", width=" + width +
", color='" + color + '\'' +
'}';
}
}
public class Worker {
private WPaint wPaint;
//当前wPaint就是所求的House
public void setHouse(WPaint wPaint) {
this.wPaint = wPaint;
}
//根据设计图纸开始建造房子
public House builderHouse(){
House house = new House();
house.setHeight(wPaint.getHeight());
house.setWidth(wPaint.getWidth());
house.setColor(wPaint.getColor());
return house;
}
}
//图纸
public class WPaint {
private int height;
private int width;
private String color;
public int getHeight() {
return height;
}
public void setHeight(int height) {
this.height = height;
}
public int getWidth() {
return width;
}
public void setWidth(int width) {
this.width = width;
}
public String getColor() {
return color;
}
public void setColor(String color) {
this.color = color;
}
@Override
public String toString() {
return "WPaint{" +
"height=" + height +
", width=" + width +
", color='" + color + '\'' +
'}';
}
}
构建者模式是为了方便构建对象,上述代码创建对象的过程如下:
Designer designer = new Designer();
//第一版
designer.addHeight(100);
designer.addWidth(100);
designer.addColor("red");
//第二版
designer.addHeight(100);
designer.addWidth(100);
designer.addColor("green");
//定稿
House house = designer.build();
System.out.println(house.toString());
房子的最终实现与最后一稿确定,上述demo只能说这是一个完备的过程描述,还需要继续优化设计。
2.将设计图纸的过程修改为流式API
//设计师
public class Designer2 {
//图纸
private WPaint wPaint;
//员工
private Worker worker;
public Designer2(){
wPaint = new WPaint();
worker = new Worker();
}
//图纸的设计
//1.设计高度
public Designer2 addHeight(int height){
wPaint.setHeight(height);
return this;
}
//2.设计宽度
public Designer2 addWidth(int width){
wPaint.getWidth();
return this;
}
//3.设计颜色
public Designer2 addColor(String color){
wPaint.setColor(color);
return this;
}
//交付图纸
//获取设计稿,开始建造,交给woker建造
public House build(){
worker.setHouse(wPaint);
return worker.builderHouse();
}
}
将返回属性修改为返回对象。
//根据流式API构建房子
House house = new Designer2().addHeight(100).addWidth(100).addColor("red").build();
?上面还是根据设计师来交付房子,和OkHttpClient的创建方式还是不一样,需要单独创建设计师。
3.从上述可以看出,设计师承担主要角色,负责分工和交付,通过内部类的方式将设计师的角色拿到House中,将图纸属性拿到内部类中,就可以实现优化版的建造者模式demo。
public class House3 {
private int height;
private int width;
private String color;
public House3(Builder builder) {
this.height = builder.height;
this.width = builder.width;
this.color = builder.color;
}
public static final class Builder{
int height;
int width;
String color;
//对图纸的属性进行初始化,在构建对象的时候只需要add修改的属性即可
public Builder(){
this.height = 100;
this.width = 100;
this.color = "red";
}
//反工修改+流式API
public Builder addHeight(int height){
this.height = height;
return this;
}
public Builder addWidth(int width){
this.width = width;
return this;
}
public Builder addColor(String color){
this.color = color;
return this;
}
public House3 build(){
return new House3(this);
}
}
@Override
public String toString() {
return "House3{" +
"height=" + height +
", width=" + width +
", color='" + color + '\'' +
'}';
}
}
创建如下:
House3 house3 = new House3.Builder().addColor("red").build();
//对比OkhttpClient的创建
OkHttpClient okHttpClient = new OkHttpClient.Builder().build();
okhttp为什么使用构建者模式?
使用多个简单的对象构建出一个复杂的对象。
优点:当内部数据过于复杂的时候,可以非常方便的构建出我们想要的对象,并且不是所有的参数我们都需要进行传递; 缺点:代码会有冗余
?OKhttp的责任链模式
okhttp的拦截器使用了责任链模式,选择多个拦截器组成了一个链条,每个拦截器作为一个链条节点,通过逐步拦截,也就是上一个拦截器通过才会执行下一个拦截器,通过链条执行传递责任拦截,直到请求完成。
|