retrofit框架探究(二)


话前:

1 如果有时间可以看一看retrofit2的jack本尊演讲视频,可能有人连retrofit 1都还没用过,但实际上基本不影响,其主要的框架设计并没有发生变化,等熟悉了1我们再看2会更加事半功倍。

2 对于retrofit的背景我稍稍提一下,它其实在2010年就开始着手开发了,而当时的大当家是鼎鼎大名的crazy bob,即guice的作者,后来由square接管。dagger的命运也是如此的相似。

3 关于retrofit与okhttp,两者都是square出品。okhttp实际上充当的是通信员的角色,而retrofit是支持替换okhttp这个通信员的,urlconnection,okhttp,protobuf都是候选通信员,这种插件式的设计的确很良心啊。用一张图可以描述开发者,retrofit以及okhttp之间的关系:


retrofit框架探究(二)_第1张图片


可以看出,retrofit在整个流程当中的作用,我们可以这样理解,myservice是我们自己的系统,主要与我们的app交互,converter是一个系统,主要负责数据的转换,okhttp是一个系统,主要负责socket连接,而这中间负责协调工作的就是retrofit这个系统,它可以与任意一个系统交互,在我看来这是典型的门面模式,这里面除了retrofit不能替换,其余的系统都是可以替换的,像插件一样。

4 示例:

public interface CharactersApi {
  @GET("/characters") Observable> getCharacters(
      @Query("offset") int offset);
}


这个接口是自己定义的请求接口,大概了解其请求方式,参数,方法名,返回值,接下来,我们转入restadapter类。


1 create方法

public  T create(Class service) {
    Utils.validateServiceClass(service);
    return (T) Proxy.newProxyInstance(service.getClassLoader(), new Class[] { service },
        new RestHandler(getMethodInfoCache(service)));
  }

这个方法是我们最常用的方法,因为其使用方法就是api = adapter.create(CharactersApi.class),然后通过对这个api进行注册便可以得到自己想要的observable。那么当我们把它传进去之后,它会首先对我们传入的类进行校验,只能是接口且不能是子接口。接下来,它使用了代理模式生成我们接口的代理类返回供开发者使用,从这点可以看出,作者希望在执行请求的前后做一些其他事情,然而它并没有实现我们的接口,而是将其大卸八块,拼装成了它自己的一个类RestMethodInfo为执行网络请求提供所需的必要信息。那么我不仅发出疑问既然如此,为什么非要是接口呢?从设计者的角度来讲,我写的是个网络框架,你调用我的主要目的就是网络请求并得到结果,那么我只需要了解你想要怎样的请求就可以了,然后我帮你调用,拿到结果返回给你便是。以其他形式将请求路径,参数,请求方式等等信息告知框架,然后框架内部执行请求,返回一个结果,我想应该也是可以的,因为很多框架都是这样做的,比如android-async-http就是如此。那么这便是retrofit的最大特色之一,开发者利用接口配合注解对需求作出定义,将这份定义传入框架内部识别,识别成功执行然后返回结果,所以相对于其他框架而言它对于这份定义有自己的规范,这套规范就是上面的CharactersApi接口的样子。

在最开始使用retrofit的时候,我很不适应这样的规范,适应了之后,反而爱不释手,在实际开发过程中,修改代码是一件繁琐但很常见的事情,但修改网络请求却从未感到如此的轻松,因为通过定义的接口,可以很直观的看到这个接口的几乎所有信息,参数也好返回值也好都很容易看到并修改到。另外它也很类似于过去得自己动手写的网络调用provider,只不过我们现在不用关心其具体实现了。如果从逻辑上来说,开发者给框架一大堆参数,倒不如双方定义一套规则能够互相都能读懂来得方便,更何况,使用接口来作为规则的定义岂不好呢。

2 缓存

private final Map, Map> serviceMethodInfoCache = new LinkedHashMap, Map>();
...

