Retrofit 源码阅读

人走茶凉 提交于 2019-12-16 20:17:48

【推荐】2019 Java 开发者跳槽指南.pdf(吐血整理) >>>

1.开发中一个常见网络请求过程


url,参数---> request ---> 转化成Http协议 -->请求执行 ---> 返回结果--> 转化成response ---> response转化成我们的对象

只有头和尾是我们日常开发进行自定义的,中间的其他过程都是网络框架进行的。

2.Retrofit做的事情


  • 1.url,参数Retrofit主要采用接口+注解方式
  • 2.中间网络请求框架采用动态代理去使用某个框架
  • 3.response转化成我们的对象采用adapter模式进行适配

3.Retrofit的实现过程


1)toRequest,url,参数Retrofit主要采用接口+注解方式

我们以一个简单的POST请求为例

@POST("ssoService/v1/logoutCTGT")
Call<BaseResponseBean> logout(@Body LogoutRequest logoutRequest);

@Documented
@Target(METHOD)
@Retention(RUNTIME)
public @interface POST {
  String value() default "";
}

@Documented
@Target(PARAMETER)
@Retention(RUNTIME)
public @interface Body {
}

<u>注解知识点解析</u>

  • 注解的定义

    • @Documented

    Documented注解表明这个注释是由 javadoc记录的,在默认情况下也有类似的记录工具。 如果一个类型声明被注释了文档化,它的注释成为公共API的一部分。

    • @Target(METHOD)

    注释可能出现在Java程序中的语法位置,TYPE,FIELD,METHOD,PARAMETER等

    • @Retention(RUNTIME)

    描述保留注释的各种策略,SOURCE(源码期,编译的时候会被忽略),CLASS(会被编译到类文件中,但是JVM中没有),RUNTIME(各个阶段都存在)

  • 注解的获取(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();
    }

获取注解的方法

this.methodAnnotations = method.getAnnotations();
this.parameterAnnotationsArray = method.getParameterAnnotations();

注解获取相对还是比较简单的,Method方法可以直接获取各种注解,一个是方法上面的注解,一个是参数上的注解

  • Retrofit对于获取到的注解的处理(ServiceMethod.Builder.build())
 for (Annotation annotation : methodAnnotations) {
        parseMethodAnnotation(annotation);
      }
      
......

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

注解处理后转化成相关属性

  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;

然后在ServiceMethod.toRequest方法里转换成 Request

  Request toRequest(@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 requestBuilder.build();
  }

以上分析了注解到toRequest,接下去我们分析下代码的调用过程

同步请求过程

OkHttpCall.execute()->OkHttpCall.createRawCall()->serviceMethod.toRequest()

异步过程

OkHttpCall.enqueue()->OkHttpCall.createRawCall()->serviceMethod.toRequest()

2)toResponse

优先解析toResponse的使用,可以方便我们反推Retrofit如何使用动态代理进行网络框架的调用

相比toRequest,toResponse则简单很多,日常工作当中我们也是经常使用Gson进行结果的转化,我们通过转化器的添加,以及转化的调用流程对toResponse进行一次分析

  • 我们先来下转化成我们自己的对象的方法是什么时候添加的
 public static <T> T createJsonApi(@Url String baseUrl, Class<T> service) {
        if (!checkUrl(retrofitRxJson, baseUrl)) {
            retrofitRxJson = new Retrofit.Builder()
                    .baseUrl(baseUrl)
                    .addConverterFactory(GsonConverterFactory.create())
                    .addCallAdapterFactory(RxJava2CallAdapterFactory.create())
                    .client(okHttpClient)
                    .build();
        }
        return retrofitRxJson.create(service);
    }
                    .addConverterFactory(GsonConverterFactory.create())

采用Gson把平台返回的内容转化成我们的对象

public class BaseResponseBean<T> {
    public Integer type;
    public String code;
    public String msg;
    public T data;
}
  • toResponse整个调用过程
  • 同步请求

OkHttpCall.excute()-> OkHttpCall.parseResponse()->serviceMethod.toResponse()->serviceMethod.responseConverter.convert()

  • 异步请求

OkHttpCall.enqueue()-> hik.lib.okhttp3.Callback.onResponse()->OkHttpCall.parseResponse()->serviceMethod.toResponse()->serviceMethod.responseConverter.convert()

  • responseConverter的初始化

ServiceMehtond.Builder.build()->ServiceMehtond.Builder.createResponseConverter()->retrofit.responseBodyConverter()-> retrofit.nextResponseBodyConverter()

<u>这里有个比较好的设计,看代码</u>

 public <T> Converter<ResponseBody, T> nextResponseBodyConverter(
      @Nullable Converter.Factory skipPast, Type type, Annotation[] annotations) {
    checkNotNull(type, "type == null");
    checkNotNull(annotations, "annotations == null");

    int start = converterFactories.indexOf(skipPast) + 1;
    for (int i = start, count = converterFactories.size(); i < count; i++) {
      Converter<ResponseBody, ?> converter =
          converterFactories.get(i).responseBodyConverter(type, annotations, this);
      if (converter != null) {
        //noinspection unchecked
        return (Converter<ResponseBody, T>) converter;
      }
    }

    StringBuilder builder = new StringBuilder("Could not locate ResponseBody converter for ")
        .append(type)
        .append(".\n");
    if (skipPast != null) {
      builder.append("  Skipped:");
      for (int i = 0; i < start; i++) {
        builder.append("\n   * ").append(converterFactories.get(i).getClass().getName());
      }
      builder.append('\n');
    }
    builder.append("  Tried:");
    for (int i = start, count = converterFactories.size(); i < count; i++) {
      builder.append("\n   * ").append(converterFactories.get(i).getClass().getName());
    }
    throw new IllegalArgumentException(builder.toString());
  }

