编程语言
首页 > 编程语言> > Andorid 开发之OKHttp源码分析

Andorid 开发之OKHttp源码分析

作者:互联网

问题概述:
1、 OSI模型
2、 Http协议概述
3、 为什么OKHttp使用Socket而不是HttpUrlConnection
4、 OKHttp的核心类有哪些?
5、 OKHttp流程
6、 构建者/责任链模式在OKHttp中的使用
7、 OKHttp是如何通过缓存相应数据来减少重复的网络请求
8、 OKHttp对于网络请求都有哪些优化

1、OSI参考模型
image.png

2、 Http协议

1.0 版本,短链接,客户端和服务器每次交互要经过 三次握手,四次挥手,传10次数据,要经过70次
1.1 版本 , 长链接 ,客户端和服务器第一次交互后,经过三次握手,中间可以无限交互,结束时,进行四次挥手
具体概念请看:
https://www.cnblogs.com/an-wen/p/11180076.html

3、为什么OKHttp使用Socket而不是HttpUrlConnection

因为这个Socket是传输层,socket会把请求头和回发可以直接做处理

4、关键类

OkHttpClient Request Call->RealCall
RealCall就是Call的具体实现
RealCall里面的 call.execute方法如下

// 防止 同一个url请求调用两次
  @Override public Response execute() throws IOException {
    synchronized (this) {
      if (executed) throw new IllegalStateException("Already Executed");
      executed = true;
    }

build的时候有一个参数叫 dispatcher
它在被call.execute调用下进入以下方法
runningAsyncCalls是个双端队列
runningCallsForHost 当请求全部访问同一个host的时候
executorService是一个线程池

  synchronized void enqueue(AsyncCall call) {
// 当运行的队列中的数值小于64,并且同时访问同一个机器目标Host的请求小于5
// 满足就直接加入运行队列,不然的话就加入等待队列
    if (runningAsyncCalls.size() < maxRequests && runningCallsForHost(call) < maxRequestsPerHost) {
      runningAsyncCalls.add(call);
      executorService().execute(call);
    } else {
      readyAsyncCalls.add(call);
    }
  }

这里附上线程池代码

  public synchronized ExecutorService executorService() {
    if (executorService == null) {
      executorService = new ThreadPoolExecutor(0, Integer.MAX_VALUE, 60, TimeUnit.SECONDS,
          new SynchronousQueue<Runnable>(), Util.threadFactory("OkHttp Dispatcher", false));
    }
    return executorService;
  }

Dispatcher在执行enqueue的方法时,传的AsyncCall 是继承了NamedRunnable,NamedRunnable就是个线程,最终NamedRunnable会执行Run里面的execute() 方法,而这个execute()抽象方法是在RealCall 的execute()方法里实现的,这个Response,就是获取相应的数据

Response response = getResponseWithInterceptorChain();

5、 OKHttp流程
image.png

1、 OkHttpClient okHttpClient = new OkHttpClient .Builder(),构建一个okHttpClient 对象,传入你想传入的对象,不传入就是默认的
2、 构建request
Request request = new Request.Builder();
3、okHttpClient.newCall 实际上返回的realCall类 继续调用 RealCall.newRealCall
4、 调用enqueue方法,传入我们需要的回调接口。而且会判断,

    synchronized (this) {
      if (executed) throw new IllegalStateException("Already Executed");
      executed = true;
    }

如果当前这个call对象已经被运行的话,则抛出异常
5、 继续调用dispatcher的enqueue方法, 如果当前队列< 64并且正在运行,访问同一个服务器地址的请求<5,就直接添加到运行队列中,并开始运行,否则就添加到等待队列中
6、 运行AsynCall,调用它的execute方法
7、 在execute方法中处理完response之后,会在finally中调用dispatcher的finished方法
8、 将当前已经处理完毕的call从运行队列中移除掉, 并且调用promoteCalls方法
9、 promoteCalls方法中进行判断,如果运行队列数目大于等于64,如果等待队列中啥也没有,也直接return。
循环等待队列,将等待队列中的数据进行移除,移除是根据运行队列中还能存放多少来决定,移到了运行队列中,并开始运行
然后到第五条重新开始循环

6、构建者/责任链模式在OKHttp中的使用

首先说一下OkHttp为什么使用构建者模式:
它是使用多个简单的对象一步一步构建成一个复杂的对象, Okhttp内部数据过于复杂,构建者模式可以非常方便的构建出我们想要的对象,并且不是所有的参数我们都需要进行传递
缺点:代码会有冗余
我们举个简单的例子:比如要构建一个房子

public class House3 {

    private double height;
    private double width;
    private String color;

    // 构造函数,使我这个构造出来的房子是我最后想要的房子的数据
    public House3(Builder builder) {
        this.color = builder.color;
        this.height = builder.height;
        this.width = builder.width;

    }

//  内部类,负责建造房子
    public static final class Builder{
        double height;
        double width;
        String color;

      // 初始化数据,房子默认是这样的
        public Builder(){
            this.color = "red";
            this.width = 100;
            this.height = 100 ;
        }

        // 添加一个 高度
        public Builder addHeight(double height){
            this.height = height;
            return this;
        }

        // 添加房子的宽度
        public Builder addWidth(double 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 + '\'' +
                '}';
    }
}

Activity 代码

House3 house = new House3.Builder()
.addColor("bule")
.addHeight(150)
.addWidth(150)
.build();

责任链模式

概念:

顾名思义,责任链模式(Chain of Responsibility Pattern)为请求创建了一个接收者对象的链。这种模式给予请求的类型,对请求的发送者和接收者进行解耦。这种类型的设计模式属于行为型模式。

在这种模式中,通常每个接收者都包含对另一个接收者的引用。如果一个对象不能处理该请求,那么它会把相同的请求传给下一个接收者,依此类推。

我们放上realCall里用到责任链模式的代码

  private Response getResponseWithInterceptorChain() throws IOException {
    // Build a full stack of interceptors.
    List<Interceptor> interceptors = new ArrayList<>();
    interceptors.addAll(client.interceptors());
    interceptors.add(retryAndFollowUpInterceptor);
    interceptors.add(new BridgeInterceptor(client.cookieJar()));
    interceptors.add(new CacheInterceptor(client.internalCache()));
    interceptors.add(new ConnectInterceptor(client));
    if (!retryAndFollowUpInterceptor.isForWebSocket()) {
      interceptors.addAll(client.networkInterceptors());
    }
    interceptors.add(new CallServerInterceptor(
        retryAndFollowUpInterceptor.isForWebSocket()));

    Interceptor.Chain chain = new RealInterceptorChain(
        interceptors, null, null, null, 0, originalRequest);
    return chain.proceed(originalRequest);
  }

retryAndFollowUpInterceptor这个拦截器主要是用来失败重试、请求重定向或者是转发用的
BridgeInterceptor 桥拦截器主要是用来压缩或者是解压用的, 主要是封装header属性 host、keep-live、gzip
CacheInterceptor缓存拦截器,顾名思义,做缓存处理,这里有一个缓存策略,这也是一个设计模式
ConnectInterceptor 连接拦截器用来建立Http链接的
CallServerInterceptor 主要功能就是—向服务器发送请求,并最终返回Response对象供客户端使用。
7、 OKHttp是如何通过缓存相应数据来减少重复的网络请求

是因为里面加了一个缓存的拦截器,这个缓存的拦截器能根据时间戳去判断我当前这个缓存是不是在一个合理的时间点,在一定时间内去取本地,超过这个时间就去取服务器,这样就通过缓存相应数据来减少重复的网络请求

8、 OKHttp对于网络请求都有哪些优化

主要有以下两点
1、 缓存拦截器
2、 桥拦截器里面有个压缩数据的方法

补充:
OkHttp里的线程池

    if (executorService == null) {
      executorService = new ThreadPoolExecutor(0, Integer.MAX_VALUE, 60, TimeUnit.SECONDS,
          new SynchronousQueue<Runnable>(), Util.threadFactory("OkHttp Dispatcher", false));
    }

SynchronousQueue 这个参数是线程池的等待队列,SynchronousQueue这个队列里只能最多存在一个等待任务,要想插入第二个任务,必须等第一个任务移除,才能插入第二个任务。
1:核心线程数,保持在线程池中的线程数量,空闲的情况下就是0
2:线程池最大可容纳的线程数
3/4参数: 时间单位。当线程池中的线程数量大于核心线程数,空闲线程就会等待60s才会终止, 如果小于就会立刻停止

标签:队列,interceptors,OKHttp,源码,线程,Andorid,new,public
来源: https://blog.csdn.net/weixin_44005563/article/details/116614241