Retrofit之Converter简单解析

Retrofit源码解析之请求流程概述》简单的对Retrofit的请求流程做了梳理:从该篇文章中我们知道Retrofit是通过Convert来完成数据解析转换的:

//ServiceMethod的方法
R toResponse(ResponseBody body) throws IOException {
    //Converter对象 
    return responseConverter.convert(body);
  }

本文就以Gson作为数据转换类简单的介绍Converter是怎么工作的。
So,让我们开车吧!咳,让我们开始吧。

Retrofit在内部帮我们将服务器返回的数据进行了解析,数据解析的目的无非就是将一种数据源转换成另外一种数据类型,所以Retrofit将此行为抽象成了一个接口:

//F源数据,T解析后返回的数据类型
public interface Converter<F, T> {
  T convert(F value) throws IOException;
}

常规情况下我们只对服务器返回的数据源解析成我们的需要的数据就可以了,在Okhttp中服务器返回的数据源就是ResponseBody对象。所以我们的目标就是对ResponseBody进行解析:

T convert(ResponseBody value){
  //do convert and return t
}

其实Retrofit考虑的远远比我们的多,不仅可以对ResponseBody进行转换,也可以对RequestBody进行转换;具体其内部提供了一个Convert.Factory:

abstract class Factory {
    //对ResponseBody进行数据转换的转换器
    public @Nullable Converter<ResponseBody, ?> responseBodyConverter(Type type,
        Annotation[] annotations, Retrofit retrofit) {
      return null;
    }

    //将未知数据转换成RequestBody的转换器
    public @Nullable Converter<?, RequestBody> requestBodyConverter(Type type,
        Annotation[] parameterAnnotations, Annotation[] methodAnnotations, Retrofit retrofit) {
      return null;
    }

    //将未知数据转换成String类型的数据转换器
    public @Nullable Converter<?, String> stringConverter(Type type, Annotation[] annotations,
        Retrofit retrofit) {
      return null;
    }
}

我们知道,如果要Retrofit要使用Gson作为数据转换器的话,在初始化Retrofit的时候需要添加这么一个代码:

Retrofit retrofit = new Retrofit.Builder()
                .baseUrl("http://www.xxx.xxx")  
               .addConverterFactory(GsonConverterFactory.create()) //添加Gson
                .build();

此篇博文的主旨就是看看这玩意内部的具体原理.,不用说GsonConvertFactory实现了Convert.Factory(Convert.Factoy的具体作用就是获取一个Convert)。其create方法如下:

  public static GsonConverterFactory create() {
    return create(new Gson());
  }

  public static GsonConverterFactory create(Gson gson) {
    return new GsonConverterFactory(gson);
  }
  
  class GsonConverterFactory extends Converter.Factory{
     //持有一个gson对象用来讲原始数据转换成JavaBean
     private final Gson gson;

     private GsonConverterFactory(Gson gson) {
       this.gson = gson;
      }
  }
  

所以我们来看看GsonConverterFactory 是怎么对ResponseBody 进行JavaBean的转换的吧:

 //GsonConverterFactory 类的工厂方法,返回一个Converter对象
 public Converter<ResponseBody, ?> responseBodyConverter(Type type, Annotation[] annotations,
      Retrofit retrofit) {
    //Gson的数据转换器
    TypeAdapter<?> adapter = gson.getAdapter(TypeToken.get(type));
    return new GsonResponseBodyConverter<>(gson, adapter);
  }

responseBodyConverter方法需要根据type类型来返回一个对应的Gson的TypeAdapter对象交给GsonResponseBodyConverter这个转换器,进入其convet方法看看:

public T convert(ResponseBody value) throws IOException {
    //拿到ResponseBody的数据流
    JsonReader jsonReader = gson.newJsonReader(value.charStream());
     //Gson将数据转换成JavaBen
      return adapter.read(jsonReader);
   
  }

还是很简单的逻辑:
1、拿到ResopnseBody的真实数据流
2、交给Gson的JsonReader进行处理
3、调用Adapter的read方法返回对应的JavaBean
关于TypeAdapter的详细使用,可以参考博主的《Gson源码解析

那么Retrofit是怎么知道返回值数据的真实类型的呢?
我们知道java中一个对象是封装成Method对象的:

 //根据Method对象获取对应method的返回值类型
 Type returnType = method.getGenericReturnType();

其实这个拿到的是一个Call<T>类型的对象,是拿不到T具体的T对象的,为此
在ServiceMethod的createAdapter方法中对接口方法做了处理(详情见):

  private CallAdapter<T, R> createCallAdapter() {
      //获取方法的放回值类型
      Type returnType = method.getGenericReturnType();
     
      Annotation[] annotations = method.getAnnotations();
      //将returnType 交给 一个CallAdapter
        return (CallAdapter<T, R>) retrofit.callAdapter(returnType, annotations);
    
    }

