OKhttp Dispatcher如何调度网络请求的

Okhttp 基本使用流程

  • 1、创建okhttpClient

    • OkHttpClient ok=new OkHttpClient.Builder().build();		
      
  • 2 、创建Request请求

    • Request request=new Request.Builder().build();
      
  • 3、创建创建Call对象 Call 是个接口 实际是通过其实现类RealCall 得到的call

    • Call call=ok.newCall(request);
      
      • 这里的newCall 内部实际是创建的RealCall 所以后面分析同步异步的方法都在这个类里

      • @Override 
        public Call newCall(Request request) {
            return RealCall.newRealCall(this, request, false /* for web socket */);
          }
        
  • 4、调用

    • 同步

      • call.execute();
        
    • 异步

      • call.enqueue();
        

okHttp的Dispatcher调度过程分析

Okhttp的Dispatcher是什么
  • Dispatcher 是 okhttp 给我们提供的 执行异步请求时的策略. 简单理解就是负责调度网络请求的
Dispatcher 在哪里初始化里面又包含什么
  • Dispatcher是在创建okHttpclient的时候创建的在builder里

    • public class OkHttpClient implements Cloneable, Call.Factory, WebSocket.Factory {
          public static final class Builder {
          Dispatcher dispatcher;
      		
          public Builder() {
            dispatcher = new Dispatcher();
            ///
          }
      
          Builder(OkHttpClient okHttpClient) {
            this.dispatcher = okHttpClient.dispatcher;
              
          }
      }
      
  • DIspatcher 内部用什么来维护请求调度策略

    • int maxRequests = 64; //同时执行的最大请求数,可通过setMaxRequests设置
      
    • Runnable idleCallback;// 空闲回调
      
    • int maxRequestsPerHost = 5;// 同主机最大同时请求数,可通过setMaxRequestsPerHost设置
      
    • ExecutorService executorService;// 执行异步任务的线程池 其参数 核心线程数为0,最大线程数为Integer.MAX_VALUE ,空闲等待时间 60s  核心数为0 代表空闲时间一过线程清空
      
    • Deque<AsyncCall> readyAsyncCalls;// 异步任务就绪队列
      
    • Deque<AsyncCall> runningAsyncCalls;//异步任务执行队列
      
    • Deque<AsyncCall> runningAsyncCalls;//同步任务队列
      
    • Dispatcher 就是通过上述的队列以及线程池维护的任务调度

接下来分析Dispatcher 如何调度
  • 根据上述我们都知道Okhttp请求分为异步和同步所以调度我们区分两种来分析
    • 同步
    • 异步
同步 Dispatcher 如何调度
  • okhttp执行同步请求是通过call.execute() ,直接看execute的代码

    •  @Override public Response execute() throws IOException {
          synchronized (this) {
             // 1. 检测call 是否执行过 执行过就抛出异常 “已经执行”
            if (executed) throw new IllegalStateException("Already Executed");
            executed = true;
          }
           try {
            //2. 这里dispatcher 将 任务ececuted 到
            client.dispatcher().executed(this);
            Response result = getResponseWithInterceptorChain();
            ///···
          } catch (IOException e) {
               ///```
           } finally {
             ///3 任务执行结束 通过Dispatcher finished掉
            client.dispatcher().finished(this);
          }
        }
      
      • 上面 2 处注释 dispatcher 调用 executed(this); 将任务放到同步队列中 我们看executed方法

        • synchronized void executed(RealCall call) {
              // 将call 对象加入同步队列
              runningSyncCalls.add(call);
            }
          
      • 注释 3 处 当任务结束时调用了Finished 方法结束 当前call 我们看起内部如何实现

        • private <T> void finished(Deque<T> calls, T call) {
              Runnable idleCallback;
              synchronized (this) {
                  // 1、移除当前传递进来的Call 任务
                if (!calls.remove(call)) throw new AssertionError("Call wasn't in-flight!");
                  // 2 设置空闲回调
                idleCallback = this.idleCallback;
              }
          	// 3 这里只对异步任务有作用
              boolean isRunning = promoteAndExecute();
          
              if (!isRunning && idleCallback != null) {
                idleCallback.run();
              }
            }
          
同步调度总结
  • Dispatcher 调用Execute 将Call 加入 同步队列
  • 任务执行结束 调用finished 将Call 从同步队列移除
异步 Dispatcher 如何调用
  • okhttp执行同步请求是通过call.enqueue() ,直接看enqueue的代码

    •   @Override public void enqueue(Callback responseCallback) {
          synchronized (this) {
              // 1 检测call 是否执行过 执行过就抛出异常 “已经执行”
            if (executed) throw new IllegalStateException("Already Executed");
            executed = true;
          }
         	// 2 调用enqueue 传入一个AsynvCall 其实是个Runnable 所以需要线程池调用
          client.dispatcher().enqueue(new AsyncCall(responseCallback));
        }
      
  • 上面 2 处注释 dispatcher 调用 enqueue 将任务放到同步队列中 我们看enqueue方法

    • void enqueue(AsyncCall call) {
          synchronized (this) {
              // 1 将Async call 加入等待就绪队列
            readyAsyncCalls.add(call);
          }
          // 2 调用促进执行 将符合条件的加入到RunningAsyncCalls 中
          promoteAndExecute();
        }
      
    •   private boolean promoteAndExecute() {
          assert (!Thread.holdsLock(this));
      
          List<AsyncCall> executableCalls = new ArrayList<>();
          boolean isRunning;
          synchronized (this) {
          // for 循环遍历realAsyncCalls 判断asyncCall判断是否满足加入
          // RunningAsyncCall 若满足 则将其从realAsyncCalls中移除
          //并加入RunningAsyncCalls
            for (Iterator<AsyncCall> i = readyAsyncCalls.iterator(); i.hasNext(); ) {
              // 1 获取AsyncCall 
               AsyncCall asyncCall = i.next();
      		// 2 判断runningAsyncCalls 是否超出最大线程数 不是向下执行 是直接跳出
              if (runningAsyncCalls.size() >= maxRequests) break; // Max capacity.
                // 3 判断callsPerHost同一主机请求数数量否超出  不是向下执行 是跳过选择下一个
              if (asyncCall.callsPerHost().get() >= maxRequestsPerHost) continue; // Host max capacity.
      
              i.remove();// 4  在realAsyncCalls中移除该AsyncCall
              asyncCall.callsPerHost().incrementAndGet();
               // 5 加入可执行调用队列
              executableCalls.add(asyncCall);
               // 6  将asyncCall 加入运行队列 runningAsyncCalls
              runningAsyncCalls.add(asyncCall);
            }
            isRunning = runningCallsCount() > 0;
          }
      
          for (int i = 0, size = executableCalls.size(); i < size; i++) {
            // 7 从可执行调用队列中取出async Call  
            AsyncCall asyncCall = executableCalls.get(i);
              // 8 调用asyncCall 的ExecutOn方法传递线程池
            asyncCall.executeOn(executorService());
          }
      
          return isRunning;
        }
      
    •  void executeOn(ExecutorService executorService) {
            assert (!Thread.holdsLock(client.dispatcher()));
            boolean success = false;
            try {
                //9 通过线程池执行Runnable->AsyncCall 
                // AsyncCall 是继承自 NamedRunnable 内部run方法中调用了execute() 
                // 所以我们看execute中做了什么 如下
              executorService.execute(this);
            } catch (RejectedExecutionException e) {
            } finally {
              if (!success) {
                client.dispatcher().finished(this); // This call is no longer running!
              }
            }
          }
      
    •  @Override protected void execute() {
            boolean signalledCallback = false;
            timeout.enter();
            try {
              // 10 通过链式调用 获取response 
              Response response = getResponseWithInterceptorChain();
               // 没取消则响应onResponse 否则响应onFailure
              if (retryAndFollowUpInterceptor.isCanceled()) {
                signalledCallback = true;
                responseCallback.onFailure(RealCall.this, new IOException("Canceled"));
              } else {
                signalledCallback = true;
                responseCallback.onResponse(RealCall.this, response);
              }
            } catch (IOException e) {
              e = timeoutExit(e);
              if (signalledCallback) {
                // Do not signal the callback twice!
                Platform.get().log(INFO, "Callback failure for " + toLoggableString(), e);
              } else {
                eventListener.callFailed(RealCall.this, e);
                responseCallback.onFailure(RealCall.this, e);
              }
            } finally {
               // 11 dispatcher 调用 finished 方法
              client.dispatcher().finished(this);
            }
          }
        }
      
    •  private <T> void finished(Deque<T> calls, T call) {
          Runnable idleCallback;
          synchronized (this) {
              // 12 在运行队列中移除该call
            if (!calls.remove(call)) throw new AssertionError("Call wasn't in-flight!");
            idleCallback = this.idleCallback;
          }
      	// 13 执行promoteAndExecute 再次将可执行的AsyncCall 添加到运行队列
          boolean isRunning = promoteAndExecute();
      
          if (!isRunning && idleCallback != null) {
            idleCallback.run();
          }
        }
      
异步调度总结
  • 1、Dispatcher 调用 enqueue 将AsyncCall 加入 RealAsyncCalls 等待队列
  • 2 、调用 promoteAndExecute() 方法 for 循环从realAsyncCalls 中取出 AsyncCall 并判断是否满足加入RunningAsyncCalls的条件
    • 不超过最大线程数
    • 不超过最大主机同时访问数
    • 若满足 则添加到RunnAsyncCalls中 不满足则跳过判断下一个AsyncCall
  • 3 、从RunnAsyncCalls取出AsyncCall 调用其executeOn方法将线程池传入
  • 4 、线程池executorService通过execute(this)执行当前AsyncCall
    • (AsyncCall 是继承自 NamedRunnable 内部run方法中调用了execute() 所以实现在execute中)
  • 5、在execute方法中 通过责任链执行网络请求获取Response
  • 6、 执行结束 dispatcher 调用finished方法 从RunningAsyncCalls中移除当前AsyncCall ,并调用第二步骤的promoteAndExecute()方法 判断是否有可执行AsyncCall
    • 若有则继续执行
    • 若没有则返回false 并触发空闲回调
  • 这就是异步调用的过程分析了
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值