Retrofit2分析

911 阅读9分钟

Retrofit2已经面世很久了,有很多好的文章分析过,这篇文章我只想记录自己阅读Retrofit 2.3.0源码后的分析过程,如何阅读源码以及分析我觉得是最重要的。

目录

  1. Retrofit2 使用
    • 使用步骤
  2. 源码分析
    • 构建 Retrofit 对象
    • 动态代理
    • 调用流程
  3. 自定义 ConverterFactory
  4. 总结

一、Retrofit2使用

1. 使用步骤

之前做过Gank的客户端,因此直接用Gank网站的请求了。

构建retrofit对象:

Retrofit retrofit = new Retrofit.Builder()
    .baseUrl("https://gank.io")
    .build();

定义请求interface, 可以把它内部的每一个接口方法看做是一个封装了请求url及参数的方法,其返回值是可执行请求的对象,而在 Retrofit 中默认是 Call 可执行对象,也就是说 call 调用某个方法,如 enqueue 就可以异步执行请求。

public interface ApiService {

    @GET("api/data/{type}/{size}/{page}")
    Call<ResponseBody> getArticles(@Path("type") String type, @Path("size") int size,
    		@Path("page") int page);

}

生成代理对象:

ApiService apiService = retrofit.create(ApiService.class);

获取 call 这个可执行请求的对象,并enqueue异步执行请求:

Call<ResponseBody> call = apiService.getArticles("Android",10, 1);
call.enqueue(new Callback<ResponseBody>() {
    @Override
    public void onResponse(Call<ResponseBody> call, Response<ResponseBody> response) {
        try {
            Log.d(TAG,response.body().string());
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
    @Override
    public void onFailure(Call<ResponseBody> call, Throwable t) {
    }
});

call.enqueue会执行真正的请求,并且内部会切换到主线程回调到 onResponse 方法内可拿到最终结果。

附上 官方Retrofit使用文档

Retrofit本质上是对okhttp3的封装,其最大的优势就是解耦做的非常好,接下来就从源码角度分析下。

二、源码分析

1. 构建Retrofit对象

构建Retrofit用到了 Builder 模式,很适合自定义一些东西。先走到 baseUrl 方法内看下:

public Builder baseUrl(String baseUrl) {
  checkNotNull(baseUrl, "baseUrl == null");
  HttpUrl httpUrl = HttpUrl.parse(baseUrl);
  if (httpUrl == null) 
    throw new IllegalArgumentException("Illegal URL: " + baseUrl);
  }
  return baseUrl(httpUrl);
}

将字符串类型的url解析成 HttpUrl对象,这个对象可以理解为拆分了url的协议、域名、端口、路径等变量并保存在了一个对象中,需要哪个部分就可以随时拿出,最后保存下 httpUrl 变量。

最简单的就是设置一个地址,然后直接调用 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);
}

如果不在构建Retrofit时设置 client 那么就会默认创建一个 OkHttpClient, 前面已经说过 Retrofit 是对 okhttp 的封装而已,本质上还是okhttp 进行请求,而现在只是分析 Retrofit, 因此不打算深入OkHttpClient, 但是根据它的接口类型也能判断是一个 生产okhttp中的Call对象的工厂, 而Call对象就是可执行任务的对象, Retrofit 中也有 Call 导致有点混乱。。需要好好辨别下。

若不设置 callbackExecutor 也会通过 platform.defaultCallbackExecutor() 创建一个默认的,通过看源码发现Retrofit也就支持两个平台,一个是Java8,一个就是Android :

static class Android extends Platform {
    @Override public Executor defaultCallbackExecutor() {
      return new MainThreadExecutor();
    }

    @Override CallAdapter.Factory defaultCallAdapterFactory(@Nullable Executor
    			callbackExecutor) {
      if (callbackExecutor == null) throw new AssertionError();
      return new ExecutorCallAdapterFactory(callbackExecutor);
    }

    static class MainThreadExecutor implements Executor {
      private final Handler handler = new Handler(Looper.getMainLooper());

      @Override public void execute(Runnable r) {
        handler.post(r);
      }
    }
  }

它会返回一个 MainThreadExecutor 对象,而它的实现非常简单,就是通过Handler将线程切换到主线程,记住这个 callbackExecutor , 它会在某个地方执行 execute 方法这个时候会切到主线程进行回调。

adapterFactories列表对象可以把它理解为 生产 adapter 的工厂,而 adapter 从名字上来看是适配器,它调用其中的 adapt 方法返回的就是可执行对象!在 Retrofit 里默认的实现就是 Call 对象,并且由于 Retrofit 的神奇解耦,它可以自定义任何CallAdapter 和 Factory,最有名的就是RxJava,其返回的可执行对象变成了Observable而已 ,列表表示可以添加多个 callAdapter工厂, 根据你写的接口方法的返回值判断选择哪个适配器。

