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> getHomeList(@Path("page")int page);
}

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

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

                if (response.isSuccessful()){
                    ResponseEntry body = response.body();
                    if (body == null) {
                        callback.onFailed(new Exception("body is null !!"));
                        return;
                    }
                    callback.onSuccess(body);
                }
            }
            @Override
            public void onFailure(Call> 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 callAdapterFactories = new ArrayList<>(this.callAdapterFactories);
    callAdapterFactories.addAll(platform.defaultCallAdapterFactories(callbackExecutor));
 
    List 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 create(final Class 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 {
  static  ServiceMethod 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  HttpServiceMethod parseAnnotations(
      Retrofit retrofit, Method method, RequestFactory requestFactory) {
    // 1. 获取 Call 适配器
    CallAdapter callAdapter = createCallAdapter(retrofit, method);
    Type responseType = callAdapter.responseType();
    // 2. 获取响应数据转换器
    Converter 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> getHomeList(@Path("page")int page);
} 

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

Service service = ...;
// 相当于执行 HttpServiceMethod#invoke 方法
Call> = 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 defaultCallAdapterFactories(
    @Nullable Executor callbackExecutor) {
    if (callbackExecutor == null) throw new AssertionError();
    return singletonList(new ExecutorCallAdapterFactory(callbackExecutor));
} 

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

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

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

 static final class ExecutorCallbackCall implements Call {
    final Executor callbackExecutor;
    final Call delegate;

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

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

  Response 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[], Class>
{
    private static final String proxyClassNamePrefix = "$Proxy";
    @Override
    public Class apply(ClassLoader loader, Class[] interfaces) {

        Map, 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,它将服务接口转换成一个代理类对象实例的实现方式也很值得我们学习。

你可能感兴趣的:(Retrofit源码解析)