介绍
OkHttp 是一套处理 HTTP 网络请求的依赖库,由 Square 公司设计研发并开源,目前可以在 Java 和 Kotlin 中使用。对于 Android App 来说,OkHttp 现在几乎已经占据了所有的网络请求操作,RetroFit + OkHttp 实现网络请求似乎成了一种标配。因此它也是每一个 Android 开发工程师的必备技能,了解其内部实现原理可以更好地进行功能扩展、封装以及优化。
适用于 Android 和 Java 应用程序的 HTTP 和 HTTP/2 客户端。
OkHttp的4.0.x版本已经全部由java替换到了Kotlin,API的一些使用也会有些不同。
要求
支持的版本
4.0.x :Android 5.0+(API 级别 21+)和 Java 8+。
3.12.x :Android 2.3+(API 级别 9+)和 Java 7+。平台可能不支持 TLSv1.2。(2021-12-31不再支持)
OkHttp有一个库的依赖Okio,用于高性能I/O一个小型library。它适用于 Okio 1.x(用 Java 实现)或 Okio 2.x(升级到 Kotlin)。
本文使用的OkHttp的版本为3.14.2,不是不会接入高版本,主要是4.0.x版本已经全部由java替换到了Kotlin,Kotlin不太熟怕理解错了,误导人民群众。
dependencies { //本文使用 implementation 'com.squareup.okio:okio:1.15.0' implementation 'com.squareup.okhttp3:okhttp:3.14.2' //高版本 // define a BOM and its version implementation(platform("com.squareup.okhttp3:okhttp-bom:4.9.0")) // define any required OkHttp artifacts without version implementation("com.squareup.okhttp3:okhttp") implementation("com.squareup.okhttp3:logging-interceptor") }
网络请求流程分析
OkHttp 经过几次迭代后,已经发生了很多变化。更好的 WebSocket 支持、更多的 Interceptor 责任链,甚至连最核心的 HttpEngine 也变成了 HttpCodec。本文会重新梳理整个网络请求的流程,以及实现机制。
先看下 OkHttp 的基本使用:
public void okHttp(String url){ //创建OkHttpClient对象 OkHttpClient client = new OkHttpClient(); //创建Request Request request = new Request.Builder() .url(url) .build(); //创建Call对象client.newCall(request) //通过execute()方法获得请求响应的Response对象 client.newCall(request).enqueue(new Callback() { @Override public void onFailure(Call call, IOException e) {} @Override public void onResponse(Call call, Response response) throws IOException { if(response.isSuccessful()){ String result = response.body().string(); //处理UI需要切换到UI线程处理 } } }); }
除了直接 new OkHttpClient 之外,还可以使用内部工厂类 Builder 来设置 OkHttpClient。如下所示:
public void buildHttp(String url){ OkHttpClient.Builder builder = new OkHttpClient.Builder(); builder.connectTimeout(15, TimeUnit.SECONDS)//设置超时 .addInterceptor(interceptor) //拦截器 .proxy(proxy) //设置代理 .cache(cache); //设置缓存 Request request = new Request.Builder() .url(url) .build(); builder.build().newCall(request).enqueue(new Callback() { @Override public void onFailure(Call call, IOException e) {} @Override public void onResponse(Call call, Response response) throws IOException {} }); }
请求操作的起点从 OkHttpClient.newCall().enqueue() 方法开始
OkHttpClient.newCall
@Override public Call newCall(Request request) { return RealCall.newRealCall(this, request, false /* for web socket */); }
RealCall.newRealCall.java
static RealCall newRealCall(OkHttpClient client, Request originalRequest, boolean forWebSocket) { // Safely publish the Call instance to the EventListener. RealCall call = new RealCall(client, originalRequest, forWebSocket); call.transmitter = new Transmitter(client, call); return call; }
这个方法会返回一个 RealCall 对象,通过它将网络请求操作添加到请求队列中。
RealCall.enqueue
@Override public void enqueue(Callback responseCallback) { synchronized (this) { if (executed) throw new IllegalStateException("Already Executed"); executed = true; } transmitter.callStart(); client.dispatcher().enqueue(new AsyncCall(responseCallback)); }
client.dispatcher()返回Dispatcher,调用 Dispatcher 的 enqueue 方法,执行一个异步网络请求的操作。
Dispatcher 是 OkHttpClient 的调度器,是一种门户模式。主要用来实现执行、取消异步请求操作。本质上是内部维护了一个线程池去执行异步操作,并且在 Dispatcher 内部根据一定的策略,保证最大并发个数、同一 host 主机允许执行请求的线程个数等。
Dispatcher.enqueue
1. void enqueue(AsyncCall call) { synchronized (this) { readyAsyncCalls.add(call); if (!call.get().forWebSocket) { AsyncCall existingCall = findExistingCallWithHost(call.host()); if (existingCall != null) call.reuseCallsPerHostFrom(existingCall); } } promoteAndExecute(); }
实际上就是使用线程池执行了一个 AsyncCall,而 AsyncCall 继承了 NamedRunnable,NamedRunnable 实现了 Runnable 接口,因此整个操作会在一个子线程(非 UI 线程)中执行。
NamedRunnable
/** * Runnable implementation which always sets its thread name. */ public abstract class NamedRunnable implements Runnable { protected final String name; public NamedRunnable(String format, Object... args) { this.name = Util.format(format, args); } @Override public final void run() { String oldName = Thread.currentThread().getName(); Thread.currentThread().setName(name); try { execute(); } finally { Thread.currentThread().setName(oldName); } } protected abstract void execute(); }
在 run 方法中执行了 一个抽象方法 execute 这个抽象方法被 AsyncCall 实现。
AsyncCall.execute
@Override protected void execute() { boolean signalledCallback = false; transmitter.timeoutEnter(); try { Response response = getResponseWithInterceptorChain(); signalledCallback = true; responseCallback.onResponse(RealCall.this, response); } catch (IOException e) { if (signalledCallback) { // Do not signal the callback twice! Platform.get().log(INFO, "Callback failure for " + toLoggableString(), e); } else { responseCallback.onFailure(RealCall.this, e); } } finally { client.dispatcher().finished(this); } }1.
从上面看出而真正获取请求结果的方法是在 getResponseWithInterceptorChain方法中,从名字也能看出其内部是一个拦截器的调用链。
RealCall.getResponseWithInterceptorChain
Response getResponseWithInterceptorChain() throws IOException { // Build a full stack of interceptors. 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(interceptors, transmitter, null, 0, originalRequest, this, client.connectTimeoutMillis(), client.readTimeoutMillis(), client.writeTimeoutMillis()); boolean calledNoMoreExchanges = false; try { Response response = chain.proceed(originalRequest); if (transmitter.isCanceled()) { closeQuietly(response); throw new IOException("Canceled"); } return response; } catch (IOException e) { calledNoMoreExchanges = true; throw transmitter.noMoreExchanges(e); } finally { if (!calledNoMoreExchanges) { transmitter.noMoreExchanges(null); } } }
Interceptor:拦截器是一种强大的机制,可以监视、重写和重试调用。
每一个拦截器的作用如下:
- BridgeInterceptor:主要对 Request 中的 Head 设置默认值,比如 Content-Type、Keep-Alive、Cookie 等。
- CacheInterceptor:负责 HTTP 请求的缓存处理。
- ConnectInterceptor:负责建立与服务器地址之间的连接,也就是 TCP 链接。
- CallServerInterceptor:负责向服务器发送请求,并从服务器拿到远端数据结果。
- RetryAndFollowUpInterceptor:此拦截器从故障中恢复,并根据需要执行重定向。如果呼叫被取消,它可能会引发IOException。
在添加上述几个拦截器之前,会调用 client.interceptors 将开发人员设置的拦截器添加到列表当中。
对于 Request 的 Head 以及 TCP 链接,我们能控制修改的成分不是很多。所以咱们了解 CacheInterceptor 和 CallServerInterceptor。