默认情况下调用 platform.defaultCallbackExecutor() 创建 ExecutorCallAdapterFactory ,这个类很关键,工厂顾名思义就是生产CallAdapter的,其中的 get 方法返回了一个 CallAdapter 对象,这个对象会在某个关键时刻调用 adapt 从而返回 Call 对象,这个调用流程之后再详解。

converterFactories 转换器工厂,其套路和 adapterFactories 非常类似,它的作用就是将请求后返回的数据转换成你想要的数据结构,应用最广的应该是 GsonConverterFactory , 可以将结果用 Gson 转换成自定义好的数据结构。其默认实现是 BuiltInConverters ,这个默认的工厂返回的 Converter 基本没做什么事情,基本只是把 okhttp 返回给 Retrofit 的 ResponseBody 数据结构返回出去。因此暂时不需要太过在意。

最后以这些对象作为参数传给 Retrofit 构造一个 Retrofit 实例。总结下关键的几个对象:将 url 解析成 HttpUrl 对象并保存供以后使用;在不自定义的情况下默认创建 callFactory, 这个 OkHttpClient 的实例,最终会通过这个工厂生成 okhttp 中的 call 对象来执行真正的请求;callbackExecutor默认实现是 MainThreadExecutor 是用来最后切换到主线程用的;adapterFactories用来生产adapter,默认是 ExecutorCallAdapterFactory,最后会返回 Call 对象;converterFactories默认实现是 BuiltInConverters, 基本上就是将 okhttp 返回的 ResponseBody 返回出去。

2. 动态代理

动态代理这个可以说是 Retrofit 最精华的地方了。再此之前需要理解反射和动态代理,推荐两篇不错的文章:反射动态代理

构建完 Retrofit 对象后,需要调用 retrofit.create(ApiService.class), 生成 ApiService 接口对应的动态代理对象。

public <T> T create(final Class<T> service) {
  // ......
  return (T) Proxy.newProxyInstance(service.getClassLoader(), new Class<?>[] { service },
      new InvocationHandler() {
        // 获取目前的平台,对于我们来说就是Android平台
        private final Platform platform = Platform.get();
        @Override public Object invoke(Object proxy, Method method, @Nullable Object[] args)
            throws Throwable {
          // ......
          
          // 构建 接口方法 对象,此对象主要是解析注解并拼接成请求所需的参数。
          // 它是最后用来给okhttp使用并真正发送请求。
          ServiceMethod<Object, Object> serviceMethod =
              (ServiceMethod<Object, Object>) loadServiceMethod(method);
          // 本质上就是Retrofit对Okhttp的Call对象进一步的封装
          OkHttpCall<Object> okHttpCall = new OkHttpCall<>(serviceMethod, args);
          // 最后默认返回的也是一个Call可执行任务对象(当然RxJava返回的对象就不是默认的了),
          // 其实类似于是OkHttpCall的代理类,只不过内部加了切换到主线程的操作。
          return serviceMethod.callAdapter.adapt(okHttpCall);
        }
      });
}

部分没啥用的代码省略了,剩下的都是关键代码,并且加了注释。这里再说下动态代理相关的,create 方法返回值是第一部分的使用步骤中的 apiService, 它是动态代理生成的对象。调用动态代理对象的方法后会调到 invoke 方法,返回值对应使用步骤中的 call 对象。

进入 loadServiceMethod 方法,可以看到对 serviceMethod 会有一个缓存,一个方法只会解析一次,之后重复利用。然后通过 build 构建一个 ServiceMethod 对象。

public ServiceMethod build() {
  // 跟进 createCallAdapter 可以看到它返回的 CallAdapter 是根据方法的返回类型,
  // 如本文使用步骤中返回类型是 Call,那么就会返回之前已创建过的默认 CallAdapter
  callAdapter = createCallAdapter();
  // 返回结果类型,对于 Retrofit 默认返回结果类型是 ResponseBody,
  // 因此在阅读源码时直接把 responseType 看做 ResponseBody 类型。
  responseType = callAdapter.responseType();
  
  // ......
  
  // 跟进 createResponseConverter 方法可以看到它是根据 responseType 返回对应的 Converter。
  responseConverter = createResponseConverter();
  // 遍历解析方法上的注解如 @GET、@POST等(拆分注解中的字符串,将参数名记录)
  for (Annotation annotation : methodAnnotations) {
    parseMethodAnnotation(annotation);
  }
  
  // ......

  for (int p = 0; p < parameterCount; p++) {
    Type parameterType = parameterTypes[p];
    // ......
    Annotation[] parameterAnnotations = parameterAnnotationsArray[p];
    // ......
    
    // 遍历解析形参的注解,如@Path、@Query等.
    parameterHandlers[p] = parseParameter(p, parameterType, parameterAnnotations);
  }
  
  // ......
  
  return new ServiceMethod<>(this);
}

其实 ServiceMethod 不是理解 Retrofit 的关键,只需知道它封装了解析注解逻辑和记录参数。

