Retrofit源码解析

一、引言

RetrofitOKHttp同为 square 出品的网络请求相关库,不同的是 Retrofit本身不进行网络请求,而是作为一个协调者,协调其他组件共同处理网络请求。用官网描述来说就是:Retrofit是可插拔的,它允许不同的执行机制和类库用于执行HTTP请求、允许不同序列化的类库进行java实体类与HTTP响应数据之间转换。

Retrofit的网络请求部分默认基于OkHttp,关于OkHttp,鄙人写过 OkHttp源码分析一文,感兴趣的童鞋可以看看。

本文纯属基于个人理解,源码解析不限于执行流程,因此受限于知识水平,有些地方可能依然没有理解到位,还请发现问题的童鞋理性指出。

温馨提示:本文源码基于 Retrofit-2.4.0

二、流程分析

1. 简单使用

这里以请求 玩Android 首页数据为例,演示使用Retrofit进行网络请求的最基本方式。

首先如下初始化 Retrofit

public void initializeRetrofit() {  
    retrofit = new Retrofit.Builder()
            .addConverterFactory(GsonConverterFactory.create())
            .baseUrl("http://wanandroid.com")
            .build();
}

然后如下建立请求接口:

public interface Service{ 
    @GET("article/list/{page}/json")
    public Call<ResponseEntry<ResponseData>> getHomeList(@Path("page")int page);
}

接着如下调用请求、处理响应数据:

public void getHomeList(int page, ResCallback<ResponseEntry<ResponseData>> callback){
    if (service == null)
        service = retrofit.create(Service.class);
    service.getHomeList(page).enqueue(new Callback<ResponseEntry<ResponseData>>() {
            @Override
            public void onResponse(Call<ResponseEntry<ResponseData>> call, Response<ResponseEntry<ResponseData>> response) {
                System.out.println(response.message());
                System.out.println(response.code());
                System.out.println(response.headers());

                if (response.isSuccessful()){
                    ResponseEntry<ResponseData> body = response.body();
                    if (body == null) {
                        callback.onFailed(new Exception("body is null !!"));
                        return;
                    }
                    callback.onSuccess(body);
                }
            }
            @Override
            public void onFailure(Call<ResponseEntry<ResponseData>> call, Throwable t) {
            }
        });
}

上面可以注意到的一点是,不同于直接使用OkHttp,这里response.body()可以直接拿到我们需要的解析好的Java实体类了,而不需要再做Json数据解析工作, 它的使用过程如下:

Retrofit使用过程

而一般来说,我们使用OkHttp进行网络请求的使用过程如下:

一般OkHttp使用过程

显然 Retrofit 的目的就是把网络请求、响应数据解析等相互分离的操作都整合到一起,达到 All in one 的效果,而实际请求和解析都是以可插拔的插件形式存在,灵活度非常高。

2. 创建服务到建立Call过程分析

关于 Retrofit的构建 ,我们注意一下必填参数以及默认参数即可,根据如下Retrofit.Build#build源码可知:

  • baseUrl必填
  • 默认callFactoryOkHttpClient;默认callbackExecutor(回调执行器)在Android中是主线程的Handler;默认会先添加Retrofit内部的转换器,然后是其他,比如我们自定义的转换器,这是为了避免内部转换器的行为被复写掉,以及确保使用消耗(consume)所有类型的转换器时能有正确的行为。
