本文将从源码角度浅析OkHttp,主要在于OkHttp处理的流程,所以叫浅析。具体的用法不是本文的重点。通过对OkHttp的基本用法来一步步探索OkHttp的运行流程。
OkHttp实际上是利用线程池来处理子线程的任务即网络请求,处理得到的结果是运行在子线程中,所以我们需要对数据通过Handler等方法处理到主线程。OkHttp利用的线程池是类似于CacheThreadPool的,具体的创建方法为:
public synchronized ExecutorService executorService() {
if (executorService == null) {
executorService = new ThreadPoolExecutor(0, Integer.MAX_VALUE, 60, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>(), Util.threadFactory("OkHttp Dispatcher", false));
}
return executorService;
}
既然用到了线程池,所以这也是OkHttp也具有线程池的优点:重用线程池中的线程,我们可以同时创建许多个线程来请求网络,且不会带来额外的性能开销。
从OkHttp的基本用法,我们一步步分析流程,OkHttp的基本Get用法:
//创建okHttpClient对象
OkHttpClient mOkHttpClient = new OkHttpClient(); //第一步
//创建一个Request
final Request request = new Request.Builder()
.url("https://github.com/hongyangAndroid")
.build(); //第二步
//new call
Call call = mOkHttpClient.newCall(request); //第三步
//请求加入调度
call.enqueue(new Callback() //第四步
{
@Override
public void onFailure(Request request, IOException e)
{
}
@Override
public void onResponse(final Response response) throws IOException
{
//String htmlStr = response.body().string();
}
});
首先第一二步是定义两个对象OkHttpClient和Request。
OkHttpClient为保存网络配置的‘javabean‘,可以配置连接时间,读取时间等,最主要的是配置插值器Interceptor(OkHttp巧妙的运用了插值器功能,代码值得借鉴,关于Interceptor类,稍后重点分析)
Request类为保存请求数据的’javabean‘,可以配置url,header,请求方法等。
通过第三步的newCall方法我们创建了一个RealCall类,该类实际上是一个Runnable
/**
* Prepares the {@code request} to be executed at some point in the future.
*/
@Override public Call newCall(Request request) {
return new RealCall(this, request, false /* for web socket */);
}
第四步调用RealCall类的异步请求方法enqueue请求数据。下面我们跟着该方法,一步一步分析流程
首先来看RealCall的enqueue方法:
@Override public void enqueue(Callback responseCallback) {
synchronized (this) {
if (executed) throw new IllegalStateException("Already Executed");
executed = true;
}
captureCallStackTrace();
client.dispatcher().enqueue(new AsyncCall(responseCallback)); //主要看这一步
}
调用OkHttpClient类中的dispatcher()方法返回一个Dispatcher对象,然后调用其enqueue()方法
synchronized void enqueue(AsyncCall call) {
//如果正在运行的call小于设置的最大值,那么将该Runnable加入到运行列表并执行,否则加入到等待列表
if (runningAsyncCalls.size() < maxRequests && runningCallsForHost(call) < maxRequestsPerHost) {
runningAsyncCalls.add(call);
executorService().execute(call);
} else {
readyAsyncCalls.add(call);
}
}
创建的线程池enecutorService()理论上可以容纳无穷多个Runnable,我们通过maxRequests来控制线程池中的数量。
通过executorService的execute()方法,我们会执行AsyncCall中的run方法,而AsyncCall是继承NamedRunnable的,NamedRunnable类:
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();
}
AsyncCall类会执行execute方法:
@Override protected void execute() {
boolean signalledCallback = false;
try {
Response response = getResponseWithInterceptorChain();
if (retryAndFollowUpInterceptor.isCanceled()) {
signalledCallback = true;
//通过回调就能得到结果
responseCallback.onFailure(RealCall.this, new IOException("Canceled"));
} else {
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);
}
}
通过
getResponseWithInterceptorChain()方法获取到从服务器返回的数据,这里面主要是对Interceptor类进行添加,来看一下方法体:
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);
return chain.proceed(originalRequest);
}
将Interceptor添加到ArrayList表中进行顺序执行,通过OkHttpClient的addInterceptor()方法逐步添加自定义的Interceptor,最后通过CallServerInterceptor方法获取到最终的网络数据。最后看一眼RealInterceptorChain类:
/**
* A concrete interceptor chain that carries the entire interceptor chain: all application
* interceptors, the OkHttp core, all network interceptors, and finally the network caller.
*/
public final class RealInterceptorChain implements Interceptor.Chain {
private final List<Interceptor> interceptors;
private final StreamAllocation streamAllocation;
private final HttpCodec httpCodec;
private final Connection connection;
private final int index;
private final Request request;
private int calls;
public RealInterceptorChain(List<Interceptor> interceptors, StreamAllocation streamAllocation,
HttpCodec httpCodec, Connection connection, int index, Request request) {
this.interceptors = interceptors;
this.connection = connection;
this.streamAllocation = streamAllocation;
this.httpCodec = httpCodec;
this.index = index;
this.request = request;
}
@Override public Connection connection() {
return connection;
}
public StreamAllocation streamAllocation() {
return streamAllocation;
}
public HttpCodec httpStream() {
return httpCodec;
}
@Override public Request request() {
return request;
}
@Override public Response proceed(Request request) throws IOException {
return proceed(request, streamAllocation, httpCodec, connection);
}
public Response proceed(Request request, StreamAllocation streamAllocation, HttpCodec httpCodec,
Connection 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 && !sameConnection(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");
}
// Call the next interceptor in the chain.
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;
}
private boolean sameConnection(HttpUrl url) {
return url.host().equals(connection.route().address().url().host())
&& url.port() == connection.route().address().url().port();
}
}
其中的proceed()方法,通过遍历Interceptor列表,依次执行各自的intercept()方法,在Interceptor的实现类中,一定要调用proceed方法依次遍历剩余Interceptor。这种方法非常巧妙的获取到列表中的最后一个值,并且第一个Interceptor可以获取到最后一个Interceptor中的值,并且可以依次加载中间的类。刚开始看这段代码比较迷惑,非常棒,在针对插值器方法的时候,非常有效简洁明了,其中一个Interceptor:
public final class ConnectInterceptor implements Interceptor {
public final OkHttpClient client;
public ConnectInterceptor(OkHttpClient client) {
this.client = client;
}
@Override public Response intercept(Chain chain) throws IOException {
RealInterceptorChain realChain = (RealInterceptorChain) chain;
Request request = realChain.request();
StreamAllocation streamAllocation = realChain.streamAllocation();
// We need the network to satisfy this request. Possibly for validating a conditional GET.
boolean doExtensiveHealthChecks = !request.method().equals("GET");
HttpCodec httpCodec = streamAllocation.newStream(client, doExtensiveHealthChecks);
RealConnection connection = streamAllocation.connection();
return realChain.proceed(request, streamAllocation, httpCodec, connection);
}
}
这样整个OkHttp异步请求网络的流程就走完了,项目中异步请求用的较多。
总结:OkHttp利用线程池实现异步操作,通过Interceptor插值器来获取数据,处理数据