Retrofit,源码简单分析

作者: 喝那个东南西北风 | 来源:发表于2018-05-16 10:57 被阅读0次

    场景

    Retrofit + Okhttp3 是现在开发标配的网络请求框架
    Okhttp3 负责底层请求逻辑
    Retrofit 负责上层请求外观,以接口方法定义的形式去直观表现

    分析带问题

    1.如何将接口外观(包括参数,注解)转换为Ok3的请求
    2.如何将Ok3的响应,解析为接口定义的返回,以及目标业务数据类型
    3.核心类,CallAdapter, Converter的职责和意义

    场景设定

    下面,是一个retrofit的请求定义:

    @POST("/uoms/servlet/dispatch")
    Observable<BaseResponse> dispatch(@Field String workNo);
    
    @POST("/uoms/servlet/dispatchBatch")
    Call<BaseResponse> dispatchBatch(@Body ApiRequest request);
    

    可以看到,将繁杂、多变的请求,变换为了注解+方法参数,返回参数的形式,非常直观,明了,根据上面的定义,我们探寻一下调用过程和设计逻辑。

    解析接口

    a) 我们先关注,最核心的类,所有业务的起点 Retrofit:
    public final class Retrofit {
      private final Map<Method, ServiceMethod<?, ?>> serviceMethodCache = new ConcurrentHashMap<>();//这是一个方法的完整解析后的对象
    
      final okhttp3.Call.Factory callFactory;//OkhttpClient的引用
      final HttpUrl baseUrl;//请求的基础
      final List<Converter.Factory> converterFactories;
      final List<CallAdapter.Factory> callAdapterFactories;
      ...
    }
    

    划重点,这3个类代表了整个解析的核心:
    ServiceMethod
    单个接口方法的逻辑抽象,为了加快调用速度,Retrofit对它做了缓存

    Converter 和 Converter.Factory
    用于数据类型解析转换,包括将参数解析为RequestBody以及将ResponseBody的数据解析为我们需要的类型

    CallAdapter 和 CallAdapter.Factory
    用于生成返回外观,默认是Retrofit的Call<?>,如果用了RxJava2Adapter库,返回形式可以变换到RxJava2的Obserable<?>形式

    我们每次调用的时候,都是调用如下方法:

    public <T> T create(final Class<T> service) {
        ...
        return (T) Proxy.newProxyInstance(service.getClassLoader(), new Class<?>[] { service },
            new InvocationHandler() {
              ...
              @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.
               ...
                ServiceMethod<Object, Object> serviceMethod =
                    (ServiceMethod<Object, Object>) loadServiceMethod(method);
                OkHttpCall<Object> okHttpCall = new OkHttpCall<>(serviceMethod, args);
                return serviceMethod.adapt(okHttpCall);
              }
            });
      }
    

    代码逻辑很简单:
    1.Retrofit巧用动态代理模式,拦截方法调用,获取到Method签名,参数Argus,然后从Cache里面查找Method代表的 ServiceMethod,如果没有,通过Method 生成一个ServiceMethod,并加入到Cache,方便下次调用
    2.整个调用逻辑,都封成了OkHttpCall,OkHttpCall通过ServiceMethod,Argus,完成请求
    3.最后调用serviceMethod.adapt(),返回,这里实际调用的就是CallAdapter.Factory去转换为我们想要的Call<?>或者Obserable<?>,后面分析

    b)ServiceMethod

    ServiceMethod是所有调用的核心,包括怎么解析参数注解,怎么解析方法注解,包括返回形式是怎么返回,需要重点分析:

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

    可以看到ServiceMethod的构建很简单,传入Method,然后调用ServiceMethod的Build构造函数:

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

    在构造函数中,ServiceMethod已经拿到了方法的注解详细信息,方法的参数注解信息,方法的参数类型信息,后面都是围绕这些数据展开处理。

    然后我们再看ServiceMethod.Bulder.build()方法:

    public ServiceMethod build() {
          callAdapter = createCallAdapter();//查找CallAdapter
          responseType = callAdapter.responseType();//获取返回参数类型,这里实际是Call<?>或者Observable<?>里面的泛型类型
          ...
          responseConverter = createResponseConverter();// 通过ResponseType去Retrofit反查可以解析当前ResponseType的Converter
    
          for (Annotation annotation : methodAnnotations) {
            parseMethodAnnotation(annotation);//解析方法注解参数,例如POST,HEAD之类
          }
         ...省略各种请求合法性判定代码...
          int parameterCount = parameterAnnotationsArray.length;
          parameterHandlers = new ParameterHandler<?>[parameterCount];
          for (int p = 0; p < parameterCount; p++) {
            Type parameterType = parameterTypes[p];
           ...省略参数类型合法性判定代码...
            Annotation[] parameterAnnotations = parameterAnnotationsArray[p];
            ...
            //获取到对应参数类型的解析器,并保存到parameterHandlers数组里面,稍后解析真实请求的时候用到
            parameterHandlers[p] = parseParameter(p, parameterType, parameterAnnotations);
          }
        ...省略各种请求合法性判定代码...
          return new ServiceMethod<>(this);
        }
    

    所以我们可以到,上面的解析流程:
    解析返回参数,查找到对应的CallAdapter,保存 ->
    解析方法注解,并保存相关请求信息 ->
    解析方法参数注解,查找对应的ParameterHandler并保存 ->
    完成返回 ServiceMethod实例

    所以我们可以知道,ServiceMethod对象的构建完成,已经确定了方法参数调用怎么解析,请求返回怎么解析,剩下就是执行真实的请求了

    c) ServiceMethod.callAdapter的作用和来源

    CallAdapter接口源码如下:

    public interface CallAdapter<R, T> {
      //返回实际的返回参数类型,Call<Repo> 这里的Type就是Repo.class
      Type responseType();
    
      //适配功能,返回Call<R>结果的包装器
      T adapt(Call<R> call);
    
      //抽象工程定义
      abstract class Factory {
        
        //能否处理,通过这个方法来判定,有效处理会返回CallAdapter实例,否则为NULL
        public abstract @Nullable CallAdapter<?, ?> get(Type returnType, Annotation[] annotations,
            Retrofit retrofit);
        ...
      }
    }
    

    CallAdapter,主要2个方法,一个提供返回值的确切类型,一个是将Call<R>转换为其他形式的包装器,比如返回 Observable<R>
    CallAdapter.Factory,抽象工程,get返回Null代表该Factory处理不了该returnType

    我们可以看到之前的Retrofit.create():

    OkHttpCall<Object> okHttpCall = new OkHttpCall<>(serviceMethod, args);
    return serviceMethod.adapt(okHttpCall);
    
    这实际调用就是
    return serviceMethod.callAdapter.adapt(okHttpCall);
    始终处理对象,都是OkHttpCall类型
    

    CallAdapter负责转换返回值形式,基于Call<?>,转换为我们想要的形式,我们看看这个serviceMethod.callAdapter怎么来的

    查看ServiceMethod的createCallAdapter():

    private CallAdapter<T, R> createCallAdapter() {
          Type returnType = method.getGenericReturnType();//拿到方法的返回类型
          ...
          Annotation[] annotations = method.getAnnotations();
          try {
            return (CallAdapter<T, R>) retrofit.callAdapter(returnType, annotations);
          } catch (RuntimeException e) { 
            ...
          }
        }
    

    我们再看看Retrofit的callAdapter():

    public CallAdapter<?, ?> callAdapter(Type returnType, Annotation[] annotations) {
      return nextCallAdapter(null, returnType, annotations);
    }
    
    public CallAdapter<?, ?> nextCallAdapter(@Nullable CallAdapter.Factory skipPast, Type returnType,
          Annotation[] annotations) {
      ...
      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;
        }
      }
      ...注意:如果上面代码没有查找到合适的CallAdapter,一定抛出异常...
    }
    

    可以知道,Retrofit通过ServiceMethod提供的returnType, annotations,循环遍历callAdapterFactories,来获取可以处理该returnType的CallAdapter实例,在Retrofit.build()时候,默认加入了CallAdapter.Factory的默认实现DefaultCallAdapterFactory :

    final class DefaultCallAdapterFactory extends CallAdapter.Factory {
      ...
      @Override
      public CallAdapter<?, ?> get(Type returnType, Annotation[] annotations, Retrofit retrofit) {
        ...
        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;
          }
        };
      }
    }
    

    所以默认,我们就可以定义返回接口为 Call<?>类型
    到此,CallAdapter的作用和来源分析完毕

    d) Converter 和 Converter.Factory 作用意义

    Converter,源码定义:

    public interface Converter<F, T> {//将 F类型的数据,转换为 T类型
      T convert(F value) throws IOException;
      ...
    }
    

    Converter.Factory源码定义:

    public interface Converter<F, T> {
      ...
      abstract class Factory {
        
        public @Nullable Converter<ResponseBody, ?> responseBodyConverter(Type type,
            Annotation[] annotations, Retrofit retrofit) {
          return null;
        }
    
        public @Nullable Converter<?, RequestBody> requestBodyConverter(Type type,
            Annotation[] parameterAnnotations, Annotation[] methodAnnotations, Retrofit retrofit) {
          return null;
        }
    
        public @Nullable Converter<?, String> stringConverter(Type type, Annotation[] annotations,
            Retrofit retrofit) {
          return null;
        }
        ...
      }
    }
    

    与CallAdapter.Factory类似,Converter.Factory可以处理某类型的数据,则返回有效的Converter实例,否则就返回null
    Converter.Factory.requestBodyConverter();负责将对应类型参数数据,转换为RequestBody,这个方法在拼装请求的时候,大量用到
    Converter.Factory.responseBodyConverter();负责将ResponseBody的数据,转换为对应需要的类型
    stringConverter();负责将对应类型数据,转换为String形式

    ConverterFactory负责生产3类Converter,一个是将参数转变为RequestBody形式的,请求装配Converter,一个是将ResponseBody解析为方法定义的返回值(这个是指的Call<T>里面的T类型),一个是将方法参数形式转换为字符串

    e) Converter使用:ParameterHandler

    上面介绍了Converter作用,我们先看看最多使用的地方。
    ParameterHandler,对参数注解进行解析,先看定义:

    abstract class ParameterHandler<T> {
      abstract void apply(RequestBuilder builder, @Nullable T value) throws IOException;
      ...解析参数,加入进ReuqestBuilder
    }
    

    注:RequestBuilder是Retrofit对构建Ok3的Request逻辑封装
    这里可以看出,ParameterHandler负责将参数合理的装配进入RequestBuilder
    它的实现类,我们应该很眼熟:

    ParameterHandler.Query
    ParameterHandler.Header
    ParameterHandler.Path
    ...
    

    这些就是具体这些注解内容,将以什么形式去apply到RequestBuilder里面,以
    ParameterHandler.Query举例:

    static final class Query<T> extends ParameterHandler<T> {
        private final String name;
        private final Converter<T, String> valueConverter;
        private final boolean encoded;
    
        Query(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 queryValue = valueConverter.convert(value);
          if (queryValue == null) return; // Skip converted but null values
    
          builder.addQueryParam(name, queryValue, encoded);
        }
      }
    

    很简单,就是用Converter去转换T类型的参数为字符串,然后拼接进入RequestBuilder,我们再回到ServiceMethod.Build.build()方法,解析方法注解:

    int parameterCount = parameterAnnotationsArray.length;
    parameterHandlers = new ParameterHandler<?>[parameterCount];
    for (int p = 0; p < parameterCount; p++) {
      Type parameterType = parameterTypes[p];
      ...
      Annotation[] parameterAnnotations = parameterAnnotationsArray[p];
      ...
      parameterHandlers[p] = parseParameter(p, parameterType, parameterAnnotations);
    }
    

    再看ServiceMethod.parseParameter(),最终调用的ServiceMethod.parseParameterAnnotation()

    private ParameterHandler<?> parseParameterAnnotation(
            int p, Type type, Annotation[] annotations, Annotation annotation) {
      if (annotation instanceof Url) {
      ...
      } else if (annotation instanceof Path) {
      ...
      } else if (annotation instanceof Query) {
            Query query = (Query) annotation;
            String name = query.value();
            boolean encoded = query.encoded();
            Class<?> rawParameterType = Utils.getRawType(type);
            gotQuery = true;
              ParameterizedType parameterizedType = (ParameterizedType) type;
              Type iterableType = Utils.getParameterUpperBound(0, parameterizedType);
              Converter<?, String> converter =
                  retrofit.stringConverter(iterableType, annotations);
              return new ParameterHandler.Query<>(name, converter, encoded).iterable();
            }else{
      ...      
      }
      ...
    }
    

    流程:
    判定注解类型 -> 是Query -> 去Retrofit查找合适的Converter -> 拿到Converter,生成 ParameterHandler.Query实例,保存进入ServiceMethod的数组里面,等待解析真实请求的时候调用。

    我们再去跟踪Retrofit.stringConverter()方法:

    public <T> Converter<T, String> stringConverter(Type type, Annotation[] annotations) {
      ...
      for (int i = 0, count = converterFactories.size(); i < count; i++) {
        Converter<?, String> converter =  converterFactories.get(i).stringConverter(type, annotations, this);
        if (converter != null) {
          return (Converter<T, String>) converter;
        }
      }
      ...
      return (Converter<T, String>) BuiltInConverters.ToStringConverter.INSTANCE;
    }
    

    最终返回的是默认实现,ToStringConverter.INSTANCE,它也是简单调用toString()方法而已。

    至此,方法的参数解析也完毕了,
    我们得到了一个ServiceMethod实例,它包含了:
    1.请求地址、类型、头部信息
    2.包含了正确解析参数的ParameterHandler数组
    3.包含了正确解析响应类型的responseConverter
    4.包含了方法的返回适配器callAdapter

    发送请求

    a)了解Retrofit.Call
    Retrofit抽象出了自己的Call逻辑,一个可以操作的请求对象:

    //请求过程抽象
    public interface Call<T> extends Cloneable {
      
      //返回有效的Response
      Response<T> execute() throws IOException;
    
      //异步请求
      void enqueue(Callback<T> callback);
    
      boolean isExecuted();
    
      //主动取消请求
      void cancel();
    
      /** True if {@link #cancel()} was called. */
      boolean isCanceled();
    
      Call<T> clone();
    
      //转换出原始的Ok3请求
      Request request();
    }
    
    //异步回调
    public interface Callback<T> {
    
      void onResponse(Call<T> call, Response<T> response);
    
      void onFailure(Call<T> call, Throwable t);
    }
    
    //Retrofit返回结果封装
    public final class Response<T> {
      ...
      private final okhttp3.Response rawResponse;
      private final @Nullable T body;
      private final @Nullable ResponseBody errorBody;
      ...
      /** The raw response from the HTTP client. */
      public okhttp3.Response raw() {
        return rawResponse;
      }
    
      /** HTTP status code. */
      public int code() {
        return rawResponse.code();
      }
    
      /** HTTP status message or null if unknown. */
      public String message() {
        return rawResponse.message();
      }
    
      /** HTTP headers. */
      public Headers headers() {
        return rawResponse.headers();
      }
    
      /** Returns true if {@link #code()} is in the range [200..300). */
      public boolean isSuccessful() {
        return rawResponse.isSuccessful();
      }
    
      /** The deserialized response body of a {@linkplain #isSuccessful() successful} response. */
      public @Nullable T body() {
        return body;
      }
      ...
    }
    

    Retrofit,封装了完整的Call, Callback, Response,对底层Ok3做进一步透明化
    我们再看Call具体实现OkhttpCall具体执行调用:

    @Override 
    public Response<T> execute() throws IOException {
        okhttp3.Call call;
        ...
        call = rawCall = createRawCall();
        ...
        return parseResponse(call.execute());
    }
    
    @Override 
    public void enqueue(final Callback<T> callback) {
        ...
        okhttp3.Call call;
        synchronized (this) {
          ...
          if (call == null && failure == null) {
            try {
                call = rawCall = createRawCall();
            } catch (Throwable t) {
                ...
            }
          }
        }
        ...
        call.enqueue(new okhttp3.Callback() {
            @Override 
            public void onResponse(okhttp3.Call call, okhttp3.Response rawResponse) {
                Response<T> response;
                ...
                response = parseResponse(rawResponse);
                ...
                callback.onResponse(OkHttpCall.this, response);
                ...
            }
    
            ...
        });
    }
    
     private okhttp3.Call createRawCall() throws IOException {
        okhttp3.Call call = serviceMethod.toCall(args);//这里负责转换
        if (call == null) {
          throw new NullPointerException("Call.Factory returned null.");
        }
        return call;
      }
    
    Response<T> parseResponse(okhttp3.Response rawResponse) throws IOException {
        ResponseBody rawBody = rawResponse.body();
        ...
    
        ExceptionCatchingRequestBody catchingBody = new ExceptionCatchingRequestBody(rawBody);
        try {
          T body = serviceMethod.toResponse(catchingBody);//这里负责转换
          return Response.success(body, rawResponse);
        } catch (RuntimeException e) {
          ...
        }
    }
    

    这里我只列出了核心代码,可以看出,最终都是调用都是ServiceMethod.toCall()方法生成请求,跟踪toCall()源码:

    okhttp3.Call toCall(@Nullable Object... args) throws IOException {
        RequestBuilder requestBuilder = new RequestBuilder(httpMethod, baseUrl, relativeUrl, headers,
            contentType, hasBody, isFormEncoded, isMultipart);
        ...
        ParameterHandler<Object>[] handlers = (ParameterHandler<Object>[]) parameterHandlers;
      ...
        for (int p = 0; p < argumentCount; p++) {
          handlers[p].apply(requestBuilder, args[p]);
        }
    
        return callFactory.newCall(requestBuilder.build());
      }
    

    终于用到我们上面分析保存的信息了,ServiceMethod的相关属性,ParameterHandler数组,生成RequestBuilder,再构建出Ok3的Request,生成Ok3的Call。
    注: 这里callFactory指的是OkHttpClient

    然后收到响应的适合,都调用ServiceMethod.toResponse():

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

    serviceMethod.responseConverter在这里解析响应结果类型

    到此,整个请求调用过程,跟踪完毕

    总结流程

    CallAdapter,转换响应形式,默认是Call<T>,有兴趣的再去看看RxJava2Adapter下面的几个类
    Converter,转换参数类型,Converter.Factory转换参数到RequestBody,ResponseBody到参数

    1.生成ServiceMethod,解析了方法注解,保存了核心的请求属性,头部,其次是ParameterHandler数组用于解析方法参数值、类型,方法参数的注解
    2.生成OkhttpCall,内部调用ServiceMethod拼装请求 和 解析响应
    3.请求的生成靠 ServiceMethod.toRequest() 响应解析靠ServiceMethod.toResponse(),实际都是依赖第一步的解析保存的信息

    其他

    我们可以自己基于现有的RxJava2Adapter, GsonConvertFactory,重写CallAdapter,Converter,以达到我们的目标,比如:统一处理请求错误,统一处理额外业务数据(resultCode, resultMessage之类的)

    相关文章

      网友评论

        本文标题:Retrofit,源码简单分析

        本文链接:https://www.haomeiwen.com/subject/ovttdftx.html