public Retrofit build() {
    // baseUrl必填
    if (baseUrl == null) {
        throw new IllegalStateException("Base URL required.");
    }
    // 默认Call工厂为 OkHttpClient
    okhttp3.Call.Factory callFactory = this.callFactory;
    if (callFactory == null) {
        callFactory = new OkHttpClient();
    }
    // 默认回调执行器为主线程Handler
    Executor callbackExecutor = this.callbackExecutor;
    if (callbackExecutor == null) {
        callbackExecutor = platform.defaultCallbackExecutor();
    } 
    List<CallAdapter.Factory> callAdapterFactories = new ArrayList<>(this.callAdapterFactories);
    callAdapterFactories.addAll(platform.defaultCallAdapterFactories(callbackExecutor));
 
    List<Converter.Factory> converterFactories = new ArrayList<>(
        1 + this.converterFactories.size() + platform.defaultConverterFactoriesSize()); 
    // 这里会先添加Retrofit内部的转换器再添加我们自定的转换器
    converterFactories.add(new BuiltInConverters());
    converterFactories.addAll(this.converterFactories);
    converterFactories.addAll(platform.defaultConverterFactories());
    // ...
}

这里关注一下Android 平台的回调执行器,因为回调执行在主线程的Handler上,因此可以在回调中直接操作UI控件。

static class Android extends Platform {
    @Override public Executor defaultCallbackExecutor() {
        return new MainThreadExecutor();
    }
    static class MainThreadExecutor implements Executor {
        // UI线程
        private final Handler handler = new Handler(Looper.getMainLooper());
        @Override public void execute(Runnable r) {
            handler.post(r);
        }
    }
    // ...
}

接着来分析一下使用Retrofit#create创建一个请求服务实例时发生了什么Retrofit#create源码如下,可知:

  • 首先需要确定的是service本身是个接口,并且不继承于其他接口。

  • 然后重点来了,eagerlyValidateMethods会通过反射获取service接口中所有的方法,接着尝试从ServiceMethod缓存池中查找对应于各个方法的ServiceMethod,如果没找到的话,则重新通过ServiceMethod.parseAnnotations去解析各个方法的注解,解析完成后将返回的ServiceMethod(这里返回的ServiceMethod其实是实现类HttpServiceMethodHttpServiceMethod会负责根据解析的注解参数创建Call,并在HttpServiceMethod#invoke调用时执行网络请求)存入缓存池中,方便后续复用,这里缓存池的作用跟线程池的概念异曲同工,都是为了减少因为每次都解析(创建)而造成的不必要的性能损耗,所以干脆花点内存存起来省事儿。eagerlyValidateMethods执行过程如下:

    eagerlyValidateMethods执行过程
  • 接着通过Proxy.newProxyInstance给服务接口创建一个代理实例,实际可转成对应接口的类型,这里主要关注一下InvocationHandler, 每个Proxy对象实例都会绑定一个InvocationHandler对象,当执行Proxy#invok方法时,最终对派发给InvocationHandler#invok,也就是说,我们通过服务接口实例调用接口方法时,最终都会通过InvocationHandler#invok去执行。invoke方法执行链如下:

    invoke执行链
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();
          private final Object[] emptyArgs = new Object[0];

          @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);
            }
            // 这里实际最终执行的是 HttpServiceMethod#invoke(..)
            return loadServiceMethod(method).invoke(args != null ? args : emptyArgs);
          }
        });
}

private void eagerlyValidateMethods(Class<?> service) {
    Platform platform = Platform.get();
    for (Method method : service.getDeclaredMethods()) {
        // 在 Android 7.0 以前版本都是 false,Android 7.0 及以上则根据 `isDefaultMethod`的复写值决定
      if (!platform.isDefaultMethod(method)) {
        loadServiceMethod(method);
      }
    }
}

ServiceMethod<?> loadServiceMethod(Method method) { 
    ServiceMethod<?> result = serviceMethodCache.get(method);
    if (result != null) return result;

    synchronized (serviceMethodCache) {
        // 先从服务方法缓存中获取
      result = serviceMethodCache.get(method);
      if (result == null) {
        // 如果此前没有解析,则重新解析注解
        result = ServiceMethod.parseAnnotations(this, method);
        // 然后将解析结果添加到缓存,以便后续复用
        serviceMethodCache.put(method, result);
      }
    }
    return result;
}

上面的 parseAnnotations 执行链如下:

parseAnnotations执行链

我们顺着这条链看看,首先是ServiceMethod#parseAnnotations

