Android学习笔记 --- okHttp框架

简单使用

  1. 接入okHttp

直接在gradle中引入okHttp的依赖包

implementation 'com.squareup.okhttp3:okhttp:4.8.1'
  1. 调用

okHttp的调用比较简单,大致分为以下几个步骤:

  • 构建网络请求控制对象OkHttpClient

  • 构建请求对象request

  • 创建Call对象

  • 创建接收返回数据的对象response

  • 发送网络请求

okHttp的请求方式有两种,同步和异步请求。

两者在使用方式上最大的区别就是在最后一步调用方式上:

同步请求调用的是execute(),异步请求调用的是enqueue()。

  1. 同步请求示例代码

        //1.构建OkHttpClient对象
        OkHttpClient okHttpClient=new OkHttpClient(); 
        //2.构建一个请求对象request,比如请求的url,请求方式 
        Request.Builder builder=new Request.Builder();
        Request request=builder.url("http://baidu.com")
                .get()
                .build();
        //3.创建一个Call对象
        final Call call= okHttpClient.newCall(request);
        new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                //4.发送请求,并接收回复的返回数据
                    Response response=call.execute();    
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }).start();
  1. 异步请求的实例代码

call.enqueue(new Callback() {
            @Override
            public void onFailure(Call call, IOException e) {
            }
 
            @Override
            public void onResponse(Call call, Response response) throws IOException {
            }
        });

工作流程

  1. 整体流程

  1. 使用okHttpClient创建一个call,并发起同步或者异步请求后,okHttp会通过Dispatcher对我们所有的RealCall(Call的具体实现类)进行统一管理,并通过execute()与enqueue()方法对同步或者异步请求进行处理。

  1. execute()与enqueue()这两个方法会最终调用RealCall中的getResponseWithInterceptorChai()方法,从阻拦器链中获取返回结果。

  1. 阻拦器链中,依次通过RetryAndFollowUpInterceptor(重定向拦截器)、BridgeInterceptor(桥接拦截器)、CacheInterceptor(缓存拦截器)、ConnectInterceptor(连接拦截器)、CallServerInterceptor(网络拦截器)对请求依次解决,与服务器建立连接后,获取返回数据,再经过上述拦截器依次解析后最后将结果返回给调用方。

整体流程图

整体流程图中涉及到几个关键核心类:

  1. OkHttpClient:整个OkHttp的核心管理类,所有的内部逻辑和对象归OkHttpClient统一管理。

  1. RealCall:负责请求的调度(同步请求在当前线程发送,异步请求则使用OkHttp内部的线程池进行);负责构造内部逻辑责任链,并执行责任链相关的逻辑,直到获取服务器响应的结果。

由上图我们可以看出,使用execute()和enqueue()方法后,都会执行getResponseWithInterceptorChain()这个方法获取请求结果。那么我们接下来就解析下这个方法。

