时间: 2020-08-30|tag: 43次围观|0 条评论

  今天我来记录一下我对Retrofit框架的理解。不得不说,Retrofit的架构设计极其优秀,既保证了使用上的方便,又高度解耦。同时Retrofit又是学习设计模式的不二典范,里面使用的设计模式数不胜数。今天我们来详细的剖析这个框架。
  本文参考资料:

  1. 这是一份很详细的 Retrofit 2.0 使用教程(含实例讲解 )
  2. Android:手把手带你深入剖析 Retrofit 2.0 源码

  由于Retrofit里面涉及到RxJava和Gson相关知识,所以在阅读本文之前,最好是对RxJava原理有一定的了解。至于Gson,只要会用就行,其实我也不太了解Gson的原理?。

1. 先逼逼几句

  众所周知,Retrofit是基于OkHttp设计的一个网络请求框架。其实说Retrofit是一个网络不是那么准确,准确来说,Retrofit就是OkHttp的一个外壳,Retrofit的底层网络请求使用就是OkHttp。
  同时,Retrofit完全兼容RxJava,这使得它的使用范围进一步扩大,因为这个特性,进一步促进演变近几年比较火的Android架构框架 - RxJava + Retrofit + MVP
  本文打算从Retrofit的基本使用开始,深入分析Retrofit的整个工作流程,包括线程调度、与RxJava兼容等等。

2. Retrofit组成部分

  Retrofit分为4个部分,我们来看看。

名字 含义
自定义的请求接口 主要定义一些网络请求基本参数,比如请求方式(get或者post),表单等等
由请求接口生成OkHttp的Request 根据自定义的请求接口,生成对应的可以执行的Request,此过程只要在ServiceMethod里面完成
网络请求执行器-Call 用来执行生成的Request,在Retrofit里面,使用了装饰者模式,OkHttpCall只是外壳,内部的Call才是真正执行Request
数据转换器-Converter 主要是将返回的Response解析成为我们想要的Java类对象

  提一句,Retrofit是适合于Restful API的网络请求框架,如果想要让Retrofit支持非Restful API,可以参考这一篇文章:如何使用Retrofit请求非Restful API(悄悄的给你们说一句,这个问题,我在腾讯面试被问过?)
  我们先大体看看Retrofit怎么通过这四个部分来进行工作。

Android 主流框架源码分析 – Retrofit源码分析插图

3. 基本使用

  本文简单介绍一下Retrofit的基本使用,让大家有一个印象。但是毕竟不是初级文章,不会在方面花很多的时间。
  首先需要定义一个接口。

public interface Service {  @POST("getSongPoetry")  @FormUrlEncoded  Observable<Bean> getCall(@Field("page") int page, @Field("count") int count);}

  然后创建一个Retrofit的对象。

  private Retrofit mRetrofit = new Retrofit.Builder()    .baseUrl("https://api.apiopen.top/")    .addCallAdapterFactory(RxJava2CallAdapterFactory.create())    .addConverterFactory(GsonConverterFactory.create())    .build();

  然后通过Retrofit的对象获得接口的代理对象。

    Service service = mRetrofit.create(Service.class);

  最后调用对应的接口来请求数据。

    service.getCall(1, 20)      .observeOn(Schedulers.newThread())      .subscribeOn(AndroidSchedulers.mainThread())      .subscribe(new Observer<Bean>() {        @Override        public void onSubscribe(Disposable d) {        }        @Override        public void onNext(Bean bean) {        }        @Override        public void onError(Throwable e) {        }        @Override        public void onComplete() {        }      });

  整个Retrofit的网络请求过程就是这样,是不是非常的简单?这里面我们根本不需要考虑线程调度的问题,以及数据解析的问题,Retrofit已经给我们做了。

4. Retrofit

  从现在开始,我们正式开始分析Retrofit整个工作流程。我们先来看看Retrofit对象的创建。

  private Retrofit mRetrofit = new Retrofit.Builder()    .baseUrl("https://api.apiopen.top/")    .addCallAdapterFactory(RxJava2CallAdapterFactory.create())    .addConverterFactory(GsonConverterFactory.create())    .build();

  从这里,我们已经可以看到两个设计模式--建造者模式工厂模式。当然我们也没必要紧张,就是两个的设计模式而已。
  这里通过调用Retrofit一个内部类Builder一系列的方法,进而初始化了Retrofit一些必须的对象。

(1).Builder

  我们先来看看Builder类,我们先从它的成员变量开始

    private final Platform platform;    private @Nullable okhttp3.Call.Factory callFactory;    private HttpUrl baseUrl;    private final List<Converter.Factory> converterFactories = new ArrayList<>();    private final List<CallAdapter.Factory> callAdapterFactories = new ArrayList<>();    private @Nullable Executor callbackExecutor;    private boolean validateEagerly;
