OkHttp的源码分析之get请求

最近迷上了去研究一些源码,OkHttp在Android项目中广泛应用,应该说Retrofit这个会更广泛些,Retrofit基于OKHttp的RESTful的网络请求的API。其中里面的网络请求就是使用的OKHttp。本文先简单分析下OkHttp,后续在进行分析Retrofit。

我的整体思路就是怎么去阅读OkHttp的源码,理解里面一些比较好的设计方式。本文基于最新的Okhttp,简单介绍下get请求的源码

OkHttp Get请求方式

提供同步和异步请求网络两种方式。

请求方式 简单解释 是否需要开启线程(请求网络属于耗时操作)
同步请求 执行请求是阻塞式,需要等到HTTP响应之后才会返回。同一时刻只能发起一个请求,synchronized会锁住所有代码,新发起的请求会阻塞,直到当前请求完毕或异常来释放锁。 需要在请求的时候,开启子线程,成功之后跳转到UI线程进行渲染UI
异步请求 非阻塞式,通过回调的方式返回到调用者。可以同时发起多个请求,每个请求都会在独立的线程。 不需要开启子线程,在OKHttp里面封装一个线程池来处理异步请求,回调方法还在子线程中,不能在回调方法中更新UI。

关键类

在进入源码分析之前先大体了解一下涉及到的关键类

关键类 实现功能
RealCall

实现了Call接口。用来实现发送请求和得到请求的最后响应

具体有以下方法:

1.同步(execute())和异步(enqueue())请求的方法、

2.取消请求(cancel())的方法、

3.是否取消(isExecuted())的方法、是否执行的方法(isCanceled()) 、获取当前request(request())方法

有以下属性:

1.OkHttpClient client:就是在调用的地方创建的OkHttpClient对象,通过构造函数传入到RealCall中

2.boolean executed,用来保证每次到执行请求的方法,在一个线程中只能调用一次

3.Request originalRequest,调用的地方创建的Request

4.HttpEngine 处理request和response对。

Dispatcher

同步任务的管理和异步任务分发。

具体有以下方法:

1. executed(RealCall call):同步任务的请求,最终会将要请求的任务放入到Deque<RealCall> runningSyncCalls队列中。所以同步任务仍需要新建线程来执行该方法。

2. enqueue(AsyncCall call):异步任务的请求。该类里面维持一个线程池,用来进行执行发送的异步任务。所以异步请求不需要新建线程来执行该方法。

3.void cancelAll():取消所有请求,包括同步、异步、以及超出设置的最大数还在排队的异步任务

4. finished():结束同步任务或者异步任务。分别对应的不同的call。

5. void promoteCalls()这个是私有方法,这个是将之前排队的异步请求放入到runningAsyncCalls队列,并执行该异步请求。

有以下属性:

1. maxRequests = 64 :同时能够处理的最大请求数的默认值,也可以通过set方法设置,超出的放入到4中的等待队列中

2.maxRequestsPerHost = 5:每个主机能够同时处理的最大请求数,超出的放入到4中的等待队列中。即 可以一个IP地址下对应的5个请求。

在每次执行异步请求的时候,都会对5集合中的请求数和5集合中对应的host进行比较,超过的话直接放入到4集合(readyAsyncCalls)中排队

3. ExecutorService executorService:用来执行异步任务的线程池,虽然创建的是一个无界的线程池,但受maxRequests和maxRequestsPerHost影响,也不同时处理无界个请求

4. Deque<AsyncCall> readyAsyncCalls:准备执行的异步任务队列

5. Deque<AsyncCall> runningAsyncCalls:正在执行的异步任务队列

6. Deque<RealCall> runningSyncCalls:正在执行的同步任务队列

当然也会有一些上述属性对应的get和set方法。不做解释

AsyncCall

异步任务的真正执行者。继承于NameRunnable。当调用者调用异步任务的时候,将该call加入到请求队列中

具体有以下方法:

1.void execute() :执行异步任务的方法,最终通过回调方法返回到调用地方

2.取消请求(cancel())的方法、

3.获取当前request(request())方法

有以下属性:

1. Callback responseCallback:请求的任务对应的callback。通过构造方法传入

2. boolean forWebSocket:是否是WebSocket。(支持WebSocket)

Get同步请求