val interceptors = mutableListOf<Interceptor>()
    // 用户自定义拦截器
    interceptors += client.interceptors
    // 重定向拦截器
    interceptors += RetryAndFollowUpInterceptor(client)
    // 桥接拦截器
    interceptors += BridgeInterceptor(client.cookieJar)
    // 缓存拦截器
    interceptors += CacheInterceptor(client.cache)
    // 连接拦截器    
    interceptors += ConnectInterceptor
    if (!forWebSocket) {
    // 网络拦截器
      interceptors += client.networkInterceptors
    }
    // 调用服务拦截器
    interceptors += CallServerInterceptor(forWebSocket)

    // 创建一个拦截链条,将当前的拦截器集合传递进去
    val chain = RealInterceptorChain(interceptors, transmitter, null, 0, originalRequest, this,
        client.connectTimeoutMillis, client.readTimeoutMillis, client.writeTimeoutMillis)

    var calledNoMoreExchanges = false
    try {
    // 调用链条的第一个拦截器的处理方法,并返回处理结果
      val response = chain.proceed(originalRequest)

从上面的源码我们可以看出,getResponseWithInterceptorChain()生成一个list集合存放多个拦截器,然后生成一个RealInterceptorChain对象,并调用RealInterceptorChain.proceed()获取到服务器的响应结果。

RealInterceptorChain源码解析图

  1. okHttp的核心 --- 拦截器

由上面我们知道okHttp的核心就是这些拦截器,那么我们接下来就分析这个拦截器。

根据RealInterceptorChain源码得到的流程图

结合上面的示意图,可以得到以下结论

  1. 拦截器按照添加顺序执行

  1. 拦截器的执行从RealInterceptorChain.proceed()开始,进入到第一个拦截器的执行逻辑

  1. 每个拦截器在执行之前,会将剩余尚未执行的拦截器组成新的RealInterceptorChain

  1. 拦截器的逻辑被新的责任链调用next.proceed()切分为start、next.proceed、end这三个部分依次执行

  1. next.proceed() 所代表的其实就是剩余所有拦截器的执行逻辑

  1. 所有拦截器最终形成一个层层内嵌的嵌套结构

接下来我们再了解下每一个拦截器的作用

  • retryAndFollowUpInterceptor(重定向拦截器):重试那些失败或者重定向的请求,因为在整个网络请求的过程中可能出现网络不稳定的情况。

  • BridgeInterceptor(桥接拦截器):请求之前对响应头做了一些检查,并添加一些头。负责将用户构建好的Request请求转换成可以进行网络访问的请求。

  • CacheInterceptor(缓存拦截器):做一些缓存工作,获取相应时先从缓存里去获取,如果缓存没有才会进行网络请求,并把获取到响应放入缓存中。

  • ConnectInterceptor(连接拦截器):负责建立和目标服务器的连接。

  • CallServerInterceptor(发送服务请求拦截器):负责向服务器发起真正的网络请求。

下面我们就对拦截器进行详细的分析:

  1. RetryAndFollowUpInterceptor

RetryAndFollowUpInterceptor源码逻辑图

从上图中可以看出,RetryAndFollowUpInterceptor开启了一个while(true)的循环,并在循环内部完成两个重要的判定,如图中的蓝色方框:

  • 当请求内部抛出异常时,判定是否需要重试

  • 当响应结果是3xx重定向时,构建新的请求并发送请求

重试的逻辑相对复杂,有如下的判定逻辑(具体代码在RetryAndFollowUpInterceptor类的recover方法):

  • 规则1: client的retryOnConnectionFailure参数设置为false,不进行重试

  • 规则2: 请求的body已经发出,不进行重试

  • 规则3: 特殊的异常类型不进行重试(如ProtocolException,SSLHandshakeException等)

  • 规则4: 没有更多的route(包含proxy和inetaddress),不进行重试

  1. BridgeInterceptor

public final class BridgeInterceptor implements Interceptor {
    
    public Response intercept(Chain chain) throws IOException {
    
        Request userRequest = chain.request();
        Request.Builder requestBuilder = userRequest.newBuilder();  
        RequestBody body = userRequest.body();
        
        //进行请求头的包装
        if (body != null) {
            MediaType contentType = body.contentType();
          if (contentType != null) {
            requestBuilder.header("Content-Type", contentType.toString());
          }
        if (contentLength != -1) {
            requestBuilder.header("Content-Length", Long.toString(contentLength));
            requestBuilder.removeHeader("Transfer-Encoding");
        }
         //....
        }   
 
     responseBuilder.body(new RealResponseBody(contentType, -1L, Okio.buffer(responseBody)));
    }
     return responseBuilder.build();
}

流程:

  1. 对请求头进行封装,将Request请求转化为可以进行网络访问的请求

  1. 进行网络请求

  1. 将请求得到的响应结果Response转化为用户可见的Response

BridageInterceptor 拦截器的功能如下:

  1. 负责把用户构造的请求转换为发送到服务器的请求 、把服务器返回的响应转换为用户友好的响应,是从应用程序代码到网络代码的桥梁

  1. 设置内容长度,内容编码

  1. 设置gzip压缩,并在接收到内容后进行解压。省去了应用层处理数据解压的麻烦

  1. 添加cookie

  1. 设置其他报头,如User-Agent,Host,Keep-alive等。其中Keep-Alive是实现连接复用的必要步骤

  1. CacheInterceptor

public final class CacheInterceptor implements Interceptor {public Response intercept(Chain chain) throws IOException {
    Response cacheCandidate = cache != null? cache.get(chain.request()): null;
    CacheStrategy strategy = new CacheStrategy.Factory(now, chain.request(), cacheCandidate).get();
    Request networkRequest = strategy.networkRequest;
    Response cacheResponse = strategy.cacheResponse;
    //调用下一个拦截器
      try {
      networkResponse = chain.proceed(networkRequest);
    } finally {
    //...
    }
}    
...
if (cacheResponse != null) {
      if (networkResponse.code() == HTTP_NOT_MODIFIED) {
        Response response = cacheResponse.newBuilder()
            .headers(combine(cacheResponse.headers(), networkResponse.headers()))
            .sentRequestAtMillis(networkResponse.sentRequestAtMillis())
            .receivedResponseAtMillis(networkResponse.receivedResponseAtMillis())
            .cacheResponse(stripBody(cacheResponse))
            .networkResponse(stripBody(networkResponse))
            .build();
        networkResponse.body().close();
        cache.trackConditionalCacheHit();
        cache.update(cacheResponse, response);
        return response;
      } else {
        closeQuietly(cacheResponse.body());
      }
    }
...
  //如果缓存没有响应且网络请求结果为空
     if (networkRequest == null && cacheResponse == null) {
      return new Response.Builder()
          .request(chain.request())
          .protocol(Protocol.HTTP_1_1)
          .code(504)
          .message("Unsatisfiable Request (only-if-cached)")
          .body(Util.EMPTY_RESPONSE)
          .sentRequestAtMillis(-1L)
          .receivedResponseAtMillis(System.currentTimeMillis())
          .build();
    }

流程:

  1. 获取本地缓存cacheCandidate,如果本地缓存可用则打断interceptor链,返回cacheCandidate。

  1. 调用下一个interceptor获取networkResponse

  1. 用networkResponse、cacheResponse构造新的response

  1. 根据新的response里的header通过缓存策略存入缓存中

CacheInterceptor 拦截器的逻辑流程如下:

  1. 通过Request尝试到Cache中拿缓存,当然前提是OkHttpClient中配置了缓存,默认是不支持的。

  1. 根据response,time,request创建一个缓存策略,用于判断怎样使用缓存。

  1. 如果缓存策略中设置禁止使用网络,并且缓存又为空,则构建一个Response直接返回,注意返回码=504

  1. 缓存策略中设置不使用网络,但是又缓存,直接返回缓存

  1. 接着走后续过滤器的流程,chain.proceed(networkRequest)

  1. 当缓存存在的时候,如果网络返回的Resposne为304,则使用缓存的Resposne。

  1. 构建网络请求的Resposne

  1. 当在OkHttpClient中配置了缓存,则将这个Resposne缓存起来。

  1. 缓存起来的步骤也是先缓存header,再缓存body。

  1. 返回Resposne

  1. ConnectInterceptor

okhttp的一大特点就是通过连接池来减小响应延迟。如果连接池中没有可用的连接,则会与服务器建立连接,并将socket的io封装到HttpStream(发送请求和接收response)中,这些都在ConnectInterceptor中完成。

public final class ConnectInterceptor implements Interceptor {
    public Response intercept(Chain chain) throws IOException {
        RealInterceptorChain realChain = (RealInterceptorChain) chain;
        Request request = realChain.request();
        //获取之前的拦截器传过来的 StreamAllocation
        StreamAllocation streamAllocation = realChain.streamAllocation();
    
        // We need the network to satisfy this request. Possibly for validating a conditional GET.
        boolean doExtensiveHealthChecks = !request.method().equals("GET");
        HttpCodec httpCodec = streamAllocation.newStream(client, chain, doExtensiveHealthChecks);
         //通过streamAllocation获取RealConnection对象
        RealConnection connection = streamAllocation.connection();
    
        return realChain.proceed(request, streamAllocation, httpCodec, connection);
      }
}

流程:

  1. ConnectInterceptor获取之前的拦截器传过来的 StreamAllocation

  1. 通过streamAllocation获取RealConnection对象,它是真正用于连接网络的对象

  1. 将RealConnection对象,以及对于与服务器交互最为关键的HttpCodec等对象传递给后面的拦截

  1. CallServerInterceptor

CallServerInterceptor是最后一个拦截器,它负责发起真正的网络请求并接收服务器返回的响应数据

public final class CallServerInterceptor implements Interceptor {
    public Response intercept(Chain chain) throws IOException {
        RealInterceptorChain realChain = (RealInterceptorChain) chain;
        HttpCodec httpCodec = realChain.httpStream();
        StreamAllocation streamAllocation = realChain.streamAllocation();
        RealConnection connection = (RealConnection) realChain.connection();
        Request request = realChain.request();
        
        realChain.eventListener().requestHeadersStart(realChain.call());
        //写入请求头信息
        httpCodec.writeRequestHeaders(request);
        realChain.eventListener().requestHeadersEnd(realChain.call(), request);
        //写入请求体的信息
        request.body().writeTo(bufferedRequestBody);
        //完成网络请求工作
        httpCodec.finishRequest();
        if(responseBuilder == null) {
              realChain.eventListener().responseHeadersStart(realChain.call());
              responseBuilder = httpCodec.readResponseHeaders(false);
        }     
    
       //读取网络响应的头信息
       if (responseBuilder == null) {
          realChain.eventListener().responseHeadersStart(realChain.call());
          responseBuilder = httpCodec.readResponseHeaders(false);
       }
       //读取网络响应的body信息
       if (forWebSocket && code == 101) {
            // Connection is upgrading, but we need to ensure interceptors see a non-null response body.
          response = response.newBuilder()
              .body(Util.EMPTY_RESPONSE)
              .build();
        } else {
          response = response.newBuilder()
              .body(httpCodec.openResponseBody(response))
              .build();
        }
    }
   return response;
}

流程:

  1. 写入请求头和请求体信息,发起正在的网络请求

  1. 获取网络请求返回的响应,读取响应头和响应体的信息

  1. 返回最终的响应结果

猜你喜欢

转载自blog.csdn.net/HaveFun_Wine/article/details/129727794