变量名 类型 作用
platform Platform 平台的对象,通过调用Platformget方法,可以获得适合当前环境的平台,包括Java8、Android(似乎Retrofit 2.4.0将其他的平台移除了)。Platform可以通过调用defaultCallAdapterFactory方法可以获得网络执行适配器的工厂类,调用defaultCallbackExecutor方法可以获得线程调度的对象
callFactory okhttp3.Call.Factory 网络请求执行器的工厂类,用来创建网络请求执行器的对象
baseUrl HttpUrl 网络请求的URL基地址,完整的URL地址应当是baseUrl + relativeUrl。其中,调用BuilderbaseUrl方法设置的就是基地址,在接口中@Post注解里面的值就是relativeUrl
converterFactories List<Converter.Factory> 数据转换器的数组,当收到Response时,Retrofit会根据接口里面定义的返回类型在converterFactories里面找到合适的数据转换器,从而将Response转换成为正确的类型。
callAdapterFactories List<CallAdapter.Factory> 网络请求执行适配器的数组,跟List<CallAdapter.Factory>一样,会根据接口里面定义的类型来找到合适的适配器。但是二者有所不同,待会会详细的分析。
callbackExecutor Executor 回调线程池,主要是将结果发送到指定的线程,比如,在Android平台上,肯定是将结果发送到主线程。
validateEagerly boolean 是否提前将接口里面的方法解析成为ServiceMethod。至于什么是ServiceMethod,后面会详细的解释。

  上表中详细的解释了Builder每一个成员变量的作用,在Builder类中,每一个成员变量都有一个对应的方法用来赋值。比如说addCallAdapterFactory方法可以往callAdapterFactories添加一个适配器,其他的也是如此。
  接下来我们看看Builderbuild方法。

    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> callAdapterFactories = new ArrayList<>(this.callAdapterFactories);      callAdapterFactories.add(platform.defaultCallAdapterFactory(callbackExecutor));      // Make a defensive copy of the converters.      List<Converter.Factory> converterFactories =          new ArrayList<>(1 + this.converterFactories.size());      // Add the built-in converter factory first. This prevents overriding its behavior but also      // ensures correct behavior when using converters that consume all types.      converterFactories.add(new BuiltInConverters());      converterFactories.addAll(this.converterFactories);      return new Retrofit(callFactory, baseUrl, unmodifiableList(converterFactories),          unmodifiableList(callAdapterFactories), callbackExecutor, validateEagerly);    }

  哟哟,这个build方法里面还做了不少的事情的,我们来看重点。

      if (callFactory == null) {        callFactory = new OkHttpClient();      }

  从这里,我们可以看出来,如果我们设置网络请求执行器,默认就是OkHttpClient,这个OkHttpClientOkHttp是一个非常核心的类,是网络请求必须的类。这里不对OkHttpClient进行展开,毕竟是OKHttp的相关知识。

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

  同理,如果回调线程池默认也为该平台的回调线程池。比如说,如果是Android平台,那么肯定是调用这段代码:

    @Override public Executor defaultCallbackExecutor() {      return new MainThreadExecutor();    }

  从MainThreadExecutor的名字,我们就可以得出结果,肯定是用来回调到主线程的,毕竟在Android中,只有主线程才能更新UI?。
  至于其他的,都是常规的赋值,这里就不详细讲解了。
  在build方法最后一行代码,创建了一个Retrofit的对象。我们来看看Retrofit的构造方法。

  Retrofit(okhttp3.Call.Factory callFactory, HttpUrl baseUrl,      List<Converter.Factory> converterFactories, List<CallAdapter.Factory> callAdapterFactories,      @Nullable Executor callbackExecutor, boolean validateEagerly) {    this.callFactory = callFactory;    this.baseUrl = baseUrl;    this.converterFactories = converterFactories; // Copy+unmodifiable at call site.    this.callAdapterFactories = callAdapterFactories; // Copy+unmodifiable at call site.    this.callbackExecutor = callbackExecutor;    this.validateEagerly = validateEagerly;  }

  其实也没做什么事情,就是将Builder的成员变量对应的赋值给Retrofit

(2).Retrofit的创建为什么要使用建造者模式?

  我们发现,经过一些操作,最终的目的就是创建一个Retrofit对象,然后给Retrofit的成员变量赋值,为什么我们需要多一个Builder,然后绕一大圈来创建一个Retrofit对象呢?
  我首先问一句,在外面写这么一句代码就能创建完整的Retrofit对象,觉得爽不爽?

  private Retrofit mRetrofit = new Retrofit.Builder()    .baseUrl("https://api.apiopen.top/")    .addCallAdapterFactory(RxJava2CallAdapterFactory.create())    .addConverterFactory(GsonConverterFactory.create())    .build();

  不用说,肯定爽,首先外部代码简洁,通过调用对应方法来给变量赋值,链式编程方格6的不行。其次,在Retrofit的构造方法也是非常的简洁,没有各种判断,所有的工作都在Builderbuild方法做了。
  所以,我们从Retrofit里面得到,如果一个对象得创建需要给很多的成员变量,不妨使用建造者模式,这使得你的代码更加的简洁。外部使用起来也是非常的简单。

(3).create方法

  从上面的栗子,我们可以知道,当创建完毕一个Retrofit的对象时,如果想要进行网络请求的话,还必须创建接口的代理对象,也就是通过调用create方法来获得一个对象。
  从我的言语之间,你们应该都知道,create方法肯定使用了代理模式,要不然何来代理对象之称。我们来看看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.adapt(okHttpCall);          }        });  }

  哎呀哎呀,懵逼还是懵逼,没错!对于那些Java功底还不够牢固的同学来说,这个方法单单是看一眼就很懵逼,当初我看这个方法时,也是一脸懵逼。
  不过,懵逼归懵逼。我想说的是,这段代码是整个Retrofit的画龙点睛之笔,特别是其中的三行代码,都可以称之为神来之笔。
  我们先来粗略的看看这段代码。

    Utils.validateServiceInterface(service);    if (validateEagerly) {      eagerlyValidateMethods(service);    }

  首先是validateEagerly相关的。第一行代码是验证当前这个Class对象是否合法。至于是否合法说的太笼统了,具体就是判断当前的Class是否是一个接口,并且没有继承接口。总之,就是验证Class是否一个单纯的接口。
  然后就是eagerlyValidateMethods方法的调用,如果validateEagerly为true的话,那么就调用eagerlyValidateMethods方法。至于eagerlyValidateMethods方法里面干了什么,在这里,先不急着说,等后面咱们分析ServiceMethod类时,咱们再来好好的说道说道?。在这里先提一句,eagerlyValidateMethods方法就是将接口的里面的方法解析成为ServiceMethod对象,这个也是validateEagerly变量的作用,之前也说过。
  看完了简单的几行代码,接下来的代码就是让人彻底懵逼的代码。说到底,其实也没啥,这里就是一个JDK的动态代理,至于不懂JDK代理的同学赶快去学习吧,这个非常的重要。
  闲话少扯,我们来看看invoke方法。前面的几个判断都没有用,在Android平台上根本不会调用,我们直接来看看最后三行代码:

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

  这三行代码,可以被整个Retrofit最核心的代码。表面上来看,只有三行代码,实际上背后给我们做的事情那是数不胜数的。我们来一行一行的看。

            ServiceMethod<Object, Object> serviceMethod =                (ServiceMethod<Object, Object>) loadServiceMethod(method);

  这行代码的目的是将一个普通的Method转换成为ServiceMethod对象。至于怎么转换的话,还有ServiceMethod是什么,这里先不急着说,我只想说ServiceMethodRetrofit里面非常非常核心的一个类。
  我们继续来看第二行代码:

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

  这里比较好理解,就是将ServiceMethod对象包装成为一个OkHttpCall对象,这个OkHttpCall对象使用来执行网络请求的。其实它使用了装饰者模式,本身就是一个壳,这个在后面会详细解释。
  最后,我们来看看最后一行代码:

            return serviceMethod.adapt(okHttpCall);

  这里调用了ServiceMethodadapt方法。这里我们可以看看干了吗。

  T adapt(Call<R> call) {    return callAdapter.adapt(call);  }

  好吧,又调用了callAdapteradapt方法。至于adapt方法到底干了什么,这不进行展开,后面再讲解CallAdapterFactory时,会详细说到。这里,我们可以这么认为,就是通过调用了callAdapter.adapt,如果此时的CallAdapter是RxJava相关的Adapter,那么这里返回就是Observable<>之类的。
  总之,最后一行代码,就是使用适配器用来适配的。

