Retrofit 源码分析:代码结构

好的代码是自解释的,Retrofit 可以算是好的代码了。

Retrofit 的依赖图
《Retrofit 源码分析:代码结构》

一、无中生有:从接口方法到Request实例

ServiceMethod.java

将 API接口 中定义的方法方法转化为 HTTP 请求。接口中描述的方法是没有方法体的,所以最终执行的时候需要根据接口信息去组织特定的方法和对象,以产生可以执行的代码。

@Headers({
        "X-Foo: Bar",
        "X-Ping: Pong"
})
@POST("/abc")
Call<ResponseBody> getSomething(@Body String body);

ServiceMethod 虽然没有继承自 java.lang.reflect.Method,但是同样提供了执行方法的途径。由内部类 Builder 中的 parseMethodAnnotation(Annotation annotation)/*参见 Retrofit源码分析:方法注解*/ , parseHttpMethodAndPath(String httpMethod, String value, boolean hasBody) , parseHeaders(String[] headers) , parseParameter(...) , parseParameterAnnotation(...) 等方法来转换成一个“有血有肉”的 OkHttp 请求。

一段很有意思的代码:

  • @Path 注解表示的 URL 不能和参数化查询混淆(形如/{name}/address?type=home)#294 ~ #303
int question = value.indexOf('?');
if (question != -1 && question < value.length() - 1) {
  // Ensure the query string does not have any named parameters.
  String queryParams = value.substring(question + 1);
  Matcher queryParamMatcher = PARAM_URL_REGEX.matcher(queryParams); //REGEX: {([a-zA-Z][a-zA-Z0-9_-])*}
  if (queryParamMatcher.find()) {
    throw methodError("URL query string \"%s\" must not have replace block. "
        + "For dynamic query parameters use @Query.", queryParams);
  }
}

RequestBuilder.java

负责构建 Request 对象的具体实现。分离职责

ServiceMethod 中有个 “根据方法参数构建 HTTP 请求” 的方法,就是把从接口方法中解析到的数据传到这里来创建请求的。目前只有构建 Request 实例的过程中用到了这个类。

主要有下面这些方法,这些方法都是 ParameterHandler 在用。

使用 IntelliJ / Android Studio 快捷键 alt+f7 可以快速查找依赖 (要把 作用域Scope 设置为 Project and Libraries )

《Retrofit 源码分析:代码结构》

ParameterHandler.java

负责处理参数,是具体实现。分离职责

里面有一些内部类:RelativeUrl , Header , Path , Query , QueryName , QueryMap , HeaderMap , Field , FieldMap , Part , RawPart , PartMapBody 等内部类。

这些内部类都继承自 ParameterHandler , 并重写了 apply() (也是唯一一个可供重写的方法)。这些类的实例供 ServiceMethod 使用。

目前只有构建 Request 实例的过程中用到了这个类。

二、 天堑变通途,Request 和 Response

Call.java

每次调用 Retrofit 方法会向服务器发起请求并得到一个响应。

在 Retrofit 中,仅有一个实现类:OkHttpCall。Retrofit 通过动态代理使用的也有一个类,即所有的 Retrofit中的所有接口方法使用的都是 OkHttpCall 。见 Retrofit 中的 public <T> T create(final Class<T> service) 方法。

接口共有七个方法,Response<T> execute()void enqueue(Callback<T> callback) 分别同步、异步发出请求;void cancle() 用来取消请求; boolean isExecuted()boolean isCanceled() 用来获取当前请求的状态;Retrofit 中每个请求只能执行一次,当需要轮询或者失败重传的时候,可以使用 Call.clone() 来产生同样的请求;Request request() 可以获取生成的 Request 对象(并没有执行 request,而是一个 getter 方法, retrofit 中好多(可能是所有) getter 方法都是这种风格的,方法名不以 get 开头)。

OkHttpCall.java

Call 的唯一实现类

