站在Stay老司机肩膀上分析Retrofit

站在Stay老司机肩膀上分析Retrofit

几个月前,第一次知道Retrofit是看了Stay的文章:Retrofit分析-漂亮的解耦套路文章看起来高大上的感觉,结果撸了一遍下来,好像很多都是一知半解。虽然老司机开车很稳,但是可能自己内功不足,沿途风景没能好好的欣赏。

现在再看回代码,感觉一下子就明白了。所以自己也打算撸一撸它的流程,自己做一回司机了,新手上路,请坐好扶稳哦(>>>)

首先引用一张Stay画的整体流程图(本来想自己画的,但是看了这张图,感觉自己也画不出更好的了)
这里写图片描述

Retrofit使用的大量的设计模式,先看看我的代码:

 public static ApiService getDefault() {
        if (SERVICE == null) {
            HttpLoggingInterceptor logging = new HttpLoggingInterceptor();
            logging.setLevel(HttpLoggingInterceptor.Level.BODY);
            OkHttpClient client = new OkHttpClient.Builder().addInterceptor(logging).build();
            SERVICE = new Retrofit.Builder()
                    .baseUrl(Constants.BASE_URL)
                    .addConverterFactory(GsonConverterFactory.create())
                    .addCallAdapterFactory(RxJavaCallAdapterFactory.create())
                    .client(client)
                    .build().create(ApiService.class);

        }
        return SERVICE;
    }

很简单,通过Retrofit的Builder创建了一个Service对象,其中配置了一些参数。这里的Retrofit的构造的方式使用了Builder模式。

这里写图片描述

然后我们点开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, 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 serviceMethod = loadServiceMethod(method);
            OkHttpCall okHttpCall = new OkHttpCall<>(serviceMethod, args);
            return serviceMethod.callAdapter.adapt(okHttpCall);
          }
        });
  }

第一行的 Utils.validateServiceInterface(service) 作用是验证参数service是一个Interface.

结果是返回一个动态代理对象,使用了代理模式。这就意味着你调用service的每一个方法都会被拦截(具体就是你自己声明的API接口)

上面分析的过程图解:
这里写图片描述

在拦截的时候会进行下列一些处理:

  • 加载API中的方法,解析注解。创建CallAdapter、Converter
  • new一个OkHttpCall,关于Okhttp的所有参数都会在里面完成
  • 用CallApater adapt OkHttpCall。

这里的步骤对应着下面几行代码:

 ServiceMethod serviceMethod = loadServiceMethod(method);
 OkHttpCall okHttpCall = new OkHttpCall<>(serviceMethod, args);
 return serviceMethod.callAdapter.adapt(okHttpCall);

首先看第一行,点进去loadSeerviceMethod(method):

ServiceMethod loadServiceMethod(Method method) {
    ServiceMethod result;
    synchronized (serviceMethodCache) {
      result = serviceMethodCache.get(method);
      if (result == null) {
        result = new ServiceMethod.Builder(this, method).build();
        serviceMethodCache.put(method, result);
      }
    }
    return result;
  }

这里其实比较简单,就是首先从缓存里面读取ServiceMethod对象,如果没有,就通过Builder模式创建出一个ServiceMethod对象。

接下来我们点进去ServiceMethod的build()方法看看,究竟做了什么:

 public ServiceMethod build() {
      callAdapter = createCallAdapter();
      responseType = callAdapter.responseType();
       ...
       //一系列判断
      responseConverter = createResponseConverter();
      for (Annotation annotation : methodAnnotations) {
        parseMethodAnnotation(annotation);
      }

     ...
     //一系列判断
      return new ServiceMethod<>(this);
    }

上面的代码我做了简化,其实里面就做了几件事:

首先创建callAdapter,经过我查询源码createdCallAdapter最终返回的就是我们在创建Retrofit对象时addCallAdapterFactory传进去的CallAdapter,接着会创建responseConverter,这个同样是我们创建Retrofit对象时添加的Converter,然后会通过parseMethodAnnotation解析方法上面的注解。

上面分析的过程图解:

这里写图片描述

接下来解释创建OkhttpCall了,这个没什么好解释的,创建一个对象:

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

接着就用CallAdapter来适配OkHttpCall了。

return serviceMethod.callAdapter.adapt(okHttpCall);

这里的CallAdapter使用了适配器模式,他会把OkHttpCall返回的结果转换成我们声明的接口的返回值,说到这里我就要解释一下几个CallAdapter,这个CallAdapter实际上是什么东西呢?

上面说过serviceMethod.callAdapter是通过createCallAdapter()得到的,然后最终的值就是我们自己声明的,看回最前面的例子,所用的CallAdapter就是RxJavaCallAdapterFactory.create(),实际上用了Rx的CallAdapter。既然是是策略模式,当然有几种策略可供选择啦。

CallAdapter在Retrofit中提供了四种,分别为:RxJavaCallAdapterFactory、Java8CallAdapterFactory、GuavaCallAdapterFactory、AndroidCallAdapterFactory。

前面的例子使用的是RxJavaCallAdapterFactory。所以我们打开RxJavaCallAdapterFactory的源码看看:

