OKHTTP内部流程

最近无聊看了一下OKHTTP源码,大致了解了一下我们使用OKHTTP发送网络请求之后,内部都发生了什么。简单说一下哈

我们正常发送一个get请求如下
 OkHttpClient client = new OkHttpClient();
 Request request = new Request.Builder().get().url(url).build();
 client.newCall(request).enqueue(new Callback() {
     @Override
     public void onFailure(Call call, IOException e) {
     }

     @Override
     public void onResponse(Call call, Response response) throws IOException {
     }
 });
  @Override 
  public void enqueue(Callback responseCallback) {
   // ......
   client.dispatcher().enqueue(new AsyncCall(responseCallback));
 }

client.newCall创建了一个RealCall对象,通过enqueue方法调用client.dispatcher().enqueue(new AsyncCall(responseCallback)); 将封装好的异步请求交给Dispatcher

查看Dispatcher怎么分发我们的请求
  synchronized void enqueue(AsyncCall call) {
    if (runningAsyncCalls.size() < maxRequests && runningCallsForHost(call) < maxRequestsPerHost) {
      runningAsyncCalls.add(call);
      executorService().execute(call);
    } else {
      readyAsyncCalls.add(call);
    }
  }

可以看出来,Dispatcher维护了两个队列runningAsyncCalls和readyAsyncCalls。

  1. 如果正在运行的队列小于maxRequests也就是64个,并且请求同一个host的数量小于maxRequestsPerHost也就是5个。那么直接将该请求加入到runningAsyncCalls队列,立刻执行。
  2. 如果不满足条件1,那么将该请求加入到readyAsyncCalls队列
看一下如何执行runningAsyncCalls中的请求

通过executorService().execute(call);执行该请求任务。executorService()创建了一个线程池,使用的是不存储元素的阻塞队列SynchronousQueue,也就是没有核心线程,队列不存储请求,当有请求时直接执行。

  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;
  }

然后我们就要看一下AsyncCall了。

  • AsyncCall继承自NamedRunnable,线程池执行execute()方法时,通过NamedRunnable调用AsyncCall重写的execute方法。
  • 通过getResponseWithInterceptorChain()方法获取到response之后,将response返回。
  • 在finally执行 client.dispatcher().finished(this)方法,表明该请求执行完成
final class AsyncCall extends NamedRunnable {
    private final Callback responseCallback;
	// ......
    @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) {
          Platform.get().log(INFO, "Callback failure for " + toLoggableString(), e);
        } else {
          eventListener.callFailed(RealCall.this, e);
          responseCallback.onFailure(RealCall.this, e);
        }
      } finally {
        client.dispatcher().finished(this);
      }
    }
  }
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();
}
我们看一下一个请求执行完成以后,还有哪些操作
  • RealCall在执行完成之后会在finally执行 client.dispatcher().finished(this)方法,finish方法会将当前执行过的请求从立即执行队列中移除,然后通过 promoteCalls()方法看是否有任务可以从准备执行队列添加到立即执行队列。
  • 当直接运行的队列小于最大值64,并且准备执行队列中仍有请求时,遍历准备执行队列进行判断
    • 判断如果加上当前请求,直接运行队列请求统一host数量仍然小于5,就把当前请求加入到直接执行队列,立刻执行,此时如果直接运行队列数量大于最大值64,那就中断循环。
    • 判断如果加上当前请求,直接运行队列请求统一host数量如果大于5,就跳过当前请求。当前请求需要等下一个任务执行完成之后,再次调用promoteCalls()方法进行判断。
    • 执行完判断之后,如果立即执行队列达到最大值64,那么就不再继续遍历。如果没有达到最大值64,那就接着遍历,继续判断,继续往立即执行队列中添加
  void finished(AsyncCall call) {
    finished(runningAsyncCalls, call, true);
  }

  void finished(RealCall call) {
    finished(runningSyncCalls, call, false);
  }
  
  private <T> void finished(Deque<T> calls, T call, boolean promoteCalls) {
	// ...
    synchronized (this) {
      // 移除执行过的请求
      if (!calls.remove(call)) throw new AssertionError("Call wasn't in-flight!");
       // 去准备执行队列,看是否要添加请求到立即执行队列
      if (promoteCalls) promoteCalls();
      // ...
    }
  }
  private void promoteCalls() {
    if (runningAsyncCalls.size() >= maxRequests) return; // Already running max capacity.
    if (readyAsyncCalls.isEmpty()) return; // No ready calls to promote.

    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; // Reached max capacity.
    }
  }
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值