直接看下 OkhttpCall 的enqueue方法

@Override public void enqueue(final Callback<T> callback) {
  okhttp3.Call call;
  synchronized (this) {
        // ......
        
        // 这里的 call 就是 OkHttp 的 call
        call = rawCall = createRawCall();
    
  }
  // ......
  
  // okhttp 的 call 异步执行并回调,注意这里依然在异步线程中。
  call.enqueue(new okhttp3.Callback() {
    @Override public void onResponse(okhttp3.Call call, okhttp3.Response rawRespon
        throws IOException {
      Response<T> response;
      try {
        response = parseResponse(rawResponse);
      } catch (Throwable e) {
        callFailure(e);
        return;
      }
      callSuccess(response);
    }
    
    // ....
    
    private void callSuccess(Response<T> response) {
      try {
        callback.onResponse(OkHttpCall.this, response);
      } catch (Throwable t) {
        t.printStackTrace();
      }
    }
  });
}

最后一步通过 callAdapter.adapt(okHttpCall) 返回一个最终可执行对象,这里我们都是看默认的,因此 callAdapter 是 ExecutorCallAdapterFactrory 中 get 获取的 callAdapter, 其 adapter 方法返回的是 ExecutorCallbackCall 也是实现了 Call 接口,之前说过此对象是 okHttpCall 的代理对象,因此传入 okHttpCall 实例,而内部干的最关键的一件事就是在异步执行请求完成后通过 callBackExecutor(之前早已准备好的Handler切换) 切换到主线程。

3. 调用流程

调用流程

绿色线框代表我们使用 Retrofit 需要做的几步。

Retrofit 内部有大量的设计模式,设计的非常巧妙,多看源码也能提高我们的代码设计能力。build 阶段用了构造者模式,create 用了动态代理模式,CallAdapterFactory 和 ConverterFactory 用了适配器模式,等等。

三、 自定义ConverterFactory

为了加深对Retrofit的理解以及体会它的好用程度,写了一个自定义ConverterFactory。

public final class StringConverterFactory extends Converter.Factory {

    private final static String TAG = "StringConverterFactory";

    @Nullable
    @Override
    public Converter<ResponseBody, ?> responseBodyConverter(Type type, Annotation[] annotations, Retrofit retrofit) {
        if(type == String.class){
            return StringResponseConverter.INSTANCE;
        }
        return null;
    }

    @Nullable
    @Override
    public Converter<?, RequestBody> requestBodyConverter(Type type, Annotation[] parameterAnnotations, Annotation[] methodAnnotations, Retrofit retrofit) {
        if(type == RequestBody.class){
            return StringRequestBodyConverter.INSTANCE;
        }
        return null;
    }

    final static class StringResponseConverter implements Converter<ResponseBody, String> {
        final static StringResponseConverter INSTANCE = new StringResponseConverter();

        @Override
        public String convert(ResponseBody value) throws IOException {
            Log.d(TAG, value.toString());
            return value.string();
        }
    }

    final static class StringRequestBodyConverter implements Converter<RequestBody, RequestBody> {
        final static StringRequestBodyConverter INSTANCE = new StringRequestBodyConverter();

        @Override
        public RequestBody convert(RequestBody value) throws IOException {
            Log.d(TAG, "no change, hahaha...");
            return value;
        }
    }

}

支持String的泛型参数,其实内部也就是把 ResponseBody 提前转成 String 并打印而已。在使用的时候需要在构建 Retrofit 时添加:

Retrofit retrofit = new Retrofit.Builder()
    .baseUrl("https://gank.io") 
    .addConverterFactory(new StringConverterFactory())
    .build();

四、总结

最后对 Retrofit 做一个总结 。Retrofit 是对 okhttp3 的封装,其最大的特性就是解耦,你可以自定义很多你想要的东西,最知名的就是 RxJava 的配合使用。

首先调用 build 方法创建生产 okhttp3 的 call 对象的callFactory、创建用来切换线程的 callBackExecutor、创建生产 CallAdapter 的 CallAdapterFactory、创建生产 Converter 的 ConverterFactory。

接着调用 retrofit.create 创建动态代理对象,调用接口方法会触发 invoke 方法,invoke 内创建 ServiceMethod并做了注解解析、创建OkhttpCall(对okhttp3的call进一步封装),最后通过 CallAdapter.adapt 方法返回可执行对象默认是 ExecutorCallBackCall。

最后调用 call.enqueue 后 okhttp3将请求 ServiceMethod 解析好的url和参数,最终返回结果会被 Converter.convert 解析成你想要的数据模型(默认是ResponseBody),最后通过 callBackExecutor.execute 切换到主线程将数据回调给开发者。

ps:做完 Retrofit 源码分析后,还想看下 RxJava 的源码,为了更深入理解 RxJava 和 Retrofit 共同使用的原理。最近想到一句话感觉蛮有意思的:当你不知道做什么的时候,去看看源码或官方文档吧。