private CallAdapter<Observable<?>> getCallAdapter(Type returnType, Scheduler scheduler) {
    Type observableType = getParameterUpperBound(0, (ParameterizedType) returnType);
    Class<?> rawObservableType = getRawType(observableType);
    if (rawObservableType == Response.class) {
      if (!(observableType instanceof ParameterizedType)) {
        throw new IllegalStateException("Response must be parameterized"
            + " as Response<Foo> or Response<? extends Foo>");
      }
      Type responseType = getParameterUpperBound(0, (ParameterizedType) observableType);
      return new ResponseCallAdapter(responseType, scheduler);
    }

    if (rawObservableType == Result.class) {
      if (!(observableType instanceof ParameterizedType)) {
        throw new IllegalStateException("Result must be parameterized"
            + " as Result<Foo> or Result<? extends Foo>");
      }
      Type responseType = getParameterUpperBound(0, (ParameterizedType) observableType);
      return new ResultCallAdapter(responseType, scheduler);
    }

    return new SimpleCallAdapter(observableType, scheduler);
  }

从上面代码看来,实际上会根据Method不同的返回值类型来返回不同的CallAdapter,比如返回值是Response的话就会返回ResponseCallAdapter,返回值是Result就会返回ResultCallAdapter,返回值是普通Object的话就回返回SimpleCallAdapter。

这三个CallAdapter最后都会调用adapt方法,adapt方法的作用就是把执行请求并且把结果发射出去:

//adapt方法
 @Override public <R> Observable<Response<R>> adapt(Call<R> call) {
      Observable<Response<R>> observable = Observable.create(new CallOnSubscribe<>(call));
      if (scheduler != null) {
        return observable.subscribeOn(scheduler);
      }
      return observable;
    }


    //CallOnSubscribe -> RequestArbiter
    @Override 
    public void request(long n) {
      if (n < 0) throw new IllegalArgumentException("n < 0: " + n);
      if (n == 0) return; // Nothing to do when requesting 0.
      if (!compareAndSet(false, true)) return; // Request was already triggered.

      try {
        Response<T> response = call.execute();
        if (!subscriber.isUnsubscribed()) {
          subscriber.onNext(response);
        }
      } catch (Throwable t) {
        Exceptions.throwIfFatal(t);
        if (!subscriber.isUnsubscribed()) {
          subscriber.onError(t);
        }
        return;
      }

      if (!subscriber.isUnsubscribed()) {
        subscriber.onCompleted();
      }
    }

最终会调用了call.execute();这个call其实就是我们上面在动态代理new出来的OkHttpCall,然后OkHttpCall的execute()方法:

@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 = rawCall;
      if (call == null) {
        try {
          call = rawCall = createRawCall();
        } catch (IOException | RuntimeException e) {
          creationFailure = e;
          throw e;
        }
      }
    }

    if (canceled) {
      call.cancel();
    }

    return parseResponse(call.execute());
  }

最后调用了parseResponse()方法,最终执行:

T body = serviceMethod.toResponse(catchingBody);


//ServiceMethod:
T toResponse(ResponseBody body) throws IOException {
    return responseConverter.convert(body);
  }

其实就是调用我们的GsonConverter来convert把数据转换成对象而已。

好了这样的一个过程就分析完毕了。

上面分析的过程图解:

这里写图片描述

最后我们分析一下Retrofit的build方法:

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

      okhttp3.Call.Factory callFactory = this.callFactory;
      if (callFactory == null) {
        callFactory = new OkHttpClient();
      }

      Executor callbackExecutor = this.callbackExecutor;
      if (callbackExecutor == null) {
        callbackExecutor = platform.defaultCallbackExecutor();
      }

      // Make a defensive copy of the adapters and add the default Call adapter.
      List<CallAdapter.Factory> adapterFactories = new ArrayList<>(this.adapterFactories);
      adapterFactories.add(platform.defaultCallAdapterFactory(callbackExecutor));

      // Make a defensive copy of the converters.
      List<Converter.Factory> converterFactories = new ArrayList<>(this.converterFactories);

      return new Retrofit(callFactory, baseUrl, converterFactories, adapterFactories,
          callbackExecutor, validateEagerly);
    }
  }

可以看到callFactroy为null的情况下会创建一个OkHttpClient(),所以没有指定client的情况下会使用okhttpclient请求网络。如果我们没有指定CallAdapter的话,也会默认生成一个

总结

上面的总结可能有些地方混乱,因为我是结合自己的代码例子来分析整个过程的,而且对于上图右边的其他一些GuavaCallAdapterFactory、Java8…等等都没有分析,不过大体上的套路就是上面这样分析,我因为是结合例子分析,使用的是RxJava,自然就只分析到RxJavaCallAdapterFactory了,整体的流程大概就拎出来了。

分析完了,还要思考一下作者为什么要做,说解耦大家都知道! 可能还是内功不够,所以思考不出什么来呀,暂时不想它了吧。嗯,就这样了。

年轻的司机开车真是每一次都这么激动。

最后我在分析代码的时候,发现Stay的图有一点点画的不太对,我在下图把图片标注出来:

这里写图片描述

1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看REaDME.md或论文文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。 5、资源来自互联网采集,如有侵权,私聊博主删除。 6、可私信博主看论文后选择购买源代码。 1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md或论文文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。 5、资源来自互联网采集,如有侵权,私聊博主删除。 6、可私信博主看论文后选择购买源代码。 1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md或论文文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。 5、资源来自互联网采集,如有侵权,私聊博主删除。 6、可私信博主看论文后选择购买源代码。
1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md或论文文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。 5、资源来自互联网采集,如有侵权,私聊博主删除。 6、可私信博主看论文后选择购买源代码。 1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md或论文文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。 5、资源来自互联网采集,如有侵权,私聊博主删除。 6、可私信博主看论文后选择购买源代码。 、资源1项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md或论文文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。 5、资源来自互联网采集,如有侵权,私聊博主删除。 6、可私信博主看论文后选择购买源代码。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值