Explain OkHttp in simple terms, [take your handwriting] build an efficient and high-performance network request framework

brief description

OKHttp is a network framework written in Java, which can be used in Android and some Java-based web application development. It uses HTTP/2 standard support and connection pool technology, which allows applications to quickly send network requests to Web servers and get responses. OKHttp provides a simple API that allows developers to send synchronous or asynchronous HTTP requests and process responses from web servers. It also supports interceptors, caching techniques, and the HTTPS transport protocol. In addition, OKHttp also provides a very flexible retry mechanism, which allows the application to automatically retry the request after an error occurs in the network request, so as to improve the stability and reliability of the application. Overall, OKHttp is a high-performance, easy-to-use, flexible, and lightweight network framework that is widely used in Android development and Java Web development.

Principle of OKHttp

The bottom layer of OKHttp is implemented based on Java's network protocol stack, which uses Java's standard library and some third-party libraries to send network requests. It takes advantage of Java's asynchronous IO technology, enabling applications to process multiple requests in one thread without blocking the main thread. The following are some important principles of OKHttp:

  • Connection pool technology: OKHttp uses connection pool technology to realize the multiplexing of HTTP requests, which can reduce the number of network interactions between applications and servers, thereby improving the efficiency of network requests. The connection pool will retain the established connections and reuse them in the next request, thus reducing the time and resource consumption of connection establishment.
  • Request interceptor and response interceptor: OKHttp provides an interceptor mechanism, which allows developers to modify and process the original request and response without changing them. The interceptor can add header information, encrypt, cache, request retry, etc. to the request. The response interceptor can decrypt the result returned by the server, add a cache, convert it into a Java object, and so on.
  • Asynchronous request and synchronous request: OKHttp supports asynchronous HTTP request and synchronous HTTP request, and developers can choose the appropriate method to send network requests according to their needs. Asynchronous requests keep the user interface responsive, while synchronous requests block the main thread, but get the results as soon as the request completes.
  • HTTPS transmission protocol: OKHttp supports the HTTPS transmission protocol, which allows the application to use a secure encryption method to transmit data during the network request process, ensuring the security of user data.
  • Caching technology: OKHttp provides caching support, and caches requests and responses in local storage, which can speed up the response speed of applications and reduce network traffic consumption.
  • Retry mechanism: OKHttp provides an adaptive request retry mechanism. When the network request fails, it will automatically retry. If the retry fails, the error message will be sent back to the caller for processing. This mechanism can improve the stability and reliability of the application, and avoid application crashes caused by network problems.

Easy to use OKHttp

An API sends a GET request and returns the data returned by the API.

First, you need to add the OKHttp dependency to your project. If you use the Gradle build tool, you can add the following dependencies to your project's build.gradle file:

dependencies {
    implementation 'com.squareup.okhttp3:okhttp:4.9.1'
}

Then, you can create an instance of OkHttpClient in your code, and use this instance to send requests:

// 创建一个OkHttpClient实例
OkHttpClient okHttpClient = new OkHttpClient();
​
// 创建一个HTTP请求
Request request = new Request.Builder()
    .url("https://jsonplaceholder.typicode.com/posts")
    .build();
​
// 发送HTTP请求
try (Response response = okHttpClient.newCall(request).execute()) {
    // 处理HTTP响应
    if (response.isSuccessful()) {
        String responseBody = response.body().string();
        System.out.println(responseBody);
    } else {
        System.out.println("Error: " + response.code() + " " + response.message());
    }
} catch (IOException e) {
    System.out.println("Error: " + e.getMessage());
}

In this code, we first create an instance of OkHttpClient. Then, we create an HTTP request and set the requested URL. We use the created OkHttpClient instance to send this request, and use the execute() method to send the request and get the response synchronously. After getting the response, we check whether the response is successful, and if successful, read the data in the response body and print it out. If the response fails, we print out the response code and error message. If an exception occurs while sending the request, we will also print out the exception message.

This is a simple case of using OKHttp for network requests. Of course, in actual use, you may need to use some advanced functions of OKHttp according to different situations, such as asynchronous requests, interceptors, caching, HTTPS, etc.

Handwritten OkHttp high concurrent network access framework

Handwriting a high-concurrency OkHttp network request framework is a relatively complex task, involving many knowledge points, and requires knowledge in network programming, multi-thread programming, HTTP protocol, Socket programming and other aspects. The following is a simple implementation process, for reference only.

First, we need to implement a request Handler to handle all requests. In this Handler, we need to maintain a request queue to save unprocessed requests. When a request is added to the queue, the Handler will immediately start a thread to process the request, and then return immediately to continue processing the next request. When there is no request in the queue, the Handler enters the waiting state.

public class RequestHandler {
    private final Deque<Request> requestQueue;
    private final Executor executor;
​
    public RequestHandler() {
        requestQueue = new ArrayDeque<>();
        executor = Executors.newFixedThreadPool(10);
    }
​
    public void enqueue(Request request) {
        requestQueue.add(request);
        executor.execute(new RequestTask());
    }
​
    private class RequestTask implements Runnable {
        @Override
        public void run() {
            while (!requestQueue.isEmpty()) {
                Request request = requestQueue.remove();
                // 处理请求
            }
​
            synchronized (requestQueue) {
                try {
                    requestQueue.wait();
                } catch (InterruptedException e) {
                    // ignore
                }
            }
        }
    }
}

In the above code, we use Deque to hold the request queue. Deque is a double-ended queue that can be operated on both the head and tail of the queue. We create a thread pool of size 10 in the constructor, which is used to start a thread in the thread pool when processing a request. When a request is added to the request queue, we immediately start a new RequestTask to handle it. RequestTask inherits from Runnable and can be started by Executor.

