Small knowledge, big challenge! This article is participating in the creation activity of “Essential Tips for Programmers”.

This article has participated in the “Digitalstar Project” and won a creative gift package to challenge the creative incentive money.

Use the sample

This analysis is mainly based on OKHTTP-4.9.0. Here are some examples of OkHttp’s primary use.

        OkHttpClient client = new OkHttpClient.Builder().build();
        Request request = new Request
                .Builder()
                .url("https://www.yanfriends.com")
                .build();
        client.newCall(request).enqueue(new Callback() {
            @Override
            public void onFailure(Call call, IOException e) {}@Override
            public void onResponse(Call call, Response response) throws IOException {
                Log.e("okhttp response", response.body().string() ); }});Copy the code

Architecture diagram

The OkHttp flow chart is shown below. Here is an overview of the flow chart. Details of each link will be covered in the following chapters.

Nature understand

An HTTP connection is essentially a socket, wrapped around the HTTP protocol to send a request and get the result back.

Network connection library appearance as shown in the following code at the beginning, in fact as long as meet the request of the Http protocol, and network can interact, similar to OkHttp library network request, to help developers convenient and blocked similar to request header in the Http protocol, reconnection, merger, agent, return result analysis and so on the application level of the Http protocol details.

        val path = "http://www.baidu.com/"
        val host = "www.baidu.com"
        var socket: Socket? = null
        var streamWriter: OutputStreamWriter? = null
        var bufferedWriter: BufferedWriter? = null
        try {
            socket = Socket(host, 80)
            streamWriter = OutputStreamWriter(socket.getOutputStream())
            bufferedWriter = BufferedWriter(streamWriter)
            bufferedWriter.write("GET HTTP / 1.1 $path \ r \ n")
            bufferedWriter.write("Host: www.baidu.com\r\n")
            bufferedWriter.write("\r\n")
            bufferedWriter.flush()
            val myRequest = BufferedReader(InputStreamReader(socket.getInputStream(), "UTF-8"))
            var d = -1
            while(myRequest.read().also({ d = it }) ! = -1) {
                print(d.toChar())
            }
        } catch (e: IOException) {
            e.printStackTrace()
        }
​
Copy the code

OkHttpClient

OkHttpClient is the configuration center for the entire OkHttp.

All call requests share the configuration in OkHttpClient, such as error retries, connection pooling, logging, various interceptors, and so on.

OkHttpClient implements the Call.Factory interface, which is a Call Factory class that produces a Call and uses the Call to initiate an HTTP Request for a Response.

OkHttpClient should be shared and used with singletons, since each Client has its own connection pool and thread pool, and reusing clients reduces resource waste.

The main configurations in OkHttpClient are:

  • Dispatcher dispatcher: a scheduler used to schedule network requests initiated by the background. It controls the total number of background requests and the total number of requests per host.
  • List<Protocol> protocols: Indicates the supported application layer protocols, such as HTTP/1.1 and HTTP/2.
  • List<ConnectionSpec> connectionSpecs: Indicates the Socket Settings supported by the application layer, whether to use plaintext transmission (for HTTP) or TLS(for HTTPS).
  • List<Interceptor> interceptorsMost interceptors should be configured here.
  • List<Interceptor> networkInterceptorsThe Interceptor that directly interacts with network requests is configured here. For example, if you want to check the returned 301 message or the undecompressed Response Body, you need to check it here.
  • CookieJar cookieJar: Controller that manages cookies. OkHttp provides judgment support for Cookie access (i.e. when cookies need to be saved and when cookies need to be read), but it does not provide specific access implementation. If you need to access cookies, you have to write your own implementation, such as storing them in memory as a Map, or storing them locally or in a database in some other way.
  • Cache cache: Cache storage configuration. The default value is no. You need to configure the Cache file location and storage space upper limit.
  • HostnameVerifier hostnameVerifier: used to verify that the owner of the certificate downloaded during the HTTPS handshake is the same as the host name to be accessed.
  • CertificatePinner certificatePinner: The Certificate Public Key Pinner is used to set the Certificate Public Key Pinner for a Host during the HTTPS handshake. As an additional validation in addition to the normal certificate validation mechanism.
  • Authenticator authenticator: Used for automatic re-authentication. After the configuration, the authenticator is directly called in response to the request receiving the 401 status code. After the Authorization header is manually added, the request is automatically re-initiated.
  • boolean followRedirects: Indicates whether redirection is allowed
  • boolean followSslRedirects: Indicates whether to automatically follow the HTTPS URL. If protocol switching occurs during redirection, the default value is true.
  • Redirection means whether to automatically follow redirections that switch between HTTP and HTTPS.
  • boolean retryOnConnectionFailure: Whether to retry automatically when a request fails. Note that most failed requests do not fall into the “need to retry” category defined by OkHttp, which only applies to “multiple IP switch retries for the same domain name”, “Socket failure retries”, etc.
  • int connectTimeout: Timeout period for establishing a connection (TCP or TLS). The default value is 10 seconds.int readTimeout: Timeout period between sending a request and reading the response data. The default value is 10 seconds.int writeTimeout: Timeout for initiating a request and being accepted by the target server. (because sometimes the other server may not read your Request for some reason), the default is 10 seconds;