5. ServiceMethod

  现在我们来分析一下这个核心之核心的类。我们还是先从成员变量开始。

  static final String PARAM = "[a-zA-Z][a-zA-Z0-9_-]*";  static final Pattern PARAM_URL_REGEX = Pattern.compile("\\{(" + PARAM + ")\\}");  static final Pattern PARAM_NAME_REGEX = Pattern.compile(PARAM);  private final okhttp3.Call.Factory callFactory;  private final CallAdapter<R, T> callAdapter;  private final HttpUrl baseUrl;  private final Converter<ResponseBody, R> responseConverter;  // --------------------  //网络请求相关参数  private final String httpMethod;  private final String relativeUrl;  private final Headers headers;  private final MediaType contentType;  private final boolean hasBody;  private final boolean isFormEncoded;  private final boolean isMultipart;  // --------------------  private final ParameterHandler<?>[] parameterHandlers;

  哎呀,这么多我们挑几个重要分析。感觉没什么要分析,callFactorycallAdapterresponseConverter这些在都是Retrofit里面的含义都是一样的,只不过Retrofit里面是数组,这里是单个变量,表示从数组选出来的合适的,待会会详细的分析怎么选择。
  唯一值得分析的就是parameterHandlers变量,这个变量是由接口方法的每个参数解析而来的。我们看到,在接口方法里面,每一个参数都会带一些注解,这些信息都是由parameterHandlers变量来保存,并且可以根据每一个参数的特点,生成特定对象供Request的构建。这些我们在后面都会详细讲解。
  现在,我们可以回去Retrofit的eagerlyValidateMethods方法和loadServiceMethod方法。
  我们知道,当validateEagerly变量为true时,会调用eagerlyValidateMethods方法将接口中的每个方法解析成为ServiceMethod。我们先来看看:

  private void eagerlyValidateMethods(Class<?> service) {    Platform platform = Platform.get();    for (Method method : service.getDeclaredMethods()) {      if (!platform.isDefaultMethod(method)) {        loadServiceMethod(method);      }    }  }

  eagerlyValidateMethods方法里面也没有做什么事,具体的操作都在loadServiceMethod方法里面,我们来看看:

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

  嗯,代码非常的简单,但是代码简单不代表容易理解。我们来具体的分析分析。
  首先是判断缓存里面是否有,如果没有的话,就根据条件创建一个ServiceMethod对象,我们看到ServiceMethod对象的创建使用的也是建造者模式。我们先来看看ServiceMethod.Builder的构造方法:

    Builder(Retrofit retrofit, Method method) {      this.retrofit = retrofit;      this.method = method;      this.methodAnnotations = method.getAnnotations();      this.parameterTypes = method.getGenericParameterTypes();      this.parameterAnnotationsArray = method.getParameterAnnotations();    }

  相信熟悉Java的同学,这几行代码应该难不倒你们吧?前两句我就不说了,我来解释后三句。

this.methodAnnotations = method.getAnnotations();

  这行代码是获取当前方法的注解,比如说,我们在上面的getCall方法添加了@POST注解和@FormUrlEncoded注解,这里都可以获得。

