Retrofit-OkHttpCall源码解读

OkHttp的同步和异步请求

Retrofit在使用OkHttpCall进行网络请求的时候,里面其实就是封装了OkHttp来进行网络请求。在分析OkHttpCall之前,先看下OkHttp怎么来实现同步和异步请求。

1)同步请求

 OkHttpClient client = new OkHttpClient();
 Request request = new Request.Builder().url("http://www.baidu.com").build();
 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();

2)异步请求

OkHttpClient client = new OkHttpClient();
Request request = new Request.Builder().url("http://www.baidu.com").build();
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)创建一个OkHttpClient对象client。

2)通过Request对象进行配置网络请求参数。

3)通过client对象创建出Call

4)调用call中的同步请求或异步请求的方法

那么我们在Retrofit中的OkHttpCall实际上就是对这些过程进行了封装。进入源码学习下

1、实例化

在Retrofit的create()中通过动态代理创建接口实例的时候,会取实例化OkHttpCall

OkHttpCall okHttpCall = new OkHttpCall<>(serviceMethod, args);

查看源码,就是将OkHttpCall里面的serviceMethod和args,其中serviceMethod就是网络请求所需要的数据,args就是我们创建的网络接口的方法的参数。

  OkHttpCall(ServiceMethod<T> serviceMethod, Object[] args) {
    this.serviceMethod = serviceMethod;
    this.args = args;
  }

2、成员变量

  private final ServiceMethod<T> serviceMethod;
  private final Object[] args;
//该请求是否被取消标记位
  private volatile boolean canceled;
//OkHttp请求的实例
  private okhttp3.Call rawCall;
  private Throwable creationFailure; // Either a RuntimeException or IOException.
//是否执行的标记位
  private boolean executed;

我们可以看到里面存在的rawCall就是OkHttp请求的实例。

3、同步请求

 @Override public Response<T> execute() throws IOException {
    okhttp3.Call call;
 //同步锁,保证每次只有一个线程可以访问
    synchronized (this) {
//每次请求只执行一次
      if (executed) throw new IllegalStateException("Already executed.");
      executed = true;
//异常标记,根据不同的异常标记抛出不用的异常
      if (creationFailure != null) {
        if (creationFailure instanceof IOException) {
          throw (IOException) creationFailure;
        } else {
          throw (RuntimeException) creationFailure;
        }
      }
//将call进行赋值,在第一次调用的时候,会检查该实例是否创建,如果创建则直接赋值使用,如果没有创建则通过下面的方法进行创建
      call = rawCall;
      if (call == null) {
        try {
          call = rawCall = createRawCall();
        } catch (IOException | RuntimeException e) {
          creationFailure = e;
          throw e;
        }
      }
    }
//如果被用户取消了请求,则调用call的取消方法来取消该次请求
    if (canceled) {
      call.cancel();
    }
//直接返回
    return parseResponse(call.execute());
  }

从源码中可以看到,使用synchronized同步锁来保证每次只有一个线程可以访问该方法,executed标记位用来标记每次请求只能调用一次。同时我们会将rawCall的实例赋值给局部变量call,第一次rawCall并没有被赋值,则通过CreateRawCall()进行创建rawCall实例,否则直接赋值给局部变量call即可。最后调用call.execute()执行OkHttp的execute()方法,将OkHttp返回的数据转换成Retrofit的所需要的数据格式。

我们看下rawCall的创建过程和返回数据的转换过程。

1)createRawCall()

  private okhttp3.Call createRawCall() throws IOException {
    Request request = serviceMethod.toRequest(args);
    okhttp3.Call call = serviceMethod.callFactory.newCall(request);
    if (call == null) {
      throw new NullPointerException("Call.Factory returned null.");
    }
    return call;
  }

首先就是根据我们之前创建的网络接口转换成OkHttp的Request请求对象。 对应OkHttp请求实例中的

 Request request = new Request.Builder().url("http://www.baidu.com").build();

然后通过serviceMethod来获取OkHttp的实例。从Retrofit的源码我们可以了解到,

   public Retrofit build() {
      if (baseUrl == null) {
        throw new IllegalStateException("Base URL required.");
      }

      okhttp3.Call.Factory callFactory = this.callFactory;
      if (callFactory == null) {
        callFactory = new OkHttpClient();
      }
//。。。。代码省略。。。。
  }

在Retrofit.Builder的build()这里的callFactory就是实例化OkHttp中的OkHttpClient,那么对应这个newCall()即OkHttpClient中对应的方法。也就是执行了我们之前的OkHttp例子的

 OkHttpClient client = new OkHttpClient();
 final Call call = client.newCall(request);  