Coarse knowledge okhttp

There are a lot of details in OkHttp. I don’t want to go into too much detail here, but just have a general understanding of the structure. Some details will be analyzed in the following articles.

RealCall initialization

Use okHttpClient.newCall () to create the RealCall and Transmitter class that connects okHTTP to the network layer.

 static RealCall newRealCall(OkHttpClient client, Request originalRequest, boolean forWebSocket) {
    RealCall call = new RealCall(client, originalRequest, forWebSocket);
    call.transmitter = new Transmitter(client, call);
    return call;
  }
Copy the code

RealCall.execute()/RealCall.enqueue()

  • Call dispatcher.executed ()/enqueue() to add a call to the executed call queue to control the start, end, and cancellation of the call.

  • Call getResponseWithInterceptorChain () sends a request to the server and get the response

    @Override public void enqueue(Callback responseCallback) {
        ...
        transmitter.callStart();
        client.dispatcher().enqueue(new AsyncCall(responseCallback));
      }
    Copy the code

RealCall.getResponseWithInterceptorChain()

OkHttp encapsulates various request information in the Interceptor of different layers. According to the parameter Settings of the open method, the information specified by HTTP protocol is added layer by layer during the request, and the request is finally sent. GetResponseWithInterceptorChain () method of the main logic is as follows:

  1. Add a custom interceptor

  2. In turn, add custom interceptor, RetryAndFollowUpInterceptor, BridgeInterceptor, CacheInterceptor networkIntercep, ConnectInterceptor, custom Tor (e.g. Stetho of Facebook),

  3. Create RealInterceptorChain object, called after RealCall. RealInterceptorChain. Proceed () method, according to the chain of responsibility sequence in turn calls each blocker processing logic

Response getResponseWithInterceptorChain(a) throws IOException {
    List<Interceptor> interceptors = new ArrayList<>();
    interceptors.addAll(client.interceptors());
    interceptors.add(new RetryAndFollowUpInterceptor(client));
    interceptors.add(new BridgeInterceptor(client.cookieJar()));
    interceptors.add(new CacheInterceptor(client.internalCache()));
    interceptors.add(new ConnectInterceptor(client));
    if(! forWebSocket) { interceptors.addAll(client.networkInterceptors()); } interceptors.add(new CallServerInterceptor(forWebSocket));
    Interceptor.Chain chain = new RealInterceptorChain();

    try{ Response response = chain.proceed(originalRequest); .return response;
    } catch(IOException e) { ... }}Copy the code

RealInterceptorChain.proceed()

  1. Check whether the custom interceptor is valid

  2. Create a new RealInterceptorChain, note index+1

  3. Get the current index interceptor and call interceptor.Intercept () to the interceptor to get the return value

  4. Require all custom interceptor to invoke RealInterceptorChain. Proceed (), otherwise it will cause no request

  5. Check whether Response or response.body() is empty

public Response proceed(Request request, Transmitter transmitter, @Nullable Exchange exchange)
      throws IOException {...// Call the next interceptor in the chain.
    RealInterceptorChain next = new RealInterceptorChain(interceptors, transmitter, exchange,
        index + 1, request, call, connectTimeout, readTimeout, writeTimeout);
    Interceptor interceptor = interceptors.get(index);
    Response response = interceptor.intercept(next);

    // Confirm that the next interceptor made its required call to chain.proceed().
    if(exchange ! =null && index + 1< interceptors.size() && next.calls ! =1) {
      throw new IllegalStateException("network interceptor " + interceptor
          + " must call proceed() exactly once");
    }

    // Confirm that the intercepted response isn't null.
    if (response == null) {
      throw new NullPointerException("interceptor " + interceptor + " returned null");
    }

    if (response.body() == null) {
      throw new IllegalStateException(
          "interceptor " + interceptor + " returned a response with no body");
    }

    return response;
  }
Copy the code

reference

square.github.io/okhttp/

zhuanlan.zhihu.com/p/58093669

www.jianshu.com/p/8d69fd920…

Juejin. Cn/post / 684490…