Android 开发只需添加依赖,如下:
implementation("com.squareup.okhttp3:okhttp:3.13.1")
官方示例1:获取一个 url 上的内容并输出
//Http 客户端 OkHttpClient client = new OkHttpClient(); String run(String url) throws IOException { //构造请求 Request request = new Request.Builder() .url(url) .build(); //执行请求,获取数据 try (Response response = client.newCall(request).execute()) { return response.body().string(); } } 复制代码
官方示例2:给服务器 post
数据
public static final MediaType JSON = MediaType.get("application/json; charset=utf-8"); OkHttpClient client = new OkHttpClient(); String post(String url, String json) throws IOException { RequestBody body = RequestBody.create(JSON, json); Request request = new Request.Builder() .url(url) .post(body) .build(); try (Response response = client.newCall(request).execute()) { return response.body().string(); } } 复制代码
简单分析一下这段代码:这个 post
请求操作看起来很简单。但是我们需要学习其中几个很重要的接口:
OKHttpClient:它代表着 http 客户端
Request:它封装了请求对象,可以构造一个 http 请求对象
Response:封装了响应结果
Call:client.newCall()调用后生成一个请求执行对象Call,它封装了请求执行过程。
下面我们结合这个例子来分析源码:
跟踪源码后发现这个方法是在 Call 中的接口,代码如下:
/** * A call is a request that has been prepared for execution. A call can be canceled. As this object * represents a single request/response pair (stream), it cannot be executed twice. */ public interface Call extends Cloneable { //省略部分代码 //同步执行请求 Response execute() throws IOException; //请求加入队列 void enqueue(Callback responseCallback); //省略部分代码 } 复制代码
Call 的实现类是 RealCall,继续追踪源码的 RealCall.java 文件,可以看到 execute 方法:
@Override public Response execute() throws IOException { //同步锁检查该请求是否已经执行,如果没有则标记executed = ture,否则抛出异常 synchronized (this) { if (executed) throw new IllegalStateException("Already Executed"); executed = true; } captureCallStackTrace(); timeout.enter(); //调用了回调方法 callStart eventListener.callStart(this); try { //okhttp 客户端调用 dispatcher 将执行请求对象 client.dispatcher().executed(this); //调用了 getResponseWithInterceptorChain 方法获取到响应数据 Response,后期还会继续分析 Response result = getResponseWithInterceptorChain(); if (result == null) throw new IOException("Canceled"); return result; } catch (IOException e) { e = timeoutExit(e); //请求失败的回调 callFailed eventListener.callFailed(this, e); throw e; } finally { client.dispatcher().finished(this); //使用 dispather 对象调用 finished 方法,完成请求 } } 复制代码
接下来我们详细分析一下 dispatcher.execute 和 getResponseWithInterceptorChain 这两个方法:
public final class Dispatcher { //省略部分代码 /** Executes calls. Created lazily. */ private @Nullable ExecutorService executorService; /** Ready async calls in the order they'll be run. */ private final Deque<AsyncCall> readyAsyncCalls = new ArrayDeque<>(); /** Running asynchronous calls. Includes canceled calls that haven't finished yet. */ private final Deque<AsyncCall> runningAsyncCalls = new ArrayDeque<>(); /** Running synchronous calls. Includes canceled calls that haven't finished yet. */ private final Deque<RealCall> runningSyncCalls = new ArrayDeque<>(); public Dispatcher(ExecutorService executorService) { this.executorService = executorService; } void enqueue(AsyncCall call) { synchronized (this) { readyAsyncCalls.add(call); // Mutate the AsyncCall so that it shares the AtomicInteger of an existing running call to // the same host. if (!call.get().forWebSocket) { AsyncCall existingCall = findExistingCallWithHost(call.host()); if (existingCall != null) call.reuseCallsPerHostFrom(existingCall); } } promoteAndExecute(); } private <T> void finished(Deque<T> calls, T call) { Runnable idleCallback; synchronized (this) { if (!calls.remove(call)) throw new AssertionError("Call wasn't in-flight!"); idleCallback = this.idleCallback; } boolean isRunning = promoteAndExecute(); if (!isRunning && idleCallback != null) { idleCallback.run(); } } } 复制代码
发现 Dispatcher 是一个调度器,它的作用是对请求进行分发。它的内部有三个队列,分别是同步请求进行队列、异步请求等待队列、异步请求执行队列。
Response getResponseWithInterceptorChain() throws IOException { // Build a full stack of interceptors. List<Interceptor> interceptors = new ArrayList<>(); interceptors.addAll(client.interceptors());//添加自定义拦截器 interceptors.add(retryAndFollowUpInterceptor);//重试重定向拦截器 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(interceptors, null, null, null, 0, originalRequest, this, eventListener, client.connectTimeoutMillis(), client.readTimeoutMillis(), client.writeTimeoutMillis()); //开始责任链模式 return chain.proceed(originalRequest); } 复制代码
此方法构建数个拦截器之后,又构造了一个拦截器责任链。跟踪源码 RealInterceptorChain:
该类主要负责将所有的拦截器串连起来,使所有的拦截器以递归的方式进行实现,从而确保只有所有的拦截器都执行完之后才会返回 Response。以下对该类中的主要方法 proceed 进行讲解:
public Response proceed(Request request, StreamAllocation streamAllocation, HttpCodec httpCodec, RealConnection connection) throws IOException { if (index >= interceptors.size()) throw new AssertionError(); //用于计算拦截器调用该方法的次数 calls++; // If we already have a stream, confirm that the incoming request will use it. if (this.httpCodec != null && !this.connection.supportsUrl(request.url())) { throw new IllegalStateException("network interceptor " + interceptors.get(index - 1) + " must retain the same host and port"); } // If we already have a stream, confirm that this is the only call to chain.proceed(). if (this.httpCodec != null && calls > 1) { throw new IllegalStateException("network interceptor " + interceptors.get(index - 1) + " must call proceed() exactly once"); } //生成下一个拦截器调动该方法的 RealInterceptorChain 对象,其中 index+1 用于获取下一个拦截器 RealInterceptorChain next = new RealInterceptorChain( interceptors, streamAllocation, httpCodec, connection, index + 1, request); //获取当前拦截器 Interceptor interceptor = interceptors.get(index); //调用该拦截器并获取返回结果 Response response = interceptor.intercept(next); // Confirm that the next interceptor made its required call to chain.proceed(). if (httpCodec != 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"); } return response; } 复制代码
以上就是我们对 OkHttp 核心源码的分析。当我们发起一个请求的时候会初始化一个 Call 的实例,然后根据同步和异步的不同,分别调用它的 execute() 和 enqueue() 方法。大致过程都是通过拦截器组成的责任链,依次经过重试、桥接、缓存、连接和访问服务器等过程,来获取到一个响应并交给用户。