okttp3-源码分析(1)execute()方法和enqueue()方法

最近在看okhttp源码,我们都知道okhttp是用来进行网络请求,下面就从最常用的execute()方法和enqueue()方法切入,来分析okhttp的请求过程。
先来看okhttp的基本用法,之后的分析应该都会以基本用法来进行:

String url = "http://wwww.baidu.com";//这里是URL
OkHttpClient okHttpClient = new OkHttpClient();//先构建okhttp客户端
final Request request = new Request.Builder()//构建request对象
        .url(url)
        .get()//默认就是GET请求,可以不写
        .build();
Call call = okHttpClient.newCall(request);//创建Call对象

Response response = call.execute();//同步方法,会抛异常

call.enqueue(new Callback() {//执行异步请求
    @Override
    public void onFailure(Call call, IOException e) {
       //这里是请求失败的逻辑
    }

    @Override
    public void onResponse(Call call, Response response) throws IOException {
       //这里写请求成功的逻辑
    }
});

execute()同步请求

先来看看execute()方法,该方法会立即进行请求。调用该方法时,实际上调用的是RealCall类下的execute()方法。Call本身是一个接口,而RealCall实现了Call接口

  @Override public Response execute() throws IOException {
    synchronized (this) {//同步检测,看请求是否已经执行
      if (executed) throw new IllegalStateException("Already Executed");
      executed = true;
    }
    captureCallStackTrace();//有待研究,和重定向拦截器油管。机翻:捕获调用堆栈跟踪
    timeout.enter();
    eventListener.callStart(this);//事件监听器
    try {
      client.dispatcher().executed(this);//实际调用dispatcher来真正执行,在这里会把请求加入执行队列
      Response result = getResponseWithInterceptorChain();
      //上面这个是重要方法,会调用拦截器来获得请求
      if (result == null) throw new IOException("Canceled");//下面这些都是异常情况处理
      return result;
    } catch (IOException e) {
      e = timeoutExit(e);
      eventListener.callFailed(this, e);
      throw e;
    } finally {
      client.dispatcher().finished(this);//这里会把请求移出执行队列
    }
  }

补充点:
dispatcher是okhttp的重要类。他里面维护三个队列,这里涉及到的队列是 runningSyncCalls,即同步执行队列。
其他两个队列分别是异步准备队列,和异步执行队列。

下面看很重要的getResponseWithInterceptorChain()方法,不管是同步请求还是异步请求都会调用该方法。

  Response getResponseWithInterceptorChain() throws IOException {
    // Build a full stack of interceptors.
    List<Interceptor> interceptors = new ArrayList<>();
    interceptors.addAll(client.interceptors());//自定义的应用拦截器
    interceptors.add(retryAndFollowUpInterceptor);//重定向拦截器
      //进行重定向的工作,在这里创建StreamAllocation(很重要的类)
    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);
  }

这里就是okhttp责任链的实现,这里需要搞清楚,还得认真研究每个拦截器,这部分的坑就在下一节再填吧。
总之这个方法就是调用各个拦截器,进行一系列的设置之后向服务器发送请求,并且返回请求结果。

enqueue() 异步请求

异步请求操作中重要的实现类就是RealCall下的内部类AsyncCall,AsyncCall继承了抽象类NamedRuunble,而抽象类NamedRunnble实现了Runable接口,并重写了run方法。

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();//调用execut()方法开始时执行
    } finally {
      Thread.currentThread().setName(oldName);
    }
  }

  protected abstract void execute();//需要子类实现
}

//下面是AsyncCall下的execute()方法
    @Override protected void execute() {
      boolean signalledCallback = false;
      timeout.enter();
      try {
        Response response = getResponseWithInterceptorChain();
        //调用责任链来获得请求(上面已经介绍过了)
        if (retryAndFollowUpInterceptor.isCanceled()) {
          signalledCallback = true;
          responseCallback.onFailure(RealCall.this, new IOException("Canceled"));
          //如果被取消,就执行我们传入的responseCallback的onFailure方法
        } else {
          signalledCallback = true;
          responseCallback.onResponse(RealCall.this, response);
          //如果成功,就执行我们传入的responseCallback的onResponse方法
        }
      } 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 {
        client.dispatcher().finished(this);//移出队列
      }
    }

