美文网首页
Retrofit源码解析(二)

Retrofit源码解析(二)

作者: andcoder | 来源:发表于2017-11-21 21:01 被阅读104次
    • 适配器模式

      上一篇文章我们已经分析了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的设计有所帮助,谢谢!

    相关文章

      网友评论

          本文标题:Retrofit源码解析(二)

          本文链接:https://www.haomeiwen.com/subject/mphgvxtx.html