OkHttp-Request-请求执行流程

OkHttp不同版本的代码差异挺大的,本篇文章中分析的源码是基于OkHttp 3.6.0版本进行的。


一、 Request任务的调度

为了分析OkHttp种一次请求的流程,我们先从最简单的请求示例开始,OkHttp中请求分成同步和异步两种:

同步请求

Request request = new Request.Builder()
        .url("http://publicobject.com/helloworld.txt")
        .build();
Response response = client.newCall(request).execute();
复制代码

异步请求

Request request = new Request.Builder()
        .url("http://publicobject.com/helloworld.txt")
        .build();
client.newCall(request).enqueue(new Callback() {
    @Override
    public void onFailure(Call call, IOException e) {
        e.printStackTrace();
    }
    @Override
    public void onResponse(Call call, Response response) throws IOException {
    }
});
复制代码

这两种请求方式都是通过创建一个 Call 来发起的,区别在于同步请求直接调用execute()在当前线程执行,而异步请求则是调用enqueue()将任务加入到队列之中,由调度器 Dispatcher 决定什么时候发起请求。

同步请求在实际开始执行请求之前调用了 dispatcher.execute()将自己加入正在执行的请求列表,并在请求结束后调用 dispatcher.finish()从执行列表中移除,这主要是为了统一管理执行中的任务。
Dispatcher 在异步请求中发挥了重要的作用,其中创建了一个线程池用于异步执行请求任务,还可以通过 setMaxRequests()设置同时允许执行的最大请求数,以及 setMaxRequestsPerHost()设置相同host下最多运行的请求数。

synchronized void enqueue(AsyncCall call) {
    // 正在执行的总任务数及相同host下正在执行的任务数小于阈值时,直接执行任务
    if(runningAsyncCalls.size() < maxRequests && runningCallsForHost(call) < maxRequestsPerHost) {
        runningAsyncCalls.add(call);
        executorService().execute(call);
    } else { // 加入等待队列
        readyAsyncCalls.add(call);
    }
}
复制代码

当正在执行的任务总数及相同 host 下的任务数小于最大值时,直接执行当前请求,而任务数超过限定时,将其加入等待队列。

AsyncCall 继承于 Runnable,相当于是对 RealCall 的一次包装。

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 {
    // 通知 dispatcher 已完成当前请求,开始执行任务调度
    client.dispatcher().finished(this);
    }
  }
}
复制代码

getResponseWithInterceptorChain()中完成实际的网络请求,在执行完毕后调用finished方法通知 dispatcher 执行任务调度,最终的调度任务在promoteCalls中执行。

private void promoteCalls() {
  // 正在执行的任务数任大于阈值,不调度
  if (runningAsyncCalls.size() >= maxRequests) return; 
  // 无等待中任务
  if (readyAsyncCalls.isEmpty()) return; 

  for (Iterator<AsyncCall> i = readyAsyncCalls.iterator(); i.hasNext(); ) {
    AsyncCall call = i.next();
    // 执行等待中任务
    if (runningCallsForHost(call) < maxRequestsPerHost) {
      i.remove();
      runningAsyncCalls.add(call);
      executorService().execute(call);
    }

    // 已达阈值,结束调度
    if (runningAsyncCalls.size() >= maxRequests) return; 
  }
}
复制代码

每次异步请求执行结束后都会拉起正在等待队列中的请求任务。

二、Request执行流程

上面我们看到实际的请求是在getResponseWithInterceptorChain()中完成的,那么我们来看看其中发生了什么。

Response getResponseWithInterceptorChain() throws IOException {
  // Build a full stack of interceptors.
  List<Interceptor> interceptors = new ArrayList<>();
  // 自定义拦截器
  interceptors.addAll(client.interceptors()); 
  // 重定向拦截器
  interceptors.add(retryAndFollowUpInterceptor); 
  // 桥接拦截器(处理header 、cookie 等)
  interceptors.add(new BridgeInterceptor(client.cookieJar())); 
  // 缓存拦截器(处理 cache)
  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 的列表,按照用户自定义的 Interceptor、RetryAndFollowUpInterceptor、BridgeInterceptor、CacheInterceptor、ConnectInterceptor、自定义的 network Interceptor、CallServerInterceptor的顺序将这些拦截器添加到列表中,后面可以看到这个顺序就是请求实际执行的顺序。

在请求的过程中,OkHttp 使用了责任链模式来一步步完成请求,这个责任链就是由 RealInterceptorChain 来实现的。

public Response proceed(Request request, StreamAllocation streamAllocation, HttpCodec httpCodec, Connection connection) throws IOException {
  // 省略部分无关代码

  // 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);

  // ...

  return response;
}
复制代码

在 proceed 中根据 index 依次获取之前传入的 interceptor 处理请求,同时创建了链式节点传递给下一个拦截器,以便下一个拦截器处理完自己的任务后调用。以ConnectInterceptor为例:

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();

    boolean doExtensiveHealthChecks = !request.method().equals("GET");
    // 创建解码器,并建立连接
    HttpCodec httpCodec = streamAllocation.newStream(client, doExtensiveHealthChecks);
    RealConnection connection = streamAllocation.connection();
    // 将请求交给下一个拦截器处理
    return realChain.proceed(request, streamAllocation, httpCodec, connection);
  }
}
复制代码