this.parameterTypes = method.getGenericParameterTypes();

  这个是获取每个参数的类型。

      this.parameterAnnotationsArray = method.getParameterAnnotations();

  这个是获取每个参数的注解,就比如上面的@Field
  然后,我们再来看看ServiceMethod.Builderbuild方法。

    public ServiceMethod build() {      callAdapter = createCallAdapter();      responseType = callAdapter.responseType();      if (responseType == Response.class || responseType == okhttp3.Response.class) {        throw methodError("'"            + Utils.getRawType(responseType).getName()            + "' is not a valid response body type. Did you mean ResponseBody?");      }      responseConverter = createResponseConverter();      for (Annotation annotation : methodAnnotations) {        parseMethodAnnotation(annotation);      }      if (httpMethod == null) {        throw methodError("HTTP method annotation is required (e.g., @GET, @POST, etc.).");      }      if (!hasBody) {        if (isMultipart) {          throw methodError(              "Multipart can only be specified on HTTP methods with request body (e.g., @POST).");        }        if (isFormEncoded) {          throw methodError("FormUrlEncoded can only be specified on HTTP methods with "              + "request body (e.g., @POST).");        }      }      int parameterCount = parameterAnnotationsArray.length;      parameterHandlers = new ParameterHandler<?>[parameterCount];      for (int p = 0; p < parameterCount; p++) {        Type parameterType = parameterTypes[p];        if (Utils.hasUnresolvableType(parameterType)) {          throw parameterError(p, "Parameter type must not include a type variable or wildcard: %s",              parameterType);        }        Annotation[] parameterAnnotations = parameterAnnotationsArray[p];        if (parameterAnnotations == null) {          throw parameterError(p, "No Retrofit annotation found.");        }        parameterHandlers[p] = parseParameter(p, parameterType, parameterAnnotations);      }      if (relativeUrl == null && !gotUrl) {        throw methodError("Missing either @%s URL or @Url parameter.", httpMethod);      }      if (!isFormEncoded && !isMultipart && !hasBody && gotBody) {        throw methodError("Non-body HTTP method cannot contain @Body.");      }      if (isFormEncoded && !gotField) {        throw methodError("Form-encoded method must contain at least one @Field.");      }      if (isMultipart && !gotPart) {        throw methodError("Multipart method must contain at least one @Part.");      }      return new ServiceMethod<>(this);    }

  build方法相对来说比较长,这里我将它分为4步:

  1. 调用createCallAdapter方法,从Retrofit里面选择合适当前方法的CallAdapter
  2. 调用createResponseConverter方法,从Retrofit里面选择合适当前方法的Convert
  3. 调用parseMethodAnnotation方法,解析接口方法的注解配置。比如说,上面我们在getCall方法设置了@POST@FormUrlEncoded注解,在这一步都会被解析出来。
  4. 调用parseParameter方法将方法的每个参数解析成为ParameterHandler对象。

  我们一步一步的分析。首先来看第一步,也就是createCallAdapter方法的调用。

    private CallAdapter<T, R> createCallAdapter() {      Type returnType = method.getGenericReturnType();      if (Utils.hasUnresolvableType(returnType)) {        throw methodError(            "Method return type must not include a type variable or wildcard: %s", returnType);      }      if (returnType == void.class) {        throw methodError("Service methods cannot return void.");      }      Annotation[] annotations = method.getAnnotations();      try {        //noinspection unchecked        return (CallAdapter<T, R>) retrofit.callAdapter(returnType, annotations);      } catch (RuntimeException e) { // Wide exception range because factories are user code.        throw methodError(e, "Unable to create call adapter for %s", returnType);      }    }

   createCallAdapter表达的意思非常简单,就是根据方法的返回类型和注解类型去Retrofit里面去寻找合适的CallAdapter。这里,我们先来看看RetrofitcallAdapter方法到底做了什么。

  public CallAdapter<?, ?> callAdapter(Type returnType, Annotation[] annotations) {    return nextCallAdapter(null, returnType, annotations);  }

   callAdapter方法也是外壳,实际的工作在nextCallAdapter方法里面做的,我们来看看nextCallAdapter方法:

  public CallAdapter<?, ?> nextCallAdapter(@Nullable CallAdapter.Factory skipPast, Type returnType,      Annotation[] annotations) {    checkNotNull(returnType, "returnType == null");    checkNotNull(annotations, "annotations == null");    int start = callAdapterFactories.indexOf(skipPast) + 1;    for (int i = start, count = callAdapterFactories.size(); i < count; i++) {      CallAdapter<?, ?> adapter = callAdapterFactories.get(i).get(returnType, annotations, this);      if (adapter != null) {        return adapter;      }    }    StringBuilder builder = new StringBuilder("Could not locate call adapter for ")        .append(returnType)        .append(".\n");    if (skipPast != null) {      builder.append("  Skipped:");      for (int i = 0; i < start; i++) {        builder.append("\n   * ").append(callAdapterFactories.get(i).getClass().getName());      }      builder.append('\n');    }    builder.append("  Tried:");    for (int i = start, count = callAdapterFactories.size(); i < count; i++) {      builder.append("\n   * ").append(callAdapterFactories.get(i).getClass().getName());    }    throw new IllegalArgumentException(builder.toString());  }

  写了那么多,归根结底就是一句话,去寻找合适的CallAdapter,如果找不到的话,就抛出异常。这里我们需要特别的注意一行代码:

      CallAdapter<?, ?> adapter = callAdapterFactories.get(i).get(returnType, annotations, this);

  这里的意思就是去callAdapterFactories的每个元素寻找合适的CallAdapter。注意CallAdapter.Factoryget方法,后面我们在分析CallAdapter.Factory会重点的讲解它。
  这里先不对CallAdapter.Factory展开,后面会详细的分析。
  以上就是ServiceMethod.Builder的build方法第一步,也就是寻找合适CallAdapter。我们现在再来看看第二步。
  第二步是调用createResponseConverter方法,寻找合适的数据转换器。其实跟第一步差不多,我们来简单看看。
  createResponseConverter方法最终会调用到RetrofitnextResponseBodyConverter方法。在nextResponseBodyConverter方法里面,我们只需要注意一点:

      Converter<ResponseBody, ?> converter =          converterFactories.get(i).responseBodyConverter(type, annotations, this);

  这里跟第一步的很像,同样的道理,后面我们在分析数据转换器时会详细的分析它。这里先有一个印象就行。
  第三步就是调用parseMethodAnnotation方法来解析方法的注解,这里所做目的有两点:

  1. 获取网络请求的配置参数,用以Request的构建。
  2. 验证注解是否合法。比如说,在上面的栗子中,使用POST请求,同时还每个参数还有@Field注解,所以该方法的注解必须有@FormUrlEncoded,否则会抛出异常。这些都是Retrofit的基本规则而已。

  最后,我们来看最后一步。最后一步整个4步中最复杂,代码量最多的一步。我们来详细分析分析。
  ServiceMethod通过调用parseParameter方法将每个参数解析成为ParameterHandler对象。
  而这个ParameterHandler对象到底是怎么一个东西,接下来我们会通过parseParameter方法来详细的分析。
  通过parseParameter方法的源码,我们可以看到parseParameter方法内部还是调用了parseParameterAnnotation来完成具体的解析工作。由于parseParameterAnnotation方法太特么的长了,这里我们拿一个特例来分析,就上面栗子中的@Field注解来分析:

      } else if (annotation instanceof Field) {        if (!isFormEncoded) {          throw parameterError(p, "@Field parameters can only be used with form encoding.");        }        Field field = (Field) annotation;        String name = field.value();        boolean encoded = field.encoded();        gotField = true;        Class<?> rawParameterType = Utils.getRawType(type);        if (Iterable.class.isAssignableFrom(rawParameterType)) {          if (!(type instanceof ParameterizedType)) {            throw parameterError(p, rawParameterType.getSimpleName()                + " must include generic type (e.g., "                + rawParameterType.getSimpleName()                + "<String>)");          }          ParameterizedType parameterizedType = (ParameterizedType) type;          Type iterableType = Utils.getParameterUpperBound(0, parameterizedType);          Converter<?, String> converter =              retrofit.stringConverter(iterableType, annotations);          return new ParameterHandler.Field<>(name, converter, encoded).iterable();        } else if (rawParameterType.isArray()) {          Class<?> arrayComponentType = boxIfPrimitive(rawParameterType.getComponentType());          Converter<?, String> converter =              retrofit.stringConverter(arrayComponentType, annotations);          return new ParameterHandler.Field<>(name, converter, encoded).array();        } else {          Converter<?, String> converter =              retrofit.stringConverter(type, annotations);          return new ParameterHandler.Field<>(name, converter, encoded);        }

  @Field注解部分还是那么的多,不过我们不用担心,我们只需要关心最后的三个return部分。为了简单起见,我们先分析最后一个return。

          Converter<?, String> converter =              retrofit.stringConverter(type, annotations);          return new ParameterHandler.Field<>(name, converter, encoded);

  其中第一句是去Retrofit里面去寻找合适的StringConverterStringConverter顾名思义,就是将一个对象转换成为String字符串。就比如说,上面的栗子,我们写的是int类型,而在网络请求时,提交表单时,这些数据都是String类型。这个就是StringConverter的作用。
  最后就是创建ParameterHandler.Field对象。我们先来看看这个类。

  static final class Field<T> extends ParameterHandler<T> {    private final String name;    private final Converter<T, String> valueConverter;    private final boolean encoded;    Field(String name, Converter<T, String> valueConverter, boolean encoded) {      this.name = checkNotNull(name, "name == null");      this.valueConverter = valueConverter;      this.encoded = encoded;    }    @Override void apply(RequestBuilder builder, @Nullable T value) throws IOException {      if (value == null) return; // Skip null values.      String fieldValue = valueConverter.convert(value);      if (fieldValue == null) return; // Skip null converted values      builder.addFormField(name, fieldValue, encoded);    }  }

  我们在ParameterHandler时,通常来说只需要关注他的apply,因为后面ServiceMethod在构建Request是通过调用ParameterHandlerapply方法来将一个参数转换成为Request需要的数据。
  Fieldapply方法非常简单,这里就不多说了。
  而其他两个return也是比较容易,一个List数组,一个是普通数组,两者都需要遍历来转换,所以一个需要调用iterable方法,一个需要调用array方法。这里不对这两个方法做过多的解释,有兴趣的可以看看。
  到这里,ServiceMethod的基本分析也差不多,至于ServiceMethod怎么将所有的相关信息转换成为一个Resquest进行网络,怎么调用数据转换器进行数据转换,这些操作在后面我会详细的分析。
  接下来,我们来分析平台适配器--CallAdapter

6. CallAdapter

  大家还记得我们创建Retrofit对象时,通过调用Retrofit.BuilderaddCallAdapterFactory方法添加了平台适配器的工厂类吗?接下来我们会详细的分析CallAdapter.Factory,同时还有它的两个子类ExecutorCallAdapterFactoryRxJava2CallAdapterFactory
  我们先来看看CallAdapter.Factory这个抽象类:

  abstract class Factory {    /**     * Returns a call adapter for interface methods that return {@code returnType}, or null if it     * cannot be handled by this factory.     */    public abstract @Nullable CallAdapter<?, ?> get(Type returnType, Annotation[] annotations,        Retrofit retrofit);    /**     * Extract the upper bound of the generic parameter at {@code index} from {@code type}. For     * example, index 1 of {@code Map<String, ? extends Runnable>} returns {@code Runnable}.     */    protected static Type getParameterUpperBound(int index, ParameterizedType type) {      return Utils.getParameterUpperBound(index, type);    }    /**     * Extract the raw class type from {@code type}. For example, the type representing     * {@code List<? extends Runnable>} returns {@code List.class}.     */    protected static Class<?> getRawType(Type type) {      return Utils.getRawType(type);    }  }

  这只需要关注get方法,至于getParameterUpperBoundgetRawType,我相信大家都非常理解,毕竟注释写的非常清楚。
  我们在RetrofitnextCallAdapter方法里面通过调用CallAdapter.Factoryget方法来获取合适的CallAdapter,怎么判断当前的CallAdapter是否合适呢?就是看这个get方法返回的是否为null。
  具体的我们来看看ExecutorCallAdapterFactoryRxJava2CallAdapterFactory。我们先来看看那RxJava2CallAdapterFactory

(1). RxJava2CallAdapterFactory

  @Override  public CallAdapter<?, ?> get(Type returnType, Annotation[] annotations, Retrofit retrofit) {    Class<?> rawType = getRawType(returnType);    if (rawType == Completable.class) {      // Completable is not parameterized (which is what the rest of this method deals with) so it      // can only be created with a single configuration.      return new RxJava2CallAdapter(Void.class, scheduler, isAsync, false, true, false, false,          false, true);    }    boolean isFlowable = rawType == Flowable.class;    boolean isSingle = rawType == Single.class;    boolean isMaybe = rawType == Maybe.class;    if (rawType != Observable.class && !isFlowable && !isSingle && !isMaybe) {      return null;    }    boolean isResult = false;    boolean isBody = false;    Type responseType;    if (!(returnType instanceof ParameterizedType)) {      String name = isFlowable ? "Flowable"          : isSingle ? "Single"          : isMaybe ? "Maybe" : "Observable";      throw new IllegalStateException(name + " return type must be parameterized"          + " as " + name + "<Foo> or " + name + "<? extends Foo>");    }    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>");      }      responseType = getParameterUpperBound(0, (ParameterizedType) observableType);    } else if (rawObservableType == Result.class) {      if (!(observableType instanceof ParameterizedType)) {        throw new IllegalStateException("Result must be parameterized"            + " as Result<Foo> or Result<? extends Foo>");      }      responseType = getParameterUpperBound(0, (ParameterizedType) observableType);      isResult = true;    } else {      responseType = observableType;      isBody = true;    }    return new RxJava2CallAdapter(responseType, scheduler, isAsync, isResult, isBody, isFlowable,        isSingle, isMaybe, false);  }

  get方法前面一系列判断,就是判断当前接口的方法是否支持RxJava适配。怎么才能表示支持RxJava呢?当然是接口方法返回的类型Observable呢。
  因为我们可以看到这个判断

    if (rawType != Observable.class && !isFlowable && !isSingle && !isMaybe) {      return null;    }

  如果返回类型不是Observable或者Flowable,这里都会返回null,表示当前的方法不支持RxJava。
  如果支持RxJava,最后会返回RxJava2CallAdapter的对象。我们来看看RxJava2CallAdapter,我们在分析RxJava2CallAdapter时,只需要关注adapt方法即可。

  @Override public Object adapt(Call<R> call) {    Observable<Response<R>> responseObservable = isAsync        ? new CallEnqueueObservable<>(call)        : new CallExecuteObservable<>(call);    Observable<?> observable;    if (isResult) {      observable = new ResultObservable<>(responseObservable);    } else if (isBody) {      observable = new BodyObservable<>(responseObservable);    } else {      observable = responseObservable;    }    if (scheduler != null) {      observable = observable.subscribeOn(scheduler);    }    if (isFlowable) {      return observable.toFlowable(BackpressureStrategy.LATEST);    }    if (isSingle) {      return observable.singleOrError();    }    if (isMaybe) {      return observable.singleElement();    }    if (isCompletable) {      return observable.ignoreElements();    }    return observable;  }

  此时跟Retrofitcreate方法就串联起来了。在Retrofitcreate方法里面,最后一行代码就是调用这里的adapt方法。
  在RxJava2CallAdapteradapt方法里面,究竟为我们做了什么?从代码中看来,其实根据情况,给我们创建了一个Observable对象。
  这里,我们还需要注意的是adapt方法的参数是一个Call对象,这个Call对象是什么,用来干嘛的?在Retrofitcreate方法里面,我们可以知道,这里的Call对象就是OkHttpCall对象,这个Call对象肯定是用来进行网络请求的。这个操作,肯定是在Observable里面。这里我们来挑一个Observable类看看。注意,接下来的分析都是跟RxJava相关的,如果不懂RxJava原理的同学,肯定感觉很吃力。
  这里我们就挑相对难一点的CallEnqueueObservable来看看。
  在看Observable的源码时,我们需要关注那个方法呢?当然是subscribeActual方法,因为最终会执行到它来,我们来看看subscribeActual方法:

  @Override protected void subscribeActual(Observer<? super Response<T>> observer) {    // Since Call is a one-shot type, clone it for each new observer.    Call<T> call = originalCall.clone();    CallCallback<T> callback = new CallCallback<>(call, observer);    observer.onSubscribe(callback);    call.enqueue(callback);  }

  整个过程的简单,熟悉RxJava和OkHttp的同学肯定感觉非常亲切,最终进行网络请求的就是最后一行代码:

    call.enqueue(callback);

  由于这里是异步的,所以需要CallBack对象来进行回调。我们来看看CallBackonResponse方法:

    @Override public void onResponse(Call<T> call, Response<T> response) {        // 省略代码        observer.onNext(response);       // 省略代码    }

  我们这里就是将网络请求返回的Response发送给订阅者。
  这个就是整个RxJava2CallAdapter的执行流程。是不是感觉RetrofitRxJava天衣无缝的结合起来了?哈哈,这就是Retrofit的魅力所在。
  至于Callenqueue方法到底做了什么,后面我会详细的分析。
  接下来我们看看ExecutorCallAdapterFactory。相信理解了RxJava2CallAdapterFactory的工作流程,ExecutorCallAdapterFactory就不会很难了。

(2). ExecutorCallAdapterFactory

  为了少说废话,这里直接看ExecutorCallAdapterFactoryget方法:

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

  在这里,我们发现它只会new了一个CallAdapter,同时实现了adapt方法。在adapt方法里面返回了一个ExecutorCallbackCall对象。接下来的ExecutorCallbackCall将Java的静态代理使用的出神入化。
  我先来简单的分析整个ExecutorCallbackCall整个结构。
  首先,如果我们在接口定义这个这种类型方法,最终会调用这个的CallAdapteradapt方法:

  @POST("getSongPoetry")  @FormUrlEncoded  Call<Bean> getCall(@Field("page") int page, @Field("count") int count);

  注意这里返回类型不是Observable,而是Call,这里的Call是什么的对象。我们通过Retrofitcreate方法获取一个代理对象,代理对象调用getCall方法,返回的对象在这里,既不是OKHttpCall,也不是OkHttp里面的Call对象,而是这里ExecutorCallbackCall对象。例如,调用下面的enqueue方法也是调用ExecutorCallbackCallenqueue方法:

    service.getCall(1, 20)      .enqueue(new Callback<Bean>() {        @Override        public void onResponse(Call<Bean> call, Response<Bean> response) {                  }        @Override        public void onFailure(Call<Bean> call, Throwable t) {        }      });

  而ExecutorCallbackCall本身就是一个外壳,在ExecutorCallbackCallenqueue方法里面调用了内部的一个代理对象的enqueue方法。我们来看看:

    @Override public void enqueue(final Callback<T> callback) {      checkNotNull(callback, "callback == null");      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()) {                // Emulate OkHttp's behavior of throwing/delivering an IOException on cancellation.                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);            }          });        }      });    }

  这个delegate是什么呢?它就是OkHttpCall对象。我们发现,在delegateCallback里面使用callbackExecutor将当前的数据调度到主线程去。
  至于为什么调用execute就会把当前的Response发送到主线程去,这里就不详细的解释了,有兴趣的可以看看Platfrom.Android.MainThreadExecutorexecute方法干了什么。
  CallAdapter的部分,我们的分析就到此为止,接下来我们来分析一下OkHttpCall到底做了什么。

7. 网络执行器--Call

  不论是接口返回返回类型为Observable,而在ObservablesubscribeActual方法会调用了Callenqueue方法进行网络请求;还是返回类型为Call,我们直接调用enqueue方法来进行网络请求。我们都会发现,enqueue是整个Call重中之重。
  我们先来看看Call--OkHttpCall。在Retrofit里面,这里的Call就是OkHttpCall
  跟OkHttp里面一样,这里OkHttpCall也分为同步请求和异步请求,分别对应的是execute方法和enqueue方法。我们先来一个一个的来分析。

(1). 同步请求- execute

  我们来看看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 if (creationFailure instanceof RuntimeException) {          throw (RuntimeException) creationFailure;        } else {          throw (Error) creationFailure;        }      }      call = rawCall;      if (call == null) {        try {          call = rawCall = createRawCall();        } catch (IOException | RuntimeException | Error e) {          throwIfFatal(e); //  Do not assign a fatal error to creationFailure.          creationFailure = e;          throw e;        }      }    }    if (canceled) {      call.cancel();    }    return parseResponse(call.execute());  }

  整个execute方法的过程,我将它分为3步:

  1. 创建rawCall,也就是OkHttp里面的call
  2. 调用rawCallexecute方法进行真正的网络请求。
  3. 调用parseResponse方法解析Response。这里面会使用到数据转换器,这个在后面会详细的分析。

  还是按着老规矩来,一步一步的来分析。
  首先是创建rawCall对象。这一步最终是调用了ServiceMethodtoCall方法。

  okhttp3.Call toCall(@Nullable Object... args) throws IOException {    RequestBuilder requestBuilder = new RequestBuilder(httpMethod, baseUrl, relativeUrl, headers,        contentType, hasBody, isFormEncoded, isMultipart);    @SuppressWarnings("unchecked") // It is an error to invoke a method with the wrong arg types.    ParameterHandler<Object>[] handlers = (ParameterHandler<Object>[]) parameterHandlers;    int argumentCount = args != null ? args.length : 0;    if (argumentCount != handlers.length) {      throw new IllegalArgumentException("Argument count (" + argumentCount          + ") doesn't match expected count (" + handlers.length + ")");    }    for (int p = 0; p < argumentCount; p++) {      handlers[p].apply(requestBuilder, args[p]);    }    return callFactory.newCall(requestBuilder.build());  }

  创建rawCall对象的过程跟OkHttp里面创建一个Call是一样的,这里我们只需关注下面的代码就行了。

    for (int p = 0; p < argumentCount; p++) {      handlers[p].apply(requestBuilder, args[p]);    }

  这里调用ParameterHandlerapply方法将每个参数转换成为网络请求真正需要的数据,这里与前面我们分析parseParameter方法不谋而合。
  创建好了rawCall对象,此时就应该调用rawCallexecute方法来进行网络请求。这里的原理就是OkHttp的知识,这里就不在展开了,有兴趣的同学,可以参考我的OkHttp源码分析系列-OkHttp 源码分析系列
  最后就是调用parseResponse方法来进行数据转化。我们来简单看看parseResponse方法

  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) {      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方法里面有很多的判断,这里我们不需要有太多的关注,只需要看下面这一行代码就行了:

      T body = serviceMethod.toResponse(catchingBody);

  通过调用ServiceMethodtoResponse方法将RequestBody转换成为我们需要的Bean对象。我们来看看ServiceMethodtoResponse方法。

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

  这里没有过多的操作,只是简单调用Converterconvert方法来进行数据转换。这里先不对Converter做过多的解释,待会会详细的分析它。
  整个三步执行完了,就是OKHttpCallexecute执行完毕。不得不说Retrofit的代码设计是多么的优秀,整个过程酣畅淋漓,一切都感觉合情合理的。
  接下里,我们来看看异步请求的过程。

(2). 异步请求- enqueue

  异步请求跟同步请求比较起来,只是多了一个Callback参数。而这个Callback就是我们在调用enqueue方法传入的Callback,这里就不对enqueue做过多的解释了。
  接下来,我们分析一下Retrofit最后一个部分Converter--数据转换器

8.数据转换器--Converter

  在创建Retrofit对象时,我们通过调用Retrofit.BuilderaddConverterFactory添加了一个数据转换器。我们来看看代码:

  private Retrofit mRetrofit = new Retrofit.Builder()    .baseUrl("https://api.apiopen.top/")    .addCallAdapterFactory(RxJava2CallAdapterFactory.create())    .addConverterFactory(GsonConverterFactory.create())    .build();

  我们直接来看看Converter.Factory这个类:

  abstract class Factory {    /**     * Returns a {@link Converter} for converting an HTTP response body to {@code type}, or null if     * {@code type} cannot be handled by this factory. This is used to create converters for     * response types such as {@code SimpleResponse} from a {@code Call<SimpleResponse>}     * declaration.     */    public @Nullable Converter<ResponseBody, ?> responseBodyConverter(Type type,        Annotation[] annotations, Retrofit retrofit) {      return null;    }    /**     * Returns a {@link Converter} for converting {@code type} to an HTTP request body, or null if     * {@code type} cannot be handled by this factory. This is used to create converters for types     * specified by {@link Body @Body}, {@link Part @Part}, and {@link PartMap @PartMap}     * values.     */    public @Nullable Converter<?, RequestBody> requestBodyConverter(Type type,        Annotation[] parameterAnnotations, Annotation[] methodAnnotations, Retrofit retrofit) {      return null;    }    /**     * Returns a {@link Converter} for converting {@code type} to a {@link String}, or null if     * {@code type} cannot be handled by this factory. This is used to create converters for types     * specified by {@link Field @Field}, {@link FieldMap @FieldMap} values,     * {@link Header @Header}, {@link HeaderMap @HeaderMap}, {@link Path @Path},     * {@link Query @Query}, and {@link QueryMap @QueryMap} values.     */    public @Nullable Converter<?, String> stringConverter(Type type, Annotation[] annotations,        Retrofit retrofit) {      return null;    }    /**     * Extract the upper bound of the generic parameter at {@code index} from {@code type}. For     * example, index 1 of {@code Map<String, ? extends Runnable>} returns {@code Runnable}.     */    protected static Type getParameterUpperBound(int index, ParameterizedType type) {      return Utils.getParameterUpperBound(index, type);    }    /**     * Extract the raw class type from {@code type}. For example, the type representing     * {@code List<? extends Runnable>} returns {@code List.class}.     */    protected static Class<?> getRawType(Type type) {      return Utils.getRawType(type);    }  }

  这里需要重点关注三个方法,一个是responseBodyConverter,另一个方法是requestBodyConverter,还有一个是stringConverter。前两个方法我们从名字上就可以知道是什么意思,一个将获取Response的转换器;一个是获取Request的转换器;stringConverter方法,我们在将每个参数解析成为ParameterHandler对象看到过,这个方法的作用主要将参数转换成为网络请求需要的配置参数。
  具体的实现,我们来看GsonConverterFactory,在这里,我们重点分析一下responseBodyConverter方法,requestBodyConverter方法跟responseBodyConverter是一样的原理。

  @Override  public Converter<ResponseBody, ?> responseBodyConverter(Type type, Annotation[] annotations,      Retrofit retrofit) {    TypeAdapter<?> adapter = gson.getAdapter(TypeToken.get(type));    return new GsonResponseBodyConverter<>(gson, adapter);  }

  这里创建了一个GsonResponseBodyConverter对象,我们来看看这个类:

final class GsonResponseBodyConverter<T> implements Converter<ResponseBody, T> {  private final Gson gson;  private final TypeAdapter<T> adapter;  GsonResponseBodyConverter(Gson gson, TypeAdapter<T> adapter) {    this.gson = gson;    this.adapter = adapter;  }  @Override public T convert(ResponseBody value) throws IOException {    JsonReader jsonReader = gson.newJsonReader(value.charStream());    try {      T result = adapter.read(jsonReader);      if (jsonReader.peek() != JsonToken.END_DOCUMENT) {        throw new JsonIOException("JSON document was not fully consumed.");      }      return result;    } finally {      value.close();    }  }}

  整个GsonResponseBodyConverter非常的简单,在ServiceMethodtoResponse方法里面,就是调用了convert方法将Responsebody转换成为我们的Bean对象。
  而我们从这里可以看出来,整个convert方法非常的简单,没有做什么难以理解的操作,实际上,所有的解析工作都是由Gson给我们完成的。从这里来看,Gson确实强大,不愧是Google爸爸的东西。这里就不对Gson做过多的解释,因为我也不懂?。

9. 总结

  最后,我们对Retrofit做一个简单的总结。

  1. Retrofit大体上分为4个部分:接口、Request(ServiceMethod)、网络请求执行器Call、数据转换器Converter。每个部分都有自己的职责。
  2. Retrofit里面最核心的一个类就是ServiceMethod。这个类负责解析方法,生成对应的Request,转换Response。
  3. Retrofit 内部使用太多太多的设计模式,是学习设计模式的不二典范。

文章转载于:https://www.jianshu.com/p/064d944606a7

原著是一个有趣的人,若有侵权,请通知删除

本博客所有文章如无特别注明均为原创。
复制或转载请以超链接形式注明转自起风了,原文地址《Android 主流框架源码分析 – Retrofit源码分析
   

还没有人抢沙发呢~