Map getMethodInfoCache(Class service) {
    synchronized (serviceMethodInfoCache) {
      Map methodInfoCache = serviceMethodInfoCache.get(service);
      if (methodInfoCache == null) {
        methodInfoCache = new LinkedHashMap();
        serviceMethodInfoCache.put(service, methodInfoCache);
      }
      return methodInfoCache;
    }
  }

  static RestMethodInfo getMethodInfo(Map cache, Method method) {
    synchronized (cache) {
      RestMethodInfo methodInfo = cache.get(method);
      if (methodInfo == null) {
        methodInfo = new RestMethodInfo(method);
        cache.put(method, methodInfo);
      }
      return methodInfo;
    }
  }

对缓存有必要提一下,因为在最初的实际开发当中,我们对代理接口的引用还是谨慎的作了缓存,但后来发现它内部其实已经作了缓存了,其缓存其实是两层,一层是对我们的接口类做缓存,因为我们有可能会根据功能模块来划分请求接口,另一层就是在当前接口类下的多个请求方法。


3 invoke


@SuppressWarnings("unchecked") //
    @Override public Object invoke(Object proxy, Method method, final 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);
      }

      // Load or create the details cache for the current method.
      final RestMethodInfo methodInfo = getMethodInfo(methodDetailsCache, method);

      if (methodInfo.isSynchronous) {
        try {
          return invokeRequest(requestInterceptor, methodInfo, args);
        } catch (RetrofitError error) {
          Throwable newError = errorHandler.handleError(error);
          if (newError == null) {
            throw new IllegalStateException("Error handler returned null for wrapped exception.",
                error);
          }
          throw newError;
        }
      }

      if (httpExecutor == null || callbackExecutor == null) {
        throw new IllegalStateException("Asynchronous invocation requires calling setExecutors.");
      }

      if (methodInfo.isObservable) {
        if (rxSupport == null) {
          if (Platform.HAS_RX_JAVA) {
            rxSupport = new RxSupport(httpExecutor, errorHandler, requestInterceptor);
          } else {
            throw new IllegalStateException("Observable method found but no RxJava on classpath.");
          }
        }
        return rxSupport.createRequestObservable(new RxSupport.Invoker() {
          @Override public ResponseWrapper invoke(RequestInterceptor requestInterceptor) {
            return (ResponseWrapper) invokeRequest(requestInterceptor, methodInfo, args);
          }
        });
      }

      // Apply the interceptor synchronously, recording the interception so we can replay it later.
      // This way we still defer argument serialization to the background thread.
      final RequestInterceptorTape interceptorTape = new RequestInterceptorTape();
      requestInterceptor.intercept(interceptorTape);

      Callback callback = (Callback) args[args.length - 1];
      httpExecutor.execute(new CallbackRunnable(callback, callbackExecutor, errorHandler) {
        @Override public ResponseWrapper obtainResponse() {
          return (ResponseWrapper) invokeRequest(interceptorTape, methodInfo, args);
        }
      });
      return null; // Asynchronous methods should have return type of void.
    }

在我们调用代理请求接口的某个方法时,它会调用invoke方法,可以看出retrofit允许我们在这个接口里定义我们自己使用的方法,除此之外,它完全抛弃了我们的接口,而组装了自己的RestMethodInfo,有几个地方要提。

1 同步异步

上一篇提到过我们定义的接口返回值会对后面有很大影响,可以说retrofit是根据接口是否存在返回值来区分该请求是同步还是异步的。如果返回值不是void则为同步,如果为observable则会走rxjava的流程,否则为void就是异步请求,若为异步请求,则把参数的最后一个对象拿来作为请求返回值的回调。以这种方式来定义规范,着实让人惊叹。

2 RequestInterceptor 拦截器

上面的代码里,我们可以找到requestInterceptor.intercept(interceptorTape),这意味着retrofit将拦截器暴露出来给开发者使用,实际开发中,一般用它来对header和参数作统一处理,比如添加设备信息或者token等等,因为这里只针对header和parameter暴露了几个方法。尽管2.0里这里有很大变化,但对于拦截器的相关类还是有必要进行说明。


下篇继续。


你可能感兴趣的:(android,细节,开源)