简单示例,具体的使用方式可以参见gitHub的使用文档,我这里简单做源码分析。

     //当然你也可以通过建造者模式new OkHttpClient.Builder()....Build()的方式创建client
        OkHttpClient client = new OkHttpClient();
       //设置request的参数
        Request request = new Request.Builder().url("http://www.baidu.com").build();
       //创建call,看源码,可以知道这里返回的是RealCall
        final Call call = client.newCall(request);

        new Thread() {
            @Override
            public void run() {
                try {
                //发送请求,得到返回的结果
                    Response response = call.execute();
                    Log.d(TAG, "code = " + response.code() + " , msg =  " + response.body().string());
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }.start();

插一句:注意这里暂时使用Thread来创建线程,其实更推荐ThreadPoolExecutor来创建线程池。其相关知识点可以自行了解下,我之前也做过一些分析:

Java提供的四种线程池

线程池的三种缓存队列

进入正文分析:

提供给调用者就是简单的几个方法:

1.创建Request,

2.OkHttpClient根据request来创建Call,从源码中可以看到该得到的Call其实是RealCall——真正的请求执行者

 @Override public Call newCall(Request request) {
    return new RealCall(this, request);
  }

3.调用call里面的execute()方法来将请求发送出去。我们去查看RealCall中的execute()代码

 @Override public Response execute() throws IOException {
   //synchronized关键字用来保证该方法是在同一线程中该方法只能调用一次
    synchronized (this) { 
      if (executed) throw new IllegalStateException("Already Executed");
      executed = true;
      }
    try {
       //将这次call加入到dispatcher的同步call队列中
      client.dispatcher().executed(this);
      //得到HTTP的响应,这里是关键点
      Response result = getResponseWithInterceptorChain(false);
      if (result == null) throw new IOException("Canceled");
      return result;
    } finally {
      client.dispatcher().finished(this);
    }
  }
  •   synchronized关键字用来保证该方法是在同一线程中该方法只能调用一次,如果多次调用会抛出异常
   java.lang.IllegalStateException: Already Executed
        at okhttp3.RealCall.execute(RealCall.java:52)
        at com.j1.aidl.okhttp.OkhttpActivity$3.run(OkhttpActivity.java:99)
  •   client.dispatcher().executed(this);将这次call加入到dispatcher的同步call队列中。我们看下dispatcher的源码如下:

  /** Running synchronous calls. Includes canceled calls that haven't finished yet. 
  * 所有的运行中的同步请求 。包括那些还没有结束就被取消的请求(从源码中可以看到调用cancel()取消方法
  * 并没有将请求从runningSyncCalls移除,只有调用finish()方法的时候从才会runningSyncCalls移除)
  */
  private final Deque<RealCall> runningSyncCalls = new ArrayDeque<>();

  /** 将同步请求加入到队列中 */
  synchronized void executed(RealCall call) {  
    runningSyncCalls.add(call);
  }
  •    什么时候才会去发送请求呢,Response result = getResponseWithInterceptorChain(false)这里是关键点,用来得到HTTP的响应。这个getResponseWithInterceptorChain这个源码,后续我会单独去分析,本文暂时不去过多解释,本文只要知道该方法负责去完成发送请求,得到服务器返回的结果。
  • 最后调用 client.dispatcher().finished(this);将这次请求从队列中移除
  /** Used by {@code Call#execute} to signal completion. */
  synchronized void finished(Call call) {
    if (!runningSyncCalls.remove(call)) throw new AssertionError("Call wasn't in-flight!");
  }

Get异步请求     

简单实例

OkHttpClient client = new OkHttpClient();
        //设置request的参数
        Request request = new Request.Builder().url("http://www.baidu.com").build();
       //创建call,看源码,可以知道这里返回的是RealCall
        final Call call = client.newCall(request);
       //不同于同步请求,调用另外的方法,通过回调返回结果
        call.enqueue(new Callback() {
            @Override
            public void onFailure(Call call, IOException e) {
            }
            @Override
            public void onResponse(Call call, Response response) throws IOException {
                if (response.isSuccessful()) {
                    Log.d(TAG, "code = " + response.code() + " , msg =  " + response.body().string());
                }
            }
        });

1和2同同步操作,直接看第3步

3.同样调用RealCall中的execute()方法,查看源码如下:

  void enqueue(Callback responseCallback, boolean forWebSocket) {
  //同样synchronized来保证每次线程只调用该方法一次
    synchronized (this) {
      if (executed) throw new IllegalStateException("Already Executed");
      executed = true;
    }
   //执行AsyncCall里的run方法
    client.dispatcher().enqueue(new AsyncCall(responseCallback, forWebSocket));
  }
  • synchronized关键字用来保证该方法是在同一线程中该方法只能调用一次,如果多次调用会抛出异常
  • 调用client.dispatcher().enqueue(new AsyncCall(responseCallback, forWebSocket));来执行AsyncCall的run方法。因为可以看到在dispatcher里面维护的一个线程池。该方法里面将AsyncCall加到线程池里面,执行AsyncCall的run方法。源码如下:
  synchronized void enqueue(AsyncCall call) {
//首先要判断运行异步任务队列中是否已满,如果没有满的话,则执行run方法;如果已满,只能加入到等待队列中等待
    if (runningAsyncCalls.size() < maxRequests && runningCallsForHost(call) < maxRequestsPerHost) {
      runningAsyncCalls.add(call);
      executorService().execute(call);
    } else {
      readyAsyncCalls.add(call);
    }
  }
  • 那么请求异步任务的重任就落到了AsyncCall身上了,该类定义在RealCall中的内部类,其执行异步任务的execute()方法的源码如下:
  @Override protected void execute() {
      boolean signalledCallback = false;
      try {
        //通过该方法获取到response,同同步任务一样
        Response response = getResponseWithInterceptorChain(forWebSocket);
       //不同的是,通过回调的方式返回
        if (canceled) {
          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 {
      //将该异步任务从队列中移除,同时还会对readyAsyncCalls队列中判断是否有还没有执行的异步任务
        client.dispatcher().finished(this);
      }
    }
  • 过程和同步类似,都是通过getResponseWithInterceptorChain获取到response,将结果通过回调方式返回,最后调用client.dispatcher().finished(this);将这次请求从队列中移除,finished()源码如下:
  synchronized void finished(AsyncCall call) {
    if (!runningAsyncCalls.remove(call)) throw new AssertionError("AsyncCall wasn't running!");
    promoteCalls();
  }
  • 同时在finished()方法中还要检测readyAsyncCalls队列中是否有待执行的异步任务,如果有的话,执行异步任务,并将异步任务放到runningAsyncCalls集合中。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.
    }
  }

总结

整理完这篇文档之后,发现自己对OKHttp又有了新的认识,后续我要分析下RealCall中的 getResponseWithInterceptorChain()方法了,希望自己有所进步

猜你喜欢

转载自blog.csdn.net/nihaomabmt/article/details/81707439
今日推荐