上面的returnType已经辗转之后具体在Retrfoit中交给了ExecutorCallAdapterFactory,对ReturnType做了进一步的解析来获取真正的javaBean类型:

 //ExecutorCallAdapterFactory的get方法
  public CallAdapter<?, ?> get(Type returnType, Annotation[] annotations, Retrofit retrofit) {
  
    //获取真正的JavaBean类型,比如Call<T> 则返回T的type对象 
    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);
      }
    };
  }

所以总体上Retrofit的执行逻辑可以总结如下:
1、拿到某个ApiService方法的返回值JavaBean类型returnType(具体见ServiceMethod的createAdapter方法)
2、根据返回值的信息,通过ConvertFactory找到一个对应的Converter
3、将Okhttp的数据源 转换成returnType类型的数据


其实读到此处还有一个问题没有深究:比如Retrofit怎么定位到某个方法返回值使用的是哪个Converter对象的呢?下面就来仔细分析这个流程。
以下面代码为例,看看Retrofit都做了些什么:

Retrofit retrofit = new Retrofit.Builder()
                .baseUrl("http://www.xxx.xxx")  
               .addConverterFactory(GsonConverterFactory.create()) //添加Gson
                .build();

//其addConverterFactory方法在Retrofit.Builder类里面如下:
   //持有
   private final List<Converter.Factory> converterFactories = new ArrayList<>();
 
   public Builder addConverterFactory(Converter.Factory factory) {
      converterFactories.add(checkNotNull(factory, "factory == null"));
      return this;
    }

最终Builder的converterFactories这个集合在build方法构建Retrofit的时候做了如下处理:

   List<Converter.Factory> converterFactories =
          new ArrayList<>(1 + this.converterFactories.size());
     
       //添加一个默认的 BuiltInConverters对象
      converterFactories.add(new BuiltInConverters());
      
      converterFactories.addAll(this.converterFactories);

这段代码除了把用户自定义的Converter.Factory注入到集合中之外,还在集合的第一个位置插入了BuiltInConverters这个默认的Converter.Factory对象。那么Retrofit是如何在这个集合里面定位到自己所需的Converter.Factory的呢?
在《Retrofit之OkhttpCall执行原理详解》一文中我们知道在每个请求执行的时候都需要创建一个ServiceMethod对象,而且每个方法执行的时候都需要最终调用toResponse方法来进行对ResponseBody进行转换:

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

那么responseConverter这个Converter对象是怎么初始化的就是回答上面问题的关键所在,追踪在代码发现其初始化ServiceMethod.Builder方法的createResponseConverter进行初始化的:

  private Converter<ResponseBody, T> createResponseConverter() {
     //获取方法的注解信息
      Annotation[] annotations = method.getAnnotations();
    
      //返回Converter对象
      return retrofit.responseBodyConverter(responseType, annotations);
  
    }

如上所示,拿到注解和最终数据返回值类型之后交给了retrofit.responseBodyConverter,需要注意的是这个responseType就是Call<T>的T。还得进入Retrofit的responseBodyConverter方法进行查看,最终定位方法如下:

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

    int start = converterFactories.indexOf(skipPast) + 1;
    
    //对Converter.Factory集合进行遍历
    for (int i = start, count = converterFactories.size(); i < count; i++) {
  
      Converter<ResponseBody, ?> converter =
          converterFactories.get(i).responseBodyConverter(type, annotations, this);
      if (converter != null) {
        return (Converter<ResponseBody, T>) converter;
      }
    }

    //省略部分代码
  }

nextResponseBodyConverter方法做的核心工作就是遍历Converter.Factory的集合,然后调用Factory对象的responseBodyConverter,如果该方法的返回值不为null则确认匹配成功并立即终止循环。其实最最核心的匹配原则是根据returenType进行匹配一个conveter对象的。

Retrofit没有写类似的if-else这些硬编码来过滤需要哪个Converter,而是设计了一个工厂类,通过通过模式来完成Converter对象的创建。这点还是很牛叉的,因为通过我们只需要添加自己所需要的工厂类即可,可插拔性极强。事实上Retrofit自己提供了好多Converter.Factory的实现,如下图,第一个就是我们熟悉的GsonConverterFactory对象的实现:
在这里插入图片描述

简单的用一个图片来作为本篇博文的总结:
在这里插入图片描述
到此为止Retrofit对服务器返回的数据转换内部原理就讲解完毕,如果不对的地方,欢迎批评指正。
在此遗留一个问题,前面说过Converter.Factory添加了一个默认的BuiltInConverters对象,这个对象的作用是神马呢?后面在分析。

根据《Retrofit源码解析之请求流程概述》我们知道Retrofit通过OkhttpCall来完成同步或者异步请求,那么OkhttpCall的execute和enqueue方法是什么时候开始执行的呢?答案在下一篇博客

评论 8
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

郭梧悠

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值