OkHttp

OkHttp 是一套处理 HTTP 网络请求的依赖库,由 Square 公司设计研发并开源,目前可以在 Java 和 Kotlin 中使用。底层的IO读取是Okio库。它补充了Java.io和java.nio的不足,以便能够更加方便,快速的访问、存储和处理数据。


1
2
3
4
5
6
OkHttpClient client = new OkHttpClient();
Request request = new Request.Builder().url(url).build();
client.newCall(request).enqueue(new Callback(){
onResponse();
onFailure();
})

还可以使用Builder来创建OkHttpClient

1
2
3
4
5
6
OkHttpClient.Builder builder = new OkHttpClient.Builder();
builder.connectTimeout()
.addInterceptor()
.proxy()
.cache();
OkHttpClient client = builder.build();
newCall方法

这个方法会返回一个 RealCall 类型的对象,通过RealCall可以将网络请求操作添加到请求队列中(即下方的Dispatcher方法)。
return RealCall.newRealCall(request);

enqueue方法

调用 Dispatcher 的入队方法,执行一个异步网络请求的操作。
client.dispatcher().enqueue(new AsyncCall(responseCallback));
enqueue(call)
executorService().execute(call)

可以看出,实际上就是使用线程池执行了一个 AsyncCall,而 AsyncCall 实现了 Runnable 接口,因此整个操作会在一个子线程(非 UI 线程)中执行。

继续查看 AsyncCall 中的 run 方法如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public abstract class NamedRunnable implements Runnable {
@Override public final void run() {
execute();
}
protected abstract void execute();
}

final class AsyncCall extends NamedRunnable {
@Override
protected void execute() {
Response response = getResponseWithInterceptorChain();
if (retryAndFollowUpInterceptor.isCanceled()) {
responseCallback.onFailure(RealCall.this, new IOException("Canceled"));
} else {
responseCallback.onResponse(RealCall.this, response);
}
}

责任链

真正获取请求结果的方法是在 getResponseWithInterceptorChain 方法中,从名字也能看出其内部是一个拦截器的调用链,具体代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
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);
}
  • client.interceptors :自定义拦截器
  • BridgeInterceptor:主要对 Request 中的 Head 设置默认值,比如 Content-Type、Keep-Alive、Cookie 等。
  • CacheInterceptor:负责 HTTP 请求的缓存处理。
  • ConnectInterceptor:负责建立与服务器地址之间的连接,也就是 TCP 链接。
  • CallServerInterceptor:负责向服务器发送请求,并从服务器拿到远端数据结果。
1
2
3
4
5
6
// Call the next interceptor in the chain.
RealInterceptorChain next = new RealInterceptorChain(interceptors, streamAllocation, httpCodec,
connection, index + 1, request, call, eventListener, connectTimeout, readTimeout,
writeTimeout);
Interceptor interceptor = interceptors.get(index);
Response response = interceptor.intercept(next);

这里首先去获取传入的interceptors(拦截器列表)取出下一个拦截器,并调用拦截器的intercept方法,并传入下一个RealInterceptorChain对象供当前拦截器调用。
然后每个拦截器根据会根据情况,拿到传入的RealInterceptorChain调用chain.proceed,调用下一个拦截器。


CacheInterceptor 缓存拦截器

CacheInterceptor 主要做以下几件事情:

  • 根据 Request 获取当前已有缓存的 Response(有可能为 null),并根据获取到的缓存 Response,创建 CacheStrategy 对象。

  • 通过 CacheStrategy 判断当前缓存中的 Response 是否有效(比如是否过期),如果缓存 Response 可用则直接返回,否则调用 chain.proceed() 继续执行下一个拦截器,也就是发送网络请求从服务器获取远端 Response。

  • 如果从服务器端成功获取 Response,再判断是否将此 Response 进行缓存操作。

OkHttp 提供了一个默认的缓存类 Cache.java,我们可以在构建 OkHttpClient 时,直接使用 Cache 来实现缓存功能。只需要指定缓存的路径,以及最大可用空间即可,如下所示:

builder.cache(new Cache(getCacheDir(),1024*1024*10))

Cache 内部使用了 DiskLruCach 来实现具体的缓存功能,DiskLruCache 最终会以 journal 类型文件将需要缓存的数据保存在本地。

CallServerInterceptor

CallServerInterceptor 是 OkHttp 中最后一个拦截器,也是 OkHttp 中最核心的网路请求部分,其 intercept 方法如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
@Override 
public Response intercept(Chain chain) throws IOException {

realChain.eventListener().requestHeadersStart(realChain.call());
httpCodec.writeRequestHeaders(request);
realChain.eventListener().requestHeadersEnd(realChain.call(), request);

httpCodec.finishRequest();

//发送请求数据
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
//获取请求结果

……

response = response.newBuilder()
.body(httpCodec.openResponseBody(response))
.build();

}

OkHttp 监听下载进度

retrofit :

在onNext中 直接监听文件的写入

okhttp:

自定义responseBody继承ResponseBody,初始化okhttp时添加自定义拦截器