OkHttp 中的请求流程可以用下面这张图来描述。

总之,请求是以链式的形式一步步进行,所有的拦截器完成自己的任务后就将请求传递给下一个拦截器进行处理,直到最后请求执行完毕。

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
package com.zhy.http.okhttp; import android.os.Handler; import android.os.Looper; import android.text.TextUtils; import android.util.Log; import com.zhy.http.okhttp.cookie.SimpleCookieJar; import okhttp3.Call; import okhttp3.OkHttpClient; import okhttp3.Response; import com.zhy.http.okhttp.builder.GetBuilder; import com.zhy.http.okhttp.builder.PostFileBuilder; import com.zhy.http.okhttp.builder.PostFormBuilder; import com.zhy.http.okhttp.builder.PostStringBuilder; import com.zhy.http.okhttp.callback.Callback; import com.zhy.http.okhttp.https.HttpsUtils; import com.zhy.http.okhttp.request.RequestCall; import java.io.IOException; import java.io.InputStream; import java.util.concurrent.TimeUnit; import javax.net.ssl.HostnameVerifier; import javax.net.ssl.SSLSession; /** * Created by zhy on 15/8/17. */ public class OkHttpUtils { public static final String TAG = "OkHttpUtils"; public static final long DEFAULT_MILLISECONDS = 10000; private static OkHttpUtils mInstance; private OkHttpClient mOkHttpClient; private Handler mDelivery; private OkHttpUtils() { OkHttpClient.Builder okHttpClientBuilder = new OkHttpClient.Builder(); //cookie enabled okHttpClientBuilder.cookieJar(new SimpleCookieJar()); mDelivery = new Handler(Looper.getMainLooper()); if (true) { okHttpClientBuilder.hostnameVerifier(new HostnameVerifier() { @Override public boolean verify(String hostname, SSLSession session) { return true; } }); } mOkHttpClient = okHttpClientBuilder.build(); } private boolean debug; private String tag; public OkHttpUtils debug(String tag) { debug = true; this.tag = tag; return this; } public static OkHttpUtils getInstance() { if (mInstance == null) { synchronized (OkHttpUtils.class) { if (mInstance == null) { mInstance = new OkHttpUtils(); } } } return mInstance; } public Handler getDelivery() { return mDelivery; } public OkHttpClient getOkHttpClient() { return mOkHttpClient; } public static GetBuilder get() { return new GetBuilder(); } public static PostStringBuilder postString() { return new PostStringBuilder(); } public static PostFileBuilder postFile() { return new PostFileBuilder(); } public static PostFormBuilder post() { return new PostFormBuilder(); } public void execute(final RequestCall requestCall, Callback callback) { if (debug) { if(TextUtils.isEmpty(tag)) { tag = TAG; } Log.d(tag, "{method:" + requestCall.getRequest().method() + ", detail:" + requestCall.getOkHttpRequest().toString() + "}"); } if (callback == null) callback = Callback.CALLBACK_DEFAULT; final Callback finalCallback = callback; requestCall.getCall().enqueue(new okhttp3.Callback() { @Override public void onFailure(Call call, final IOException e) { sendFailResultCallback(call, e, finalCallback); } @Override public void onResponse(final Call call, final Response response) { if (response.code() >= 400 && response.code() <= 599) { try { sendFailResultCallback(call, new RuntimeException(response.body().string()), finalCallback); } catch (IOException e) { e.printStackTrace(); } return; } try { Object o = finalCallback.parseNetworkResponse(response); sendSuccessResultCallback(o, finalCallback); } catch (Exception e) { sendFailResultCallback(call, e, finalCallback); } } }); } public void sendFailResultCallback(final Call call, final Exception e, final Callback callback) { if (callback == null) return; mDelivery.post(new Runnable() { @Override public void run() { callback.onError(call, e); callback.onAfter(); } }); } public void sendSuccessResultCallback(final Object object, final Callback callback) { if (callback == null) return; mDelivery.post(new Runnable() { @Override public void run() { callback.onResponse(object); callback.onAfter(); } }); } public void cancelTag(Object tag) { for (Call call : mOkHttpClient.dispatcher().queuedCalls()) { if (tag.equals(call.request().tag())) { call.cancel(); } } for (Call call : mOkHttpClient.dispatcher().runningCalls()) { if (tag.equals(call.request().tag())) { call.cancel(); } } } public void setCertificates(InputStream... certificates) { mOkHttpClient = getOkHttpClient().newBuilder() .sslSocketFactory(HttpsUtils.getSslSocketFactory(certificates, null, null)) .build(); } public void setConnectTimeout(int timeout, TimeUnit units) { mOkHttpClient = getOkHttpClient().newBuilder() .connectTimeout(timeout, units) .build(); } }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值