abstract class ServiceMethod<T> {
  static <T> ServiceMethod<T> parseAnnotations(Retrofit retrofit, Method method) {
    // 1. 解析方法的注解参数,保存在 RequestFactory
    RequestFactory requestFactory = RequestFactory.parseAnnotations(retrofit, method);
    // ...
    // 2. 使用将上面解析的参数建立Call,用于网络请求
    return HttpServiceMethod.parseAnnotations(retrofit, method, requestFactory);
  }
}

接着是RequestFactory#parseAnnotations,源码如下,主要做了三件事情,看注释即可:

final class RequestFactory {
  static RequestFactory parseAnnotations(Retrofit retrofit, Method method) {
    return new Builder(retrofit, method).build();
  }
  static final class Builder{
    RequestFactory build() {
      // 1. 解析每个方法的注解
      for (Annotation annotation : methodAnnotations) {
        parseMethodAnnotation(annotation);
      }
      // ... 
      int parameterCount = parameterAnnotationsArray.length;
      parameterHandlers = new ParameterHandler<?>[parameterCount];
      // 2. 解析方法参数
      for (int p = 0; p < parameterCount; p++) {
        parameterHandlers[p] = parseParameter(p, parameterTypes[p], parameterAnnotationsArray[p]);
      }
      // ...
      // 3. 创建 RequestFactory 保存参数
      return new RequestFactory(this);
    }
  }
}  

接着是 HttpServiceMethod#parseAnnotations,源码如下:

static <ResponseT, ReturnT> HttpServiceMethod<ResponseT, ReturnT> parseAnnotations(
      Retrofit retrofit, Method method, RequestFactory requestFactory) {
    // 1. 获取 Call 适配器
    CallAdapter<ResponseT, ReturnT> callAdapter = createCallAdapter(retrofit, method);
    Type responseType = callAdapter.responseType();
    // 2. 获取响应数据转换器
    Converter<ResponseBody, ResponseT> responseConverter = createResponseConverter(retrofit, method, responseType);
    okhttp3.Call.Factory callFactory = retrofit.callFactory;
    // 3. 根据解析的参数创建 HttpServiceMethod
    return new HttpServiceMethod<>(requestFactory, callFactory, callAdapter, responseConverter);
}

HttpServiceMethod#invok执行时源码如下:

  @Override ReturnT invoke(Object[] args) {
    // 创建一个 OkHttpCall, 用于进行网络请求和响应数据转换
    return callAdapter.adapt(
        new OkHttpCall<>(requestFactory, args, callFactory, responseConverter));
  }

至此,便是一个服务接口从解析到创建成一个OkHttp#Call的过程,纵观全局,其实这个过程就好比一个为了将如下接口:

public interface Service{ 
    @GET("article/list/{page}/json")
    public Call<ResponseEntry<ResponseData>> getHomeList(@Path("page")int page);
} 

解析成一个请求链接为http://wanandroid.com/article/list/0/json,请求方式为 GET,请求的调用方式为:

Service service = ...;
// 相当于执行 HttpServiceMethod#invoke 方法
Call<ResponseEntry<ResponseData>> = service.getHomeList(0);

的过程,而这个过程中需要解决将接口转换成对象实例、将方法注解、参数解析处理拼接为请求连接、最后确定返回类型的问题,此时Call尚未进行请求;

3. Call请求执行到响应数据回调过程分析

关于OkHttp#Call如何运作的问题已经在 OkHttp源码解析 一文中做了详细分析,这里的不同之处在于,在Retrofit中我们需要更多地关注它是如何协调请求和响应,最终回调给UI线程的。

OK,从HttpServiceMethod#invoke出发,根据前面的内容中我们已经知道它会通过callAdapter.adapt(new OkHttpCall<>(requestFactory, args, callFactory, responseConverter))返回一个Call实例,并且在Android平台上会将响应数据回调在UI线程的Handler上,因此我们先关注一下Android平台下的默认CallAdapter,于是定位到Android#defaultCallAdapterFactories

