超详细Retrofit源码解析(二)

来源:互联网 发布:网络连接错误 编辑:程序博客网 时间:2024/06/05 04:23
  • 适配器模式

    上一篇文章我们已经分析了Retrofit解析注解封装进ServiceMethod的流程,读者在这里要记住,一个ServiceMethod就代表一个interface接口里的方法。相信部分读者已经忘了Retrofit的create流程,这里我们再贴一下这部分代码,然后继续分析下去。

    public <T> T create(final Class<T> service) {    Utils.validateServiceInterface(service);    if (validateEagerly) {      eagerlyValidateMethods(service);    }    return (T) Proxy.newProxyInstance(service.getClassLoader(), new Class<?>[] { service },        new InvocationHandler() {          private final Platform platform = Platform.get();          @Override public Object invoke(Object proxy, Method method, @Nullable Object[] args)              throws Throwable {            // If the method is a method from Object then defer to normal invocation.            if (method.getDeclaringClass() == Object.class) {              return method.invoke(this, args);            }            if (platform.isDefaultMethod(method)) {              return platform.invokeDefaultMethod(method, service, proxy, args);            }            ServiceMethod<Object, Object> serviceMethod =                (ServiceMethod<Object, Object>) loadServiceMethod(method);            OkHttpCall<Object> okHttpCall = new OkHttpCall<>(serviceMethod, args);            return serviceMethod.callAdapter.adapt(okHttpCall);          }        });  }

    ServiceMethod初始化完毕之后,接下来需要创建Call对象。

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

    看到没有?这部分默认使用的是OkHttpCall,并且是写死的,用户无法自行扩展。如果你不想默认使用okhttp,那只能修改这部分代码,重新打包retrofit了。

    构造完okhttpCall对象,接下来该做什么呢?

    有了接口代理对象之后,我们就需要把http请求发送出去了。发送可以使用同步方式与异步方式。同步方式我们好理解,http请求发送出去后程序需要阻塞等待http响应的返回,这样我们自然会自己开辟子线程来执行同步请求等待结果并处理;但异步形式呢?既然是异步发送,http响应的到达一般是通过回调的形式通知的。而异步发送请求在Retrofit里面本身又是运行在子线程的,如果不经过特殊处理,回调自然也是运行在子线程。

    这么说,难道还要用户自行从子线程切换到主线程去处理回调?这也太麻烦了吧?

    想太多!!!Retrofit这么神奇的库,当然早就处理好了这种情况。还记得我们在上一篇文章里面提到的MainThreadExecutor吗?聪明的你肯定已经想起来了。是的,它就是用来执行切换操作的神器。

    既然知道有这个神器存在,接下来该怎么使用呢?默认的okhttpCall对象和MainThreadExecutor是没有任何挂钩的,okhttpCall的异步请求的实现也没有去做任何的切换操作,而我们又需要执行异步操作完毕后可以自行切换到主线程去回调,很显然,okhttpCall不能满足我们的使用场景,该怎么办呢?

    这时候就需要适配器模式来发挥作用了,把不能满足使用情形的Call适配成符合场景的另外一个Call。这个系列的文章开头就已经大致介绍了CallAdapter的作用,但还没有实际看过这个接口内部的构造,我们现在跟进去看看。

    public interface CallAdapter<R, T> {  //返回要将http响应转换到的Java对象类型,如Call<Repo>,则返回类型是Repo  Type responseType();  T adapt(Call<R> call);  abstract class Factory {    public abstract @Nullable CallAdapter<?, ?> get(Type returnType, Annotation[] annotations,        Retrofit retrofit);    protected static Type getParameterUpperBound(int index, ParameterizedType type) {      return Utils.getParameterUpperBound(index, type);    }    protected static Class<?> getRawType(Type type) {      return Utils.getRawType(type);    }  }}

    CallAdapter里面最重要的一个方法是adapt,它的作用是把传递进来的Call对象适配成符合程序需求的其他类型的对象。

    还记得上一篇文章中,在建造者模式这一节中我们分析了默认的适配器工厂的赋值吗?是的,它的默认值是ExecutorCallAdapterFactory(忘记的读者请自行异步上一篇文章回顾哦)。我们再重新温习一下ExecutorCallAdapterFactory。

    final class ExecutorCallAdapterFactory extends CallAdapter.Factory {  final Executor callbackExecutor;  ExecutorCallAdapterFactory(Executor callbackExecutor) {    this.callbackExecutor = callbackExecutor;  }  @Override  public CallAdapter<?, ?> get(Type returnType, Annotation[] annotations, Retrofit retrofit) {    if (getRawType(returnType) != Call.class) {      return null;    }    final Type responseType = Utils.getCallResponseType(returnType);    return new CallAdapter<Object, Call<?>>() {      @Override public Type responseType() {        return responseType;      }      @Override public Call<Object> adapt(Call<Object> call) {        return new ExecutorCallbackCall<>(callbackExecutor, call);      }    };  }  }

    ExecutorCallAdapterFactory的get方法是获取CallAdapter的入口,可以看到,get内部返回了一个匿名的callAdapter对象,实现了responseType和adapt两个方法。

    因此,Retrofit.create的serviceMethod.callAdapter.adapt(okHttpCall)最终调用的是由ExecutorCallAdapterFactory获取到的CallAdapter的adapt方法。okhttpCall最终也被适配成ExecutorCallbackCall返回。这就是Retrofit需要的Call对象。

    这里我们也关注两个点:

    1. 构造ExecutorCallbackCall对象时,传入的callbackExecutor是我们在上一篇文章分析过的MainThreadExecutor;

    2. 构造ExecutorCallbackCall对象时,传入的call是OkhttpCall;

    至于适配的其他平台,如RxJava\Java8\Guava等,我们不做展开,这里主要是梳理分析Retrofit的主要流程源码。若有较多读者有需要,可以反馈给我,后续开专栏补上。


  • 装饰者模式

    创建完代理对象后,接下来就是使用代理对象来发送http请求了。发送分为同步和异步两种,对应Call接口里的execute和enqueue。我们首先分析execute流程,然后再分析enqueue流程,循序渐进。

    经过Retrofit.create方法创建的Call对象实际上是ExecutorCallbackCall对象,因此,同步、异步请求真正调用的是ExecutorCallbackCall的execute和enqueue方法。

    execute

     @Override public Response<T> execute() throws IOException {  return delegate.execute();}

    execute内部的实现非常简单,仅仅是调用了delegate的execute方法。那么delegate是什么呢?顾名思义,它是被ExecutorCallbackCall委托来执行execute操作的。我们再来看看ExecutorCallbackCall对于delegate的赋值。

    static final class ExecutorCallbackCall<T> implements Call<T> {final Executor callbackExecutor;final Call<T> delegate;ExecutorCallbackCall(Executor callbackExecutor, Call<T> delegate) {  this.callbackExecutor = callbackExecutor;  this.delegate = delegate;}...}

    有没有似曾相识的感觉?是的,我们在上文的分析中,曾经划重点指出了ExecutorCallbackCall在构造时的默认赋值,其中,callbackExecutor的默认赋值是MainThreadExecutor,delegate的默认赋值是OkhttpCall。也就是说,实际的发起同步请求的操作还是由OkhttpCall来发起的。

    将某个值通过依赖注入的形式在类内部持有其引用,之后再在类对应的方法里调用该引用的方法(前后可增加一些操作),这种模式就是装饰者模式。

    好了,现在定位到OkhttpCall的execute方法。

    @Override public Response<T> execute() throws IOException {okhttp3.Call call;synchronized (this) {  if (executed) throw new IllegalStateException("Already executed.");  executed = true;  call = rawCall;  if (call == null) {    try {      call = rawCall = createRawCall();    } catch (IOException | RuntimeException | Error e) {      creationFailure = e;      throw e;    }  }}if (canceled) {  call.cancel();}return parseResponse(call.execute());}

    Retrofit的一个请求对象只能执行一次,因此execute一开始会进行判断。通过判断之后,会对call对象赋值rawCall。rawCall一开始显然是null,所以会执行到createRawCall()来创建call对象。

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

    Retrofit在创建ServiceMethod对象时,已经将请求的注解信息都封装进去了,这里直接调用ServiceMethod.toRequest就可以把一个接口方法转换成Request请求对象了。我们来看看ServiceMethod是如何将一个接口方法转换成Request对象的。

      Request toRequest(@Nullable Object... args) throws IOException {RequestBuilder requestBuilder = new RequestBuilder(httpMethod, baseUrl, relativeUrl, headers,    contentType, hasBody, isFormEncoded, isMultipart);ParameterHandler<Object>[] handlers = (ParameterHandler<Object>[]) parameterHandlers;int argumentCount = args != null ? args.length : 0;for (int p = 0; p < argumentCount; p++) {  handlers[p].apply(requestBuilder, args[p]);}return requestBuilder.build();}

    toRequest内部使用了RequestBuilder来封装解析的注解信息,并对每个参数注解调用其处理器的apply方法进行转换解析,最后调用RequestBuilder.build方法构造出一个Request对象。RequestBuilder就是一个Request建造器,其内部源码不复杂,读者也请自行翻阅,这里不做展开,我们的解读目标依然是Retrofit主线。

    创建出了Request对象之后,Request对象如何传输到网络上,还需要Call来实现。接下来createRawCall会执行serviceMethod.callFactory.newCall(request)。我们在上一篇文章中已经提到过,serviceMethod.callFactory实际上就是OkhttpClient,因此实际构造Call的操作是交由Okhttp来完成的(Okhtpp实际创建的是RealCall对象),本系列文章主要是探索Retrofit的源码实现,Okhttp部分的源码我们就不做深入分析了,读者有兴趣可以自行去阅读。

    到此,Call对象就已经创建出来了,其本质是Okhttp的RealCall对象,这也直接说明了Retrofit本身并不负责网络请求,而是将该功能交由okhttp来执行,很好地解除了依赖。

    Call对象创建完毕,剩下的就是以下步骤了。

    parseResponse(call.execute())

    call.execute实际就是调用Okhttp.RealCall.execute了。那么RealCall里面的execute到底做了什么呢?这里我们小小地涉足一下,浅尝辄止。

    Okhtpp.RealCall @Override public Response execute() throws IOException {synchronized (this) {  if (executed) throw new IllegalStateException("Already Executed");  executed = true;}try {  client.dispatcher().executed(this);  Response result = getResponseWithInterceptorChain(false);  if (result == null) throw new IOException("Canceled");  return result;} finally {  client.dispatcher().finished(this);}}

    相信细心的读者已经发现了。

    Response result = getResponseWithInterceptorChain(false);

    RealCall是通过上述getResponseWithInterceptorChain方法获取到了http响应的。Okhttp发起网络请求和解析网络响应的核心正是使用了责任链模式实现的,这部分源码太多,这里不做展开,读者可自行搜索相关文章查看。

    好了,现在让我们回到Retrofit。

    call.execute执行完毕以后,程序会获取到Okhttp.Response类型的http响应。而Okhttp.Response在Retrofit中是无法被识别的,因此需要将其转换成REtrofit可以识别的对象,parseResponse()就是起这个作用的。

     Response<T> parseResponse(okhttp3.Response rawResponse) throws IOException {ResponseBody rawBody = rawResponse.body();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) {  rawBody.close();  return Response.success(null, rawResponse);}ExceptionCatchingRequestBody catchingBody = new ExceptionCatchingRequestBody(rawBody);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;}}

    parseResponse解析出rawBody之后,调用了ServiceMethod.toResponse方法,将rawBody转换成对应的JavaBean对象。

    R toResponse(ResponseBody body) throws IOException {return responseConverter.convert(body);}

    可以看到,ServiceMethod的toResponse方法很简单,就是调用事先已经注册好的responseConverter来将rawBody对象convert成我们事先定义好的JavaBean。那么responseConverter的值各位读者还记得吗?对了,它就是我们一开始的时候注册进去的GsonConverterFactory.GsonResponseBodyConverter。

    经过以上流程,execute方法就执行完毕了。


    enqueue

    分析完同步请求execute的流程,现在我们来分析异步请求enqueue。

    call.enqueue调用的还是ExecutorCallbackCall的enqueue方法。

    ExecutorCallbackCall @Override public void enqueue(final Callback<T> callback) {  delegate.enqueue(new Callback<T>() {    @Override public void onResponse(Call<T> call, final Response<T> response) {      callbackExecutor.execute(new Runnable() {        @Override public void run() {          if (delegate.isCanceled()) {            callback.onFailure(ExecutorCallbackCall.this, new IOException("Canceled"));          } else {            callback.onResponse(ExecutorCallbackCall.this, response);          }        }      });    }    @Override public void onFailure(Call<T> call, final Throwable t) {      callbackExecutor.execute(new Runnable() {        @Override public void run() {          callback.onFailure(ExecutorCallbackCall.this, t);        }      });    }  });}

    这里的enqueue依然是交由OkhttpCall的enqueue方法来执行。我们来看看OkhttpCall的enqueue方法。

    OkhttpCall @Override public void enqueue(final Callback<T> callback) {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();}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);  }

    与execute方法的执行流程相似,首先还是会调用createRawCall()创建一个Okhttp.RealCall对象,再调用该对象的enqueue方法发起真正的http异步请求。

    OkhttpCall.enqueue方法的参数callBack是从ExecutorCallbackCall的enqueue方法传入的。当请求成功获取到响应时,OkhttpCall会执行和execute同样的解析操作:调用parseResponse,在parseResponse里面调用ServiceMethod.toResponse方法,将Okhttp.Response转换成JavaBean,最终再经由Retrofit.Response.success进行封装返回Retrofit.Response对象。

    无论成功或者失败,OkhttpCall都会通过callBack将结果进行回调。这里的回调执行的是ExecutorCallbackCall传递的callBack。

    我们再来回顾一下ExecutorCallbackCall的回调吧。

    new Callback<T>() {        @Override public void onResponse(Call<T> call, final Response<T> response) {          callbackExecutor.execute(new Runnable() {            @Override public void run() {              if (delegate.isCanceled()) {                callback.onFailure(ExecutorCallbackCall.this, new IOException("Canceled"));              } else {                callback.onResponse(ExecutorCallbackCall.this, response);              }            }          });        }        @Override public void onFailure(Call<T> call, final Throwable t) {          callbackExecutor.execute(new Runnable() {            @Override public void run() {              callback.onFailure(ExecutorCallbackCall.this, t);            }          });        }      }

    可以看到,在onResponse和onFailure里面,真正执行回调的是callbackExecutor!!!

    callbackExecutor.execute(...)

    各位,各位,这里的callbackExecutor对象还记得吗???

    它就是我们反复强调的MainThreadExecutor了!!!

    从这里开始,异步请求的回调就顺利地从子线程切换到了主线程。


    跋山涉水,一番探索之后,我们终于把Retrofit的源码捋顺了。希望这两篇文章对各位读者理解Retrofit的设计有所帮助,谢谢!

原创粉丝点击