Android study notes --- okHttp framework

easy to use

  1. Access okHttp

Introduce the dependency package of okHttp directly in gradle

implementation 'com.squareup.okhttp3:okhttp:4.8.1'
  1. transfer

The call of okHttp is relatively simple, roughly divided into the following steps:

  • Build network request control object OkHttpClient

  • Build request object request

  • Create a Call object

  • Create an object response that receives the returned data

  • send network request

There are two request methods of okHttp, synchronous and asynchronous requests.

The biggest difference between the two in terms of usage is in the calling method of the last step:

Synchronous requests call execute(), and asynchronous requests call enqueue().

  1. Synchronous request sample code

        //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. Example code for asynchronous request

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

work process

  1. overall process

  1. After using okHttpClient to create a call and initiate a synchronous or asynchronous request, okHttp will uniformly manage all our RealCalls (the specific implementation class of Call) through Dispatcher, and perform synchronous or asynchronous requests through the execute() and enqueue() methods deal with.

  1. The execute() and enqueue() methods will finally call the getResponseWithInterceptorChai() method in RealCall to obtain the return result from the blocker chain.

  1. In the blocker chain, through RetryAndFollowUpInterceptor (redirection interceptor), BridgeInterceptor (bridge interceptor), CacheInterceptor (cache interceptor), ConnectInterceptor (connection interceptor), CallServerInterceptor (network interceptor) to solve the request in turn, and establish with the server After connecting, get the returned data, and then analyze it in turn by the above interceptor, and finally return the result to the caller.

Overall flow chart

Several key core classes are involved in the overall flowchart:

  1. OkHttpClient: The core management class of the entire OkHttp, all internal logic and objects are managed by OkHttpClient.

  1. RealCall: Responsible for request scheduling (synchronous requests are sent in the current thread, asynchronous requests are performed using the thread pool inside OkHttp); responsible for constructing the internal logical chain of responsibility, and executing the logic related to the chain of responsibility until the result of the server response is obtained.

From the above figure, we can see that after using the execute() and enqueue() methods, the getResponseWithInterceptorChain() method will be executed to obtain the request result. Then we will analyze this method next.

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)

From the source code above, we can see that getResponseWithInterceptorChain() generates a list collection to store multiple interceptors, then generates a RealInterceptorChain object, and calls RealInterceptorChain.proceed() to obtain the response result from the server.

RealInterceptorChain source code analysis diagram

  1. The core of okHttp --- interceptor

From the above, we know that the core of okHttp is these interceptors, so we will analyze this interceptor next.

The flow chart obtained according to the source code of RealInterceptorChain

Combined with the above diagram, the following conclusions can be drawn

  1. Interceptors are executed in the order they were added

  1. The execution of the interceptor starts from RealInterceptorChain.proceed() and enters the execution logic of the first interceptor

  1. Before each interceptor is executed, the remaining unexecuted interceptors will form a new RealInterceptorChain

  1. The logic of the interceptor is divided into three parts: start, next.proceed, and end, which are executed sequentially by calling next.proceed() by the new chain of responsibility

  1. What next.proceed() represents is actually the execution logic of all remaining interceptors

  1. All interceptors eventually form a nested structure embedded in layers

Next, let's understand the role of each interceptor

  • retryAndFollowUpInterceptor (redirection interceptor): Retry those failed or redirected requests, because network instability may occur during the entire network request process.

  • BridgeInterceptor (bridge interceptor): some checks are made on the response header before the request, and some headers are added. Responsible for converting the Request constructed by the user into a request that can be accessed through the network.

  • CacheInterceptor (cache interceptor): Do some caching work. When obtaining the response, first obtain it from the cache. If the cache does not exist, it will make a network request and put the obtained response into the cache.

  • 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. 返回最终的响应结果

Guess you like

Origin blog.csdn.net/HaveFun_Wine/article/details/129727794