@Override List<? extends CallAdapter.Factory> defaultCallAdapterFactories(
    @Nullable Executor callbackExecutor) {
    if (callbackExecutor == null) throw new AssertionError();
    return singletonList(new ExecutorCallAdapterFactory(callbackExecutor));
} 

可见Android平台下的默认CallAdapterExecutorCallAdapterFactory, 于是可以定位到ExecutorCallAdapterFactory#adapt

@Override public Call<Object> adapt(Call<Object> call) {
    return new ExecutorCallbackCall<>(callbackExecutor, call);
} 

ExecutorCallbackCall这里实际是使用了装饰器模式,它将工作委托给了callbackExecutordelegate,而它自身仅仅起到了协调作用,将响应数据回调到UI线程:

 static final class ExecutorCallbackCall<T> implements Call<T> {
    final Executor callbackExecutor;
    final Call<T> delegate;

    ExecutorCallbackCall(Executor callbackExecutor, Call<T> delegate) {
      this.callbackExecutor = callbackExecutor;
      this.delegate = delegate;
    } 
    @Override public void enqueue(final Callback<T> callback) {
      checkNotNull(callback, "callback == null");
      delegate.enqueue(new Callback<T>() {
        @Override public void onResponse(Call<T> call, final Response<T> response) {
          // 回调至主线程
          callbackExecutor.execute(new Runnable() {
            @Override public void run() {
              if (delegate.isCanceled()) { 
                callback.onFailure(ExecutorCallbackCall.this, new IOException("Canceled"));
              } else {
                callback.onResponse(ExecutorCallbackCall.this, response);
              }
            }
          });
        }   // ...
      });
    }   // ...
  }

具体到网络请求的执行与响应数据的转换工作还得看OkHttpCall,这里我们只关注一下OKHttpCall#enqueue即可, 可见这里除了请求网络数据外,还会先转换响应数据后再回调给上一级:

  @Override public void enqueue(final Callback<T> callback) {
    
    okhttp3.Call call;
    Throwable failure;
    // 1. 执行请求
    call.enqueue(new okhttp3.Callback() {
      @Override public void onResponse(okhttp3.Call call, okhttp3.Response rawResponse) {
        Response<T> response;
        try {
            // 2. 解析响应数据,将网络响应数据转换成指定数据类型
          response = parseResponse(rawResponse);
        } catch (Throwable e) { 
            // ...
          return;
        } 
        try {
            // 3. 将解析完成的数据回调给上一级
          callback.onResponse(OkHttpCall.this, response);
        } catch (Throwable t) {
            // ...
        }
      }
     // ... 
    });
  } 

然后parseResponse部分源码如下,可见这里会通过Converter网络响应数据转换为我们指定的数据类型:

  Response<T> parseResponse(okhttp3.Response rawResponse) throws IOException {
    ResponseBody rawBody = rawResponse.body(); 
    // Remove the body's source (the only stateful object) so we can pass the response along.
    rawResponse = rawResponse.newBuilder()
        .body(new NoContentResponseBody(rawBody.contentType(), rawBody.contentLength()))
        .build();
    // ... 
    ExceptionCatchingResponseBody catchingBody = new ExceptionCatchingResponseBody(rawBody);
    try {
      // 通过转换器转换数据 
      T body = responseConverter.convert(catchingBody);
      return Response.success(body, rawResponse);
    } catch (RuntimeException e) {
      // ...
    }
  }

综上可知,最终网络请求会在OkHttpCall中执行,获取响应数据后通过设定的Converter转换器将数据转换成指定类型;而最终回调给UI线程则是在ExecutorCallbackCall中进行,作为装饰器,它实际将请求和响应数据处理工作都委托给了OkHttpCall,而自身仅仅做了最终数据的回调处理。

于是整体执行流程如下:

整体执行流程

三、Proxy