executeenqueue 的代码片段(一个 Call 只能执行一次):

if (executed) throw new IllegalStateException("Already executed.");
executed = true;

executeenqueue 函数的结构很相似,拿 enqueue 来说(移除了错误处理代码,并 inline 了部分函数):

@Override public void enqueue(final Callback<T> callback) {
  okhttp3.Call call;
  synchronized (this) {
    call = rawCall;  // rawCall 是全局变量
    if (canceled) { call.cancel(); }
  
    call.enqueue(new okhttp3.Callback() {
      @Override public void onResponse(okhttp3.Call call, okhttp3.Response rawResponse throws IOException {
        Response<T> response = parseResponse(rawResponse);
      }
      callback.onResponse(OkHttpCall.this, response);
    }
    @Override public void onFailure(okhttp3.Call call, IOException e) {
        callback.onFailure(OkHttpCall.this, e);
    }
}

可以看到这个方法中进行了一次复制,使用原来的 call 并间接调用了 callback 。为什么要这样多此一举呢?这跟 Retrofit 请求道路的曲折性是分不开的。当 通过 OkHttpCall(ServiceMethod<T, ?> serviceMethod, @Nullable Object[] args) 这个构造方法创建对象后,得到的 call 对象就是个简单的对象而已,无法完成网络请求,需要在内部通过callFactory 创建。

createRawCall 根据构造函数中传入的 args , 创建新的 request ,并根据 request 创建 Call (代码中出现的 callFactory 默认情况下是 Retrofit 创建的 ,真正的实例是 OkHttpClient ,这个类实现了 Call.Factory 接口 。OkHttpClient 中的实现是 return RealCall.newRealCall(this, request, false /* for web socket */) 。很明显,真正进行请求的是RealCall ,代码中的 rawCall 的类型一般情况就是 RealCall 啦!这也就说通了为什么会有这么一层包装,因为 OkHttpCall 就是个中间层!):

private okhttp3.Call createRawCall() throws IOException {
  Request request = serviceMethod.toRequest(args);
  okhttp3.Call call = serviceMethod.callFactory.newCall(request);
  if (call == null) {
    throw new NullPointerException("Call.Factory returned null.");
  }
  return call;
}

解析响应 response , 所有的分支都是 return Response.success/error(...) 的形式, Response 类中 successerror 的几个静态重载方法会返回带有泛型的 Response

Response<T> parseResponse(okhttp3.Response rawResponse) throws IOException {
  // 不是所有的响应 body 都有意义,这里先把它拿掉
  ResponseBody rawBody = rawResponse.body();
  rawResponse = rawResponse.newBuilder()
      .body(new NoContentResponseBody(rawBody.contentType(), rawBody.contentLength()))
      .build();
  int code = rawResponse.code();
  if (code < 200 || code >= 300) {  // “非成功”状态码
    try {  // 
      ResponseBody bufferedBody = Utils.buffer(rawBody);  // wtf…
      return Response.error(bufferedBody, rawResponse);  // 返回出错响应
    } finally {
      rawBody.close();
    }
  }
  if (code == 204 || code == 205) {  // 服务器成功处理了请求,没有返回任何内容。
    rawBody.close();
    return Response.success(null, rawResponse);   // 返回成功响应,body 为 null
  }
  // 2xx but 204 205, 用到了之前取出的 body
  ExceptionCatchingRequestBody catchingBody = new ExceptionCatchingRequestBody(rawBody);
  try {
    T body = serviceMethod.toResponse(catchingBody);
    return Response.success(body, rawResponse);  // 我们最关心的一行
  } catch (RuntimeException e) {
    catchingBody.throwIfCaught();
    throw e;
  }
}

Callback.java

对于绝大多数 Android开发者来说,这个接口是相当熟悉的。

onResponse & onFailure 分别对应请求成功和失败

Response.java

okhttp3.Response 的一个简单封装

包含一些 getter 方法(delegate)和几个用来创建 Response 的静态方法(successerror 的重载方法)。

三、Factory、Adapter、Converter

Converter.java

此接口包含了一个方法:T convert(F value) 和一个抽象内部类 FactoryFactory 产生特定的 Converter

Factory 有三个可供重写的方法:

  • responseBodyConverter(Type type,Annotation[] annotations, Retrofit retrofit)
  • requestBodyConverter(Type type, Annotation[] parameterAnnotations, Annotation[] methodAnnotations, Retrofit retrofit)

    返回处理 HTTP Request 的 Converter ,某种类型(在接口方法的声明中一般被 PartBodyPartMap 修饰)的对象 –> HTTP Request Body

  • stringConverter(Type type, Annotation[] annotations, Retrofit retrofit)

    返回将某种类型转化为 StringConverter ,某种类型(在接口方法的声明中一般被 Field/FieldMapHeader/HeaderMapQuery/QueryMapPath 修饰) –> String 。因为 HTTP 1.1 的 URL / Body / Header 是建立在字符串上的

对于不能/不想处理的数据类型,直接返回 null 就可以了(这也是这些方法的默认实现)。Retrofit 将继续查找适用的 Converter 或者采用一个默认实现。

BuiltInConverters.java

  • VoidResponseBodyConverter.class
  • RequestBodyConverter.class
  • StreamingResponseBodyConverter.class
  • BufferingResponseBodyConverter.class
  • ToStringConverter.class

CallAdapter.java

比如我们本来只能写返回 Call<Foo> 这样的方法,但是在 CallAdapterRxJava2CallAdapterFactory , 'com.squareup.retrofit2:adapter-rxjava2:2.3.0')的帮助下,我们可以写返回 Observable<UsersResponse>这种的方法。

同样有个名为 Factory 的内部类

  • T adapt(Call<R> call); /* Call<R> --> Foo<R> */

返回一个真正执行(delegate,代理)这个 Call 的实例。比如:

@Override
public <R> Async<R> adapt(final Call<R> call) {
  return Async.create(new Callable<Response<R>>() {
    @Override
    public Response<R> call() throws Exception {
      return call.execute();
    }
  });
}
  • Type responseType();

举个简单的例子,如果接口声明的是 Call<Foo> ,那么这里应该返回 Foo

Retrofit 内置的两个 CallAdapter ,都有这样的代码:

if (getRawType(returnType) != Call.class) {
  return null;
}

意味着默认情况下仅仅能处理 Call<Foo> 这种返回值的接口方法

DefaultCallAdapterFactory.java

创建那种 Adapter,就是 I/O 和 Callback 执行在同一个线程上那种

由于是这里的 I/O 几乎等于网络请求,所以 Android 一般情况下是不会用到这个的。

ExecutorCallAdapterFactory.java

这个类的adapt(...) 方法将 call 外包给自己的内部类:ExecutorCallbackCall

看到这个 Executor ,em……, 没错,就是 java.util.concurrent.Executor

四、Retrofit、Platform

##Retrofit.java

Retrofit , Retrofit, Retrofit

一切的入口,Retrofit 家的门面

  • 构造函数
Retrofit(okhttp3.Call.Factory callFactory, HttpUrl baseUrl,    List<Converter.Factory> converterFactories, List<CallAdapter.Factory> adapterFactories,    @Nullable Executor callbackExecutor, boolean validateEagerly);
  • public Builder newBuilder()

    em…… new Retrofit.Builder().build() 就是 Retrofit 了 。

  • public <T> T create(final Class<T> service)

    根据接口信息使用反射生成对应的对象,要求 service.isInterface() 并且不能继承其他接口。

  • private void eagerlyValidateMethods(Class<?> service)

    如果 validateEagerlytrue,Retrofit将在反射之前来验证并加载接口中的方法。

  • ServiceMethod<?, ?> loadServiceMethod(Method method)

    java.lang.reflect.Method 加载为 retrofit2.ServiceMethod

  • callAdapter 相关

    public CallAdapter<?, ?> callAdapter(Type returnType, Annotation[] annotations);
    public CallAdapter<?, ?> nextCallAdapter(@Nullable CallAdapter.Factory skipPast, Type returnType, Annotation[] annotations);
  • converter 相关

    // a naive getter
    public List<Converter.Factory> converterFactories();
    // 根据参数查找一个合适的 Converter
    public <T> Converter<T, RequestBody> requestBodyConverter(Type type,
          Annotation[] parameterAnnotations, Annotation[] methodAnnotations);
    public <T> Converter<T, RequestBody> nextRequestBodyConverter(
          @Nullable Converter.Factory skipPast, Type type, Annotation[] parameterAnnotations,
          Annotation[] methodAnnotations);
    public <T> Converter<ResponseBody, T> responseBodyConverter(Type type, Annotation[] annotations);
    public <T> Converter<ResponseBody, T> nextResponseBodyConverter(
          @Nullable Converter.Factory skipPast, Type type, Annotation[] annotations);
    public <T> Converter<T, String> stringConverter(Type type, Annotation[] annotations);
  • getters 相关

    public HttpUrl baseUrl();
    public okhttp3.Call.Factory callFactory();
    public List<CallAdapter.Factory> callAdapterFactories();
    public List<Converter.Factory> converterFactories();
    public @Nullable Executor callbackExecutor();

简单看下 create 方法:

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();
        @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)) {    // 默认为 false,Android 下为 fasle
            return platform.invokeDefaultMethod(method, service, proxy, args);
          }
          // 根据接口方法信息构建等价的 ServiceMethod
          ServiceMethod<Object, Object> serviceMethod =
              (ServiceMethod<Object, Object>) loadServiceMethod(method);
          // 根据 ServiceMethod 构建响应的 Call
          OkHttpCall<Object> okHttpCall = new OkHttpCall<>(serviceMethod, args);
          return serviceMethod.callAdapter.adapt(okHttpCall); // 这里是个 Object, 如果接口方法返回值类型为Call<Foo>,则此返回值的类型为 Foo 
        }
      });
}

