Basic usage of okHttp
Let's briefly mention the basic usage of okHttp.
okHttp can use both synchronous and asynchronous requests, although synchronous requests cannot be done in the UI thread, which can cause app crashes.
Synchronization Request
//Construct OkHttpClient final OkHttpClient client=new OkHttpClient.Builder().build(); //Construct Requestor final Request request=new Request.Builder().url("www.baidu.com").build(); new Thread(new Runnable() { @Override public void run() { try { //Request Responded Response response=client.newCall(request).execute(); } catch (IOException e) { e.printStackTrace(); } } }).start();
Asynchronous request
//Construct OkHttpClient final OkHttpClient client=new OkHttpClient.Builder().build(); //Construct Requestor final Request request=new Request.Builder().url("www.baidu.com").build(); //Asynchronous request client.newCall(request).enqueue(new Callback() { @Override public void onFailure(Call call, IOException e) { //Request Failure Response } @Override public void onResponse(Call call, Response response) throws IOException { //Request Successful Response } });
Basic workflow of okHttp
okHttp has three queues:
/** Ready async calls in the order they'll be run. */ //Wait queue for all tasks to be executed private final Deque<AsyncCall> readyAsyncCalls = new ArrayDeque<>(); /** Running asynchronous calls. Includes canceled calls that haven't finished yet. */ //Executing asynchronous request queue holding all executing asynchronous request tasks private final Deque<AsyncCall> runningAsyncCalls = new ArrayDeque<>(); /** Running synchronous calls. Includes canceled calls that haven't finished yet. */ //Executing synchronization request queue, holding all executing synchronization request tasks private final Deque<RealCall> runningSyncCalls = new ArrayDeque<>();
enqueue method
This method passes in an AsyncCall object, each encapsulating all the information required for the request. Also, add the current request to the queue to be executed
void enqueue(AsyncCall call) { synchronized (this) { //Join the waiting queue readyAsyncCalls.add(call); } promoteAndExecute(); }
PromAndExecute method
1. Traverse the task queue to be executed
2. Judge to skip if the number of tasks being performed exceeds the maximum number of requests specified
3. Decide that if the number of requests for the current domain name exceeds the specified maximum number of requests for a single domain name, the current request will not be processed
4. Remove the current request from the list to be executed and add it to the executing queue and executable list
5. Traverse the executable list and throw all the tasks you just performed into the thread pool to execute them
private boolean promoteAndExecute() { assert (!Thread.holdsLock(this)); List<AsyncCall> executableCalls = new ArrayList<>(); boolean isRunning; synchronized (this) { //Traversing the task queue to be executed for (Iterator<AsyncCall> i = readyAsyncCalls.iterator(); i.hasNext(); ) { AsyncCall asyncCall = i.next(); //Judge to skip if the number of tasks being performed exceeds the specified maximum number of requests if (runningAsyncCalls.size() >= maxRequests) break; //Determine that if the number of requests for the current domain name exceeds the specified maximum number of single domain name requests, the current request will not be processed if (runningCallsForHost(asyncCall) >= maxRequestsPerHost) continue; //Remove the current request from the list to execute and add it to the executing queue and executable list i.remove(); executableCalls.add(asyncCall); runningAsyncCalls.add(asyncCall); } isRunning = runningCallsCount() > 0; } //Traverse the executable list, throw all the tasks you just left to be executed into the thread pool to execute for (int i = 0, size = executableCalls.size(); i < size; i++) { AsyncCall asyncCall = executableCalls.get(i); asyncCall.executeOn(executorService()); } return isRunning; }
When dropped to the thread pool, the execute method for each request to AsyncCall is executed
The execute() method has two main functions:
1. Call the getResponseWithInterceptorChain method to construct an interceptor chain to process the request
2. Call dispatcher at the end of the task. Finish method, which moves the current request out of the executing queue, traverses the queue to be executed again, and pushes the task to be executed into the thread pool for execution
protected void execute() { ..... try { //Construct interceptor chains to respond to various interceptors Response response = getResponseWithInterceptorChain(); signalledCallback = true; responseCallback.onResponse(RealCall.this, response); } catch (IOException e) { ... } catch (Throwable t) { ... } finally { //End of task, call finished method client.dispatcher().finished(this); } } }
Let's first look at the finished method, followed by getResponseWithInterceptorChain.
The finished method calls the promoteAndExecute() method again. Remember that the function of this method is to traverse the queue to be executed and drop the task to be executed into the thread pool, because when the request is executed, the thread pool is free, and if there were any requests to be executed before, the execution is called again.
private <T> void finished(Deque<T> calls, T call) { Runnable idleCallback; synchronized (this) { //Remove the current task from the executing queue if (!calls.remove(call)) throw new AssertionError("Call wasn't in-flight!"); idleCallback = this.idleCallback; } //Mainly here, the promoteAndExecute() method is called again boolean isRunning = promoteAndExecute(); if (!isRunning && idleCallback != null) { idleCallback.run(); } }
So here we have a look at how okHttp pushes requests. Here's what each request task does in the thread pool, starting with the figure above
The thread pool executes the execute() method for each requested task.
Call getResponseWithInterceptorChain in execute to construct interceptor chain to process request
protected void execute() { ..... try { //Construct interceptor chains to respond to various interceptors Response response = getResponseWithInterceptorChain(); signalledCallback = true; responseCallback.onResponse(RealCall.this, response); } catch (IOException e) { ... } catch (Throwable t) { ... } finally { //End of task, call finished method client.dispatcher().finished(this); } } }
getResponseWithInterceptorChain() method
This method constructs an interceptor chain object and calls its proceed method
Response getResponseWithInterceptorChain() throws IOException { List<Interceptor> interceptors = new ArrayList<>(); //Add user-defined interceptors interceptors.addAll(client.interceptors()); //Adding reconnection and redirection interceptors interceptors.add(retryAndFollowUpInterceptor); //Add request headers, cookie s, compression processing interceptors interceptors.add(new BridgeInterceptor(client.cookieJar())); //Add Cache Processing Interceptor interceptors.add(new CacheInterceptor(client.internalCache())); //Add Connection Interceptor interceptors.add(new ConnectInterceptor(client)); if (!forWebSocket) { interceptors.addAll(client.networkInterceptors()); } //Add Send Request Interceptor interceptors.add(new CallServerInterceptor(forWebSocket)); //Construct interceptor chain object, index passes in 0 Interceptor.Chain chain = new RealInterceptorChain(interceptors, null, null, null, 0, originalRequest, this, eventListener, client.connectTimeoutMillis(), client.readTimeoutMillis(), client.writeTimeoutMillis()); //Call proceed method of chain object Response response = chain.proceed(originalRequest); if (retryAndFollowUpInterceptor.isCanceled()) { closeQuietly(response); throw new IOException("Canceled"); } return response; }
chain.proceed method
public Response proceed(Request request, StreamAllocation streamAllocation, HttpCodec httpCodec, RealConnection connection) throws IOException { ..... //Construct a new intercept chain object using the original interceptor list, index+1 RealInterceptorChain next = new RealInterceptorChain(interceptors, streamAllocation, httpCodec, connection, index + 1, request, call, eventListener, connectTimeout, readTimeout, writeTimeout); //Interceptor to get current index Interceptor interceptor = interceptors.get(index); //Call the intercept method of the current interceptor and pass in a new intercept chain object (index already + 1) Response response = interceptor.intercept(next); ..... return response; }
interceptor.intercept method
All interceptors except CallServerInterceptor. Intercept method, call chain.proceed() method,
public Response intercept(Chain chain) throws IOException { ..... 1,Do your own interceptor duties 2,call chain.proceed()Method, because of the incoming chain For newly constructed, index Add 1 and re-execute the above chain.proceed When using the method, The acquired interceptor is the next one and will be constructed index+2 New intercept chain object, passed in index+1 Interceptors for }
Chain will not be called until the last interceptor, CallServerInterceptor. Proceed() method, instead of requesting the network, constructs the return Response object