2)cancel()

  public void cancel() {
    canceled = true;

    okhttp3.Call call;
    synchronized (this) {
      call = rawCall;
    }
    if (call != null) {
      call.cancel();
    }

就是调用的OkHttp的cancel(),不多解释

3)parseResponse(call.execute())

在该方法中调用call.execute()来通过OkHttp请求网络,最后将OkHttp的response转换成Retrofit的response

Response<T> parseResponse(okhttp3.Response rawResponse) throws IOException {
    ResponseBody rawBody = rawResponse.body();

    // Remove the body's source (the only stateful object) so we can pass the response along.
    rawResponse = rawResponse.newBuilder()
        .body(new NoContentResponseBody(rawBody.contentType(), rawBody.contentLength()))
        .build();
//报错
    int code = rawResponse.code();
    if (code < 200 || code >= 300) {
      try {
        // Buffer the entire body to avoid future I/O.
        ResponseBody bufferedBody = Utils.buffer(rawBody);
        return Response.error(bufferedBody, rawResponse);
      } finally {
        rawBody.close();
      }
    }
//成功情况
    if (code == 204 || code == 205) {
      return Response.success(null, rawResponse);
    }

    ExceptionCatchingRequestBody catchingBody = new ExceptionCatchingRequestBody(rawBody);
//将OkHttp返回的内容根据设置的数据转换器的格式转换成Retrofit的返回结果  
  try {
      T body = serviceMethod.toResponse(catchingBody);
      return Response.success(body, rawResponse);
    } catch (RuntimeException e) {
      // If the underlying source threw an exception, propagate that rather than indicating it was
      // a runtime exception.
      catchingBody.throwIfCaught();
      throw e;
    }
  }

上述3步已经完成了同步请求。

4、异步请求

 @Override public void enqueue(final Callback<T> callback) {
    if (callback == null) throw new NullPointerException("callback == null");

    okhttp3.Call call;
    Throwable failure;

 //同步锁,保证每次只有一个线程可以访问
    synchronized (this) {
//每次请求只执行一次
      if (executed) throw new IllegalStateException("Already executed.");
      executed = true;
//异常情况处理
      call = rawCall;
      failure = creationFailure;
      if (call == null && failure == null) {
        try {
          call = rawCall = createRawCall();
        } catch (Throwable t) {
          failure = creationFailure = t;
        }
      }
    }
//异常情况处理
    if (failure != null) {
      callback.onFailure(this, failure);
      return;
    }
//被取消了请求
    if (canceled) {
      call.cancel();
    }
//调用OkHttp的call的方法进行请求数据
    call.enqueue(new okhttp3.Callback() {
      @Override public void onResponse(okhttp3.Call call, okhttp3.Response rawResponse)
          throws IOException {
        Response<T> response;
        try {
          response = parseResponse(rawResponse);
        } catch (Throwable e) {
          callFailure(e);
          return;
        }
        callSuccess(response);
      }

      @Override public void onFailure(okhttp3.Call call, IOException e) {
        try {
          callback.onFailure(OkHttpCall.this, e);
        } catch (Throwable t) {
          t.printStackTrace();
        }
      }

      private void callFailure(Throwable e) {
        try {
          callback.onFailure(OkHttpCall.this, e);
        } catch (Throwable t) {
          t.printStackTrace();
        }
      }

      private void callSuccess(Response<T> response) {
        try {
          callback.onResponse(OkHttpCall.this, response);
        } catch (Throwable t) {
          t.printStackTrace();
        }
      }
    });
  }

同同步请求一致,都是先获取到OkHttp的实例,然后分别调用OkHttp的实例对应的异步请求的方法来完成请求。

总结

OkHttpCall在Retrofit中其实就是采用装饰模式对OkHttp的进行包装:

1)OkHttpClient是在通过Builder来创建Retrofit实例的时候,在build()方法中进行实例化,设置到Retrofit的默认的callFactory;

2)Request的创建在于我们调用OkHttpCall的enqueue()或者execute()的方法里面,创建OkHttp对象的时候,通过serviceMethod的toRequest(),将定义的网络接口类转换成OkHttp的Request对象。同时通过调用callFactory.newCall(),实际上就是调用了OkHttpClient的newCall()来创建出call实例

3)最后我们就通过OkHttpCall对外提供的同步或者异步的方法来进行调用OkHttp对应的方法。

后面要在分析下OkHttp的源码,学习源码就是去学习别人的一些设计思想。

猜你喜欢

转载自blog.csdn.net/nihaomabmt/article/details/88056228