Retrofit2分析

Retrofit2已经面世好久了,有不少好的文章分析过,这篇文章我只想记录本身阅读Retrofit 2.3.0源码后的分析过程,如何阅读源码以及分析我以为是最重要的。java

目录

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

1、Retrofit2使用

1. 使用步骤

以前作过Gank的客户端,所以直接用Gank网站的请求了。git

构建retrofit对象:github

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

}
复制代码

生成代理对象:api

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 方法内可拿到最终结果。bash

附上 官方Retrofit使用文档数据结构

Retrofit本质上是对okhttp3的封装,其最大的优点就是解耦作的很是好,接下来就从源码角度分析下。异步

2、源码分析

1. 构建Retrofit对象

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

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 用了适配器模式,等等。

3、 自定义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();
复制代码

4、总结

最后对 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 共同使用的原理。最近想到一句话感受蛮有意思的:当你不知道作什么的时候,去看看源码或官方文档吧。

相关文章
相关标签/搜索