Platform.java

不同的平台有不同的行为,上面提到的 adapterconverter 等细节方面也是存在差异的。比如 Android 平台在系统层面上限制在 UI 线程上进行网络请求。

这个类有两个内部类:Java8 extends PlatformAndroid extends Platform

对于 Java8,跟 callback 有关的是默认实现:

@Nullable Executor defaultCallbackExecutor() {
  return null;  // 没有回调 Executor
}
CallAdapter.Factory defaultCallAdapterFactory(@Nullable Executor callbackExecutor) {
  if (callbackExecutor != null) {  // false
    return new ExecutorCallAdapterFactory(callbackExecutor);
  }
  return DefaultCallAdapterFactory.INSTANCE;   // 所以使用的是 DefaultCallAdapterFactory
}

而对于 Android,对应的实现则是这样的:

@Override public Executor defaultCallbackExecutor() {
  return new MainThreadExecutor();  // 有回调 Executor
}
@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) {
    // Callback 在 UI 线程上执行 , 
    // 不然可能就 android.view.ViewRoot$CalledFromWrongThreadException: 
    // Only the original thread that created a view hierarchy can touch its views.
    handler.post(r);  
  }
}

对着代码中嵌入的文档,和胡猜各种命名,终于把这个项目捋下来了。
可能是市面上相当详细全面的一篇了,当然很多地方还是不够详细。因为代码实在是不少,贴进来肯定是一篇“巨作”。但是结构还算清晰,如果你也感兴趣,打开你的 Android Studio / IntelliJ Idea 也来看看吧!

Ref:

via boileryao

Happy coding :)

    原文作者:Android源码分析
    原文地址: https://juejin.im/entry/5a78904ff265da4e7a783651
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