<font color="Hotpink"><u>如果返回转换的类型有多个,会根据接口的返回类型进行匹配</u></font>

3)动态代理进行网络框架的使用

我们分析了toRequest()与toResponse(),以及从OkHttpCall网络请求调用两个方法的过程,接下去我们分析动态代理如何调用OkHttpCall里的execute()与enqueue()

我们在使用的时候都是定义接口,但是并没有定义实现类,这里就是使用动态代理方式实现了接口的实现类

##这里有个要注意的地方******************* <u>接口返回的是一个对网络请求进行了封装的对象,并没有进行网络请求</u>

  • 动态代理的创建
 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);
          }
        });
  }

从代码来说,动态代理是比较简单的。

<u>动态代理知识点解析</u>

动态代理(dynamic proxy)            利用Java的反射技术(Java Reflection),在运行时创建一个实现某些给定接口的新类(也称“动态代理类”)及其实例(对象),代理的是接口(Interfaces),不是类(Class),也不是抽象类。在运行时才知道具体的实现,spring aop就是此原理。           

   public static Object newProxyInstance(ClassLoader loader,
                                          Class<?>[] interfaces,
                                          InvocationHandler h)throws IllegalArgumentException
                                          

newProxyInstance,方法有三个参数:

loader: 用哪个类加载器去加载代理对象

interfaces:动态代理类需要实现的接口

h:动态代理方法在执行时,会调用h里面的invoke方法去执行

<u>结合我们Retrofit.create方法以及动态代理的知识,也就是我们接口里的所有方法最后都是调用</u>

serviceMethod.callAdapter.adapt(okHttpCall);

这里返回了一个包裹OkHttpCall的对象

这里又有一个知识点

Retrofit

  final hik.lib.okhttp3.Call.Factory callFactory;
  final HttpUrl baseUrl;
  final List<Converter.Factory> converterFactories;
  final List<CallAdapter.Factory> adapterFactories;
  final @Nullable Executor callbackExecutor;
  final boolean validateEagerly;
  

adapterFactories 返回的对象到底是RxJava2CallAdapterFactory,还是默认的DefaultCallAdapterFactory

我们看下serviceMethod中callAdapter属性的初始化

ServiceMethod.Builder.buid()->ServiceMethod.Builder.createCallAdapter->retrofit.callAdapter->retrofit.nextCallAdapter

上代码

 public CallAdapter<?, ?> nextCallAdapter(@Nullable CallAdapter.Factory skipPast, Type returnType,
      Annotation[] annotations) {
    checkNotNull(returnType, "returnType == null");
    checkNotNull(annotations, "annotations == null");

    int start = adapterFactories.indexOf(skipPast) + 1;
    for (int i = start, count = adapterFactories.size(); i < count; i++) {
      CallAdapter<?, ?> adapter = adapterFactories.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(adapterFactories.get(i).getClass().getName());
      }
      builder.append('\n');
    }
    builder.append("  Tried:");
    for (int i = start, count = adapterFactories.size(); i < count; i++) {
      builder.append("\n   * ").append(adapterFactories.get(i).getClass().getName());
    }
    throw new IllegalArgumentException(builder.toString());
  }

<font color="Hotpink"><u>最后是根据接口的返回类型去匹配使用那个AdapterFactory</u></font>

  • 接下去我们看下AdaperFactory的实现

我们先看默认的一个DefaultCallAdapterFactory的实现

final class DefaultCallAdapterFactory extends CallAdapter.Factory {
  static final CallAdapter.Factory INSTANCE = new DefaultCallAdapterFactory();

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

直接返回的是OkHttpCall 所以网络请求是 execute,enqueue。

如果是RxJava2CallAdapter 则返回的是Observable,然后通过RXJava方法进行网络请求调用,上代码

  public static void asyHttpRequest(Observable observable , BaseNetCallback netCallback){
        observable.subscribeOn(Schedulers.io())
                .observeOn(AndroidSchedulers.mainThread());
        observable.subscribe(new BaseNetObserver<>(netCallback));
    }
   

设置执行的线程,监听结果的线程,然后发起信号执行

我们在看下信号执行里的代码

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

到这里的话我们基本已经了解了Retrofit2 整个代码的过程了

####4.Retrofit的代码总结 Retrofit 主要的代码实现

  • OKHttpCall 主要是网络请求实际实现的地方,主要有

    • execute
    • enqueue
    • createRawCall
  • ServiceMethod

    • toRequest
    • toResponse
    • Builder.build()
  • Retrofit

    • create
    • nextCallAdapter
    • nextResponseBodyConverter
标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!