In RequestTask, we use a while loop to continuously process requests in the request queue until the queue is empty. If the queue is empty, we use synchronized and wait to put the current thread into a waiting state until a new request is added to the request queue before being woken up.

Next, we need to implement an HTTP client. We can use the URL.openConnection() method that comes with Java to create an HTTP connection, and call the setRequestMethod() method of HttpURLConnection to set the request method, the setRequestProperty() method to set the request header, and the setDoOutput() method to enable the output stream, etc. .

public class HttpClient {
    public Response execute(Request request) throws IOException {
        URL url = new URL(request.getUrl());
        HttpURLConnection connection = (HttpURLConnection) url.openConnection();
        connection.setRequestMethod(request.getMethod());
        connection.setConnectTimeout(request.getConnectionTimeout());
        connection.setReadTimeout(request.getReadTimeout());
​
        for (Map.Entry<String, String> entry : request.getHeaders().entrySet()) {
            connection.setRequestProperty(entry.getKey(), entry.getValue());
        }
​
        if ("POST".equals(request.getMethod()) || "PUT".equals(request.getMethod())) {
            connection.setDoOutput(true);
            OutputStream outputStream = connection.getOutputStream();
            outputStream.write(request.getBody());
            outputStream.flush();
            outputStream.close();
        }
​
        int responseCode = connection.getResponseCode();
        String responseMessage = connection.getResponseMessage();
        Map<String, List<String>> headers = connection.getHeaderFields();
        byte[] responseBody = null;
​
        if (responseCode >= 200 && responseCode < 300) {
            InputStream inputStream = connection.getInputStream();
            ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
            byte[] buffer = new byte[1024];
            int bytesRead;
            while ((bytesRead = inputStream.read(buffer)) > 0) {
                byteArrayOutputStream.write(buffer, 0, bytesRead);
            }
            responseBody = byteArrayOutputStream.toByteArray();
​
            try {
                inputStream.close();
            } catch (IOException e) {
                // ignore
            }
        } else {
            InputStream inputStream = connection.getErrorStream();
            if (inputStream != null) {
                ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
                byte[] buffer = new byte[1024];
                int bytesRead;
                while ((bytesRead = inputStream.read(buffer)) > 0) {
                    byteArrayOutputStream.write(buffer, 0, bytesRead);
                }
                responseBody = byteArrayOutputStream.toByteArray();
​
                try {
                    inputStream.close();
                } catch (IOException e) {
                    // ignore
                }
            }
        }
​
        return new Response(responseCode, responseMessage, headers, responseBody);
    }
}

In the above code, we use HttpURLConnection to establish HTTP connection. We first set the request method, connection timeout and read timeout, and then set the request header according to the Header in the request. If the request method is POST or PUT, we enable the output stream and write the request body to the output stream. We also handle the input stream when the request is wrong, read it out and save it in the ResponseBody. Finally, we return a Response object, which contains the HTTP response status code, response information, Header and ResponseBody.

Finally, we need to implement an OkHttp class, which encapsulates the above-mentioned RequestHandler and HttpClient, as well as some other methods, such as interceptors. Because this class is more complicated, here only provides a simple framework for reference.

public class OkHttp {
    private final RequestHandler requestHandler;
    private final HttpClient httpClient;
    private final List<Interceptor> interceptors;
​
    public OkHttp() {
        requestHandler = new RequestHandler();
        httpClient = new HttpClient();
        interceptors = new ArrayList<>();
    }
​
    public Response execute(Request request) throws IOException {
        for (Interceptor interceptor : interceptors) {
            request = interceptor.intercept(request);
        }
​
        return httpClient.execute(request);
    }
​
    public void enqueue(Request request) {
        requestHandler.enqueue(request);
    }
​
    public void addInterceptor(Interceptor interceptor) {
        interceptors.add(interceptor);
    }
​
    public interface Interceptor {
        Request intercept(Request request);
    }
}

In the above code, we use the execute() method to complete the synchronous HTTP request, use the enqueue() method to complete the asynchronous HTTP request, and use the addInterceptor() method to add the interceptor. We also define an Interceptor interface, which can be used to implement custom interceptors.

To sum up, writing a high-concurrency OkHttp network request framework by hand is a relatively complicated task, which requires knowledge of multi-threading, network programming, HTTP protocol, etc. The code provided above is for reference only, and needs to be optimized and improved according to specific requirements and scenarios in actual implementation.

For more information about the Android network framework, you can refer to the technical document "Android Core Technical Manual" . It not only includes the technology section of the network framework; it also includes more than 30 sections of Android core technology information, click to view the detailed categories to get related information.

Summarize

OkHttp is a popular web access framework that can be used to make HTTP and HTTP/2 requests in Android and Java applications. The purpose of writing the OkHttp framework by yourself is to gain a deep understanding of the functions and internal implementation of this framework, and to realize some functions and features by yourself.

When implementing the OkHttp high-concurrency network access framework, the following aspects need to be considered:

  1. The life cycle of a network request: some operations need to be performed before the request starts and after the request ends, such as establishing a connection, sending a request, receiving a response, and so on. These operations need to be called when appropriate.
  2. Connection pool management: In order to reduce network overhead and improve performance, a connection pool can be used to manage reusable connections, avoiding frequent establishment and disconnection.
  3. Interceptors for requests and responses: Interceptors are one of the core components of OkHttp processing network requests and responses. Interceptors can be used to log, modify, retry, and cache requests and responses, and they can help improve application performance and reliability.
  4. Thread pool management: In order to support highly concurrent request and response processing, it is necessary to use a thread pool to manage thread execution. The thread pool can avoid excessive thread creation and destruction, and improve the performance of the application.

Guess you like

Origin blog.csdn.net/m0_71524094/article/details/130134226