我们常用的enqueue方法实际调用的同样是RealCall下的enqueue() 方法。这个方法做的其实就是吧请求封装成AsyncCall并加入队列,因为AsyncCall已经间接实现了Runnable的接口,所以这个方法的最终目的是执行AsyncCall线程,在okhttp中会交给线程池处理。

  @Override public void enqueue(Callback responseCallback) {
    synchronized (this) {
      if (executed) throw new IllegalStateException("Already Executed");
      executed = true;
    }
    captureCallStackTrace();
    eventListener.callStart(this);
    //以上部分的代码,基本和execute()方法一样,可以理解成请求前的准备工作吧,后面再来把这部分坑补上
    client.dispatcher().enqueue(new AsyncCall(responseCallback));
    //创建一个异步线程,直接丢进异步准备队列,具体看下面分析
  }

//下面是Dispatcher里面的enqeue方法
  void enqueue(AsyncCall call) {
    synchronized (this) {
      readyAsyncCalls.add(call);//加入准备队列
    }
    promoteAndExecute();//异步执行,重要方法
  }

不管是execute()还是enqueue()都会调用dispatcher的同名方法。可见dispatcher正如他的类名一样,是okhttp的调度中心
区别就是:
dispatcher的execute()方法加入队列后,方法就结束了
而enqueue()方法只是加入准备队列,把执行交给了promoteAndExecute()去做。

下面看promoteAndExecute()方法:

//顾名思义,把准备队列的请求推到运行队列,并执行
  private boolean promoteAndExecute() {
    assert (!Thread.holdsLock(this));//断言,调试用

    List<AsyncCall> executableCalls = new ArrayList<>();//需要执行的请求队列
    boolean isRunning;//是否在执行的标志

    synchronized (this) {
      for (Iterator<AsyncCall> i = readyAsyncCalls.iterator(); i.hasNext(); ) {
        AsyncCall asyncCall = i.next();//利用迭代器,在准备队列中取出每个请求

        if (runningAsyncCalls.size() >= maxRequests) break;
          // Max capacity.执行队列已经满了的话,就退出队列
        if (runningCallsForHost(asyncCall) >= maxRequestsPerHost) continue; 
          // Host max capacity. 估计是可同时发送的Host有限制(待研究)

        i.remove();//把请求移出准备队列
        executableCalls.add(asyncCall);//加入需要执行的请求队列(上面创建的那个)
        runningAsyncCalls.add(asyncCall);//加入运行队列
      }
      isRunning = runningCallsCount() > 0;//count大于0,说明正在执行,isRunning就为true
    }

    for (int i = 0, size = executableCalls.size(); i < size; i++) {
      AsyncCall asyncCall = executableCalls.get(i);//取出请求
      asyncCall.executeOn(executorService());
      //创建executorService线程池,并传给给RealCall的内部类asyncall 的excuteOn执行
    }

    return isRunning;
  }
	
	//这里是executorService线程池的创建代码
 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));
//同步创建,核心线程为0,最大线程数为Integer.MAX_VALUE,线程空闲存活时间为60s,没有容量的队列
    }
    return executorService;
  }

//这里是AsyncCall的executeOn()方法
    void executeOn(ExecutorService executorService) {
      assert (!Thread.holdsLock(client.dispatcher()));//断言.方便调试程序?(不确定)
      boolean success = false;
      try {
        executorService.execute(this);//这个this就是AsyncCall,可以理解为异步线程
        //异步执行,把请求交给executeService执行,executeService是dispatcher里创建的线程池
        success = true;
      } catch (RejectedExecutionException e) {
        InterruptedIOException ioException = new InterruptedIOException("executor rejected");
        ioException.initCause(e);
        eventListener.callFailed(RealCall.this, ioException);
        responseCallback.onFailure(RealCall.this, ioException);
      } finally {
        if (!success) {
        //将它从队列中移出
          client.dispatcher().finished(this); // This call is no longer running!
        }
      }
    }

总结

okhttp的execute()方法在调用Dispatch的对应方法,将请求加入同步执行队列后,会直接进行网络请求。
而enqueue()方法就较为复杂一点,会创建一个间接实现了Runnable接口的AsyncCall对象(该对象重写了run方法,并把进行网络请求的逻辑写到run方法中),并把AsyncCall对象加入到异步准备队列中,之后在Dispatcher的promoteAndExecute()方法中又会将异步准备队列里的AsyncCall对象取出,添加到异步执行队列中并把AsyncCall对象交给名为executorService的线程池里内执行。

最后,在请求执行完成后,不管是同步和异步请求,都会将自己从对应的队列中移出。

  • 0
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值