网络请求框架OkHttp

1okhttp的核心类

首先写一个简单的demo,用于分析:

1.添加网络权限(androidmanifest.xml)

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 runningAsyncCalls = new ArrayDeque<>();
  •  运行时队列是一个双端队列,两边都可以获取请求,而不是传统的先进先出。

运行请求获取响应

网络请求框架OkHttp_第1张图片

@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.最初理解的样子

网络请求框架OkHttp_第2张图片

创建Designer、WPaint、House、Worker

网络请求框架OkHttp_第3张图片

  • 设计图纸+工人建造房子+交付房子
//设计师
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的拦截器使用了责任链模式,选择多个拦截器组成了一个链条,每个拦截器作为一个链条节点,通过逐步拦截,也就是上一个拦截器通过才会执行下一个拦截器,通过链条执行传递责任拦截,直到请求完成。

你可能感兴趣的:(okhttp,android)