这里指的是反射工具类中的java.lang.reflect.Proxy,通过前面的分析,我们已经知道,我们建立的服务接口会通过Proxy.newProxyInstance来实例化一个代理对象实例,而通过这个实例化的对象,就能像使用普通类对象实例一个调用方法。

这里我比较好奇的是它是如何给接口实例化的,因此咱就来研究研究,定位到Proxy#newProxyInstance,精简一下源码(去除了验证逻辑等),如下,可以发现Proxy会为我们的服务接口构建一个代理类(当然会先从代理类缓存,也就是WeakCache中查找已经构建的代理类),然后通过这个类的构造函数构建出一个实例对象出来:

    public static Object newProxyInstance(ClassLoader loader,
                                          Class<?>[] interfaces,
                                          InvocationHandler h)
        throws IllegalArgumentException
    { 
        final Class<?>[] intfs = interfaces.clone(); 
        // 1. 从 `WeakCache`中查找,或者创建一个接口的代理类 
        Class<?> cl = getProxyClass0(loader, intfs);
        // 2. 拿到代理类的构造函数
        final Constructor<?> cons = cl.getConstructor(constructorParams);
        final InvocationHandler ih = h; 
        // ...
        // 3. 通过构造函数创建一个实例对象
        return cons.newInstance(new Object[]{h});
    }

再来看看getProxyClass0(), 根据代码注释可知,如果根据类加载器查找已经实现的代理类,那么直接返回拷贝的缓存,如果没找到,那么就会通过ProxyClassFactory去创建一个代理类。

private static Class<?> getProxyClass0(ClassLoader loader,
                                        Class<?>... interfaces) {
    if (interfaces.length > 65535) {
        throw new IllegalArgumentException("interface limit exceeded");
    }
    return proxyClassCache.get(loader, interfaces);
}

于是再来看看ProxyClassFactory,可知通过其apply方法会根据我们服务接口的信息配置代理类,然后通过ProxyGenerator生成一个代理类class文件,最终通过defineClass0将这个代理类定义出来:

private static final class ProxyClassFactory
    implements BiFunction<ClassLoader, Class<?>[], Class<?>>
{
    private static final String proxyClassNamePrefix = "$Proxy";
    @Override
    public Class<?> apply(ClassLoader loader, Class<?>[] interfaces) {

        Map<Class<?>, Boolean> interfaceSet = new IdentityHashMap<>(interfaces.length);
        // ... 
        int accessFlags = Modifier.PUBLIC | Modifier.FINAL;
        // 1. 进行一系列的代理类信息的配置
        //... 
        // 2. 根据配置信息生成代理类class文件
        byte[] proxyClassFile = ProxyGenerator.generateProxyClass(proxyName, interfaces, accessFlags);
        // 3. 最终生成特定代理类
        return defineClass0(loader, proxyName, proxyClassFile, 0, proxyClassFile.length);
    }
}

这里的defineClass0是个native方法,因此就不再深挖了:

  private static native Class<?> defineClass0(ClassLoader loader, String name, byte[] b, int off, int len);

至此,是不是已经明白了Proxy如何实例化接口的呢?

四、总结

通过上面的分析,可以发现 Retrofit 更像是对一个OkHttp请求的抽取与封装:

  • 网络请求参数全部抽离成服务接口方法的注解,注解参数解析和Request构建工作抽离到了RequestFactory
  • CallAdapterOkHttpCall的执行匹配到我们指定的执行器,而Converter则将网络响应数据转换成我们想要的类型
  • 最终,在Android平台上直接将指定的数据类型返回给UI线程的Handler处理。

关于Proxy,它将服务接口转换成一个代理类对象实例的实现方式也很值得我们学习。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,088评论 5 459
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,715评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,361评论 0 319
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,099评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 60,987评论 4 355
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,063评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,486评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,175评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,440评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,518评论 2 309
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,305评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,190评论 3 312
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,550评论 3 298
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,880评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,152评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,451评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,637评论 2 335

推荐阅读更多精彩内容