目录

一个专注音视频领域的小圈子

分析一波 Retrofit 的源码实现。

简单使用

官方给出了使用例子,具体详情参看 官网。

如果加入 RxJava 和 Gson ,就需要添加对应的适配和转换工厂类。

val retrofit = Retrofit.Builder()
    .baseUrl("https://api.github.com/")
    .addCallAdapterFactory(RxJavaCallAdapterFactory.create())
    .addConverterFactory(GsonConverterFactory.create())
    .build()

围绕 Retrofit 的调用流程分析它的内部实现。

Retrofit 类的构建

首先是 Retrofit 类的构建,通过建造者模式来实现。

在 Retrofit.Builder 类的调用方法中指定一些参数,就比如上面的 RxJava 和 Gson 对应的工厂类。

在 build 方法里面可以看到全部的所需要的内容:

    public Retrofit build() {
      // HTTP 请求的 host 路径
      if (baseUrl == null) {
        throw new IllegalStateException("Base URL required.");
      }
      // 用来执行请求的工厂,若没有设置就是使用 OKHttpClient
      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> callAdapterFactories = new ArrayList<>(this.callAdapterFactories);
      callAdapterFactories.add(platform.defaultCallAdapterFactory(callbackExecutor));
      // 数据转换器的工厂
      // Make a defensive copy of the converters.
      List<Converter.Factory> converterFactories =
          new ArrayList<>(1 + this.converterFactories.size());
      // Add the built-in converter factory first. This prevents overriding its behavior but also
      // ensures correct behavior when using converters that consume all types.
      converterFactories.add(new BuiltInConverters());
      converterFactories.addAll(this.converterFactories);
      // 创建 Retrofit 实例
      return new Retrofit(callFactory, baseUrl, unmodifiableList(converterFactories),
          unmodifiableList(callAdapterFactories), callbackExecutor, validateEagerly);
    }

在 Build 创建 Retrofit 时有如下要注意的东西:

  • callFactory : 网络请求工厂
  • adapterFactories : 网络请求适配器工厂的集合
  • converterFactories : 数据转换器工厂的集合
  • callbackExecutor : 回调方法执行器

这四个东西可以说是 Retrofit 中很重要的内容了,它们在 Retrofit 类中都是接口或抽象类类型,将定义与实现进行分离,可以做到面向接口编程。

Retrofit 是对网络请求的封装库,一个网络请求的过程大致可以抽象如下:

创建网络请求 --> 发出网络请求 --> 收到请求内容 --> 对内容进行处理

而 Retrofit 可以说是对上面的流程都进行了抽象,抽象出一些类和接口,这些抽象类或者接口的调用就是实现上面的请求流程。

adapterFactories 就是对创建的网络请求适配的集合,比如默认请求是 Call,但是可以通过 RxJavaCallAdapterFactory 将网络请求适配到 RxJava 的 Observable 。

callFactory 就是用来执行网络请求的,将请求发出去,如果没有指定该变量,则默认使用 OkHttpClient 来完成该功能。

converterFactories 就是将请求后的内容转换成指定的格式的集合,如果服务器返回的是 GSON 格式,那么就可以指定 GsonConverterFactory 将 HTTP 的 Response 内容转换 GSON 对象。

callbackExecutor 就是在网络请求结束后,从子线程切换到对数据操作的线程,主要就是切换到主线程。

依照网络请求流程,上面四个重点变量在请求中出现的先后顺序也是如下:

adapterFactories --> callFactory --> converterFactories --> callbackExecutor

另外,Retrofit 还是支持多平台的,除了 Android 平台之外,还有 Java 等平台,在 Retrofit.Builder 的构造方法中就对当前平台做了一个判断,这里就只看 Android Platform 就好了。

弄懂了大致的脉络,接着再来看实际代码。

如果没有指定请求后的线程切换操作,那么就会使用 defaultCallbackExecutor 。 Android 下默认的就是 MainThreadExecutor ,通过主线程的 Handler 来实现线程切换。

    static class MainThreadExecutor implements Executor {
      private final Handler handler = new Handler(Looper.getMainLooper());
      // 向主线程 post 一个 runnable 实现切换
      @Override public void execute(Runnable r) {
        handler.post(r);
      }
    }

如果没有添加网络请求适配器对象,那么就会使用默认的 defaultCallAdapterFactory 。Android 下默认的就是 ExecutorCallAdapterFactory ,

它的主要代码如下:

  @Override
  public CallAdapter<?, ?> get(Type returnType, Annotation[] annotations, Retrofit retrofit) {
    if (getRawType(returnType) != Call.class) {
      return null;
    }
    final Type responseType = Utils.getCallResponseType(returnType);
    // 返回 CallAdapter 对象进行适配
    return new CallAdapter<Object, Call<?>>() {
      @Override public Type responseType() {
        return responseType;
      }
      // 具体适配函数
      @Override public Call<Object> adapt(Call<Object> call) {
        // 返回 ExecutorCallbackCall 函数,因为就是对 call 的一个委托
        return new ExecutorCallbackCall<>(callbackExecutor, call);
      }
    };
  }

重点在于返回 CallAdapter 对象,它将网络请求进行适配,如果没有使用 RxJava 的话,那么返回的就是 ExecutorCallbackCall 。

如果没有指定请求结果转换对象,那么就会使用默认的 BuiltInConverters 类进行转换,BuiltInConverters 与请求适配和线程切换不同,它是和平台无关的。

在 adapterFactories 和 converterFactories 中还用到了工厂模式,通过工厂模式对具体执行操作的 CallAdapter 和 Converter 进行封装。

以上就完成了 Retrofit 对象的创建。

动态代理创建网络请求对象

接下来就是使用 Retrofit 类通过动态代理创建一个代理对象实例。

// 定义请求接口
public interface GitHubService {
  @GET("users/{user}/repos")
  Call<List<Repo>> listRepos(@Path("user") String user);
}
// 动态代理创建实现了请求接口的类
GitHubService service = retrofit.create(GitHubService.class);

动态代理的作用与实现,可以参考 这篇文章。

请求接口定义的所有方法,都会通过 InvocationHandler 中的 invoke 方法去分发处理。

也就是说,请求接口定义的返回类型是 Call ,那么 invoke 方法返回的也是 Call 类型。

主要代码如下:

        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)) {
              return platform.invokeDefaultMethod(method, service, proxy, args);
            }
            ServiceMethod<Object, Object> serviceMethod =
                (ServiceMethod<Object, Object>) loadServiceMethod(method);
            OkHttpCall<Object> okHttpCall = new OkHttpCall<>(serviceMethod, args);
            return serviceMethod.adapt(okHttpCall);
          }
        });

其中,method 就是对应接口中声明的方法,对每一个方法都会通过 loadServiceMethod 方法将它转换成 ServiceMethod 对象。

serviceMethod 会先从缓存 ConcurrentHashMap 中查找,如果没有在通过 ServiceMethod.Builder 创建一个。

创建时首先是如下两个方法:

    // 通过 method 的返回类型和 注解 找到对应的 callAdapter
    callAdapter = createCallAdapter();
    // 通过 method 的 请求返回类型 和 注解 找到对应的 responseConverter
    responseConverter = createResponseConverter();

这两个方法有点类似,在前面提到的 adapterFactories 适配器集合和 converterFactories 转换器集合都是列表。

要通过方法的返回类型、请求结果的返回类型、方法注解,为当前方法找到合适的适配器和转换器。

在这里可以看到,Retrofit 其实支持在请求接口中定义不同的返回类型,可以 Call 和 Observable 同时使用,也可以 GSON 和 Response 同时使用。

确定了请求适配和结果转换类之后,就是处理接口方法中定义的那些注解,分别是为该方法定义的注解和为该方法参数定义的注解。

      // 处理方法对应的注解
      for (Annotation annotation : methodAnnotations) {
        parseMethodAnnotation(annotation);
      }
      // 处理方法参数对应的注解
      int parameterCount = parameterAnnotationsArray.length;
      parameterHandlers = new ParameterHandler<?>[parameterCount];
      for (int p = 0; p < parameterCount; p++) {
          parameterHandlers[p] = parseParameter(p, parameterType, parameterAnnotations);
      }

关于注解处理这块比较冗长,文章篇幅有限,就不细写了,其实也主要是对那些 GET、POST、HEAD 等进行处理。

对这些注解的处理在最后创建 Call 用来发起网络请求时会用到。

当注解也处理完了之后,就创建了 serviceMethod 对象,接着创建 OkHttpCall 对象,这个对象就不是缓存的了,因为它就是用来执行请求的,每一次都要新建一个。

OkHttpCall 实现了 Call 接口,可以看做是对它的封装。Call 接口在 OkHttp 中就是用来发起网络请求的,它主要方法就是 execute 和 enqueue ,

然后 serviceMethod 对象通过 adapt 方法对 OkHttpCall 进行适配转换,并且返回了转换结果,如果在创建 Retrofit 时添加了 RxJava 的适配,那么返回的就是 Observable 类型。

这个 adapt 方法的适配其实就是由 serviceMethod 的 callAdapter 来实现的。

上面提到,默认是使用 ExecutorCallAdapterFactory 工厂来实现的。

最后,invoke 函数返回的就是把 OkHttpCall 经过 adapt 适配后的对象,默认情况下返回 ExecutorCallbackCall 对象。

执行网络请求

有了 ExecutorCallbackCall 对象之后,就可以像 OkHttp 那样去执行请求了。

        // 异步请求
        repos.enqueue(object : Callback<MutableList<Repo>> {
            override fun onFailure(call: Call<MutableList<Repo>>?, t: Throwable?) {
            }

            override fun onResponse(call: Call<MutableList<Repo>>?, response: Response<MutableList<Repo>>?) {
            }
        })
        // 同步请求
        repos.execute()

ExecutorCallbackCall 对象其实是对 OkHttpCall 的委托,具体的 enqueue 和 execute 方法都是由 OkHttpCall 来实现的。

先看一下同步调用

   private @Nullable okhttp3.Call rawCall;
   @Override public Response<T> execute() throws IOException {
    okhttp3.Call call;
    synchronized (this) {
      if (executed) throw new IllegalStateException("Already executed.");
      executed = true;
      call = rawCall;
      if (call == null) {
        try {
        // createRawCall 方法创建 call 
          call = rawCall = createRawCall();
        } catch (IOException | RuntimeException | Error e) {
        }
      }
    }
    // 取消请求
    if (canceled) {
      call.cancel();
    }
    // 解析请求内容
    return parseResponse(call.execute());
  }

实际是通过 createRawCall 方法来创建 Call 的,在 createRawCall 里面又主要是通过 serviceMethod.toCall 方法来完成的。

  okhttp3.Call toCall(@Nullable Object... args) throws IOException {
    // 用上了之前解析注解得到的结果
    RequestBuilder requestBuilder = new RequestBuilder(httpMethod, baseUrl, relativeUrl, headers,
        contentType, hasBody, isFormEncoded, isMultipart);
    // 处理请求的参数
    @SuppressWarnings("unchecked") // It is an error to invoke a method with the wrong arg types.
    ParameterHandler<Object>[] handlers = (ParameterHandler<Object>[]) parameterHandlers;

    int argumentCount = args != null ? args.length : 0;
    if (argumentCount != handlers.length) {
      throw new IllegalArgumentException("Argument count (" + argumentCount
          + ") doesn't match expected count (" + handlers.length + ")");
    }
    // 把注解和参数对应上
    for (int p = 0; p < argumentCount; p++) {
      handlers[p].apply(requestBuilder, args[p]);
    }
    // 最后还是通过 callFactory 来创建 Call 
    return callFactory.newCall(requestBuilder.build());
  }

在 toCall 方法里就用到了之前解析的注解内容,依然通过建造者模式创建请求,并且把请求的参数和对应的注解内容对应上,也就是 ParameterHandler 的 apply 方法。

最后是通过 callFactory 网络请求工厂来创建请求,默认就是使用 OkHttp 来创建。

parseResponse 方法会处理 Call 执行的结果,最终也是通过 serviceMethod.toResponse 方法来处理。

  R toResponse(ResponseBody body) throws IOException {
    return responseConverter.convert(body);
  }

在该方法里,用到之前提到的数据转换器 responseConverter 来实现数据的转换,最终就得到了请求的结果 Response 。

异步请求和同步请求大致相同,不同点在于异步请求要切换到主线程,这也是用之前提到的 callbackExecutor 来实现线程切换。

      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()) {
                // Emulate OkHttp's behavior of throwing/delivering an IOException on cancellation.
                callback.onFailure(ExecutorCallbackCall.this, new IOException("Canceled"));
              } else {
                callback.onResponse(ExecutorCallbackCall.this, response);
              }
            }
          });
        }
        //...
      }

enqueue 请求的回调方法通过 callbackExecutor 实现了线程切换。

以上,就分析了 Retrofit 从创建到发起网络请求的过程。

小结

Retrofit 的设计思想非常值得学习,事实上直接使用 OkHttp 同样可以实现功能,但是 Retrofit 对 OkHttp 的封装却更加方便了。

首先,是把网络请求的类型和参数,通过注解来处理了,用注解标注,可以简化不少代码,在 Retrofit 内部去实现注解的解析,并且通过动态代理去创建实现了注解方法接口的对象实例。

其次,大量设计模式的运用让 Retrofit 更容易去拓展,添加不同的适配和转换,把网络请求的每一个环节都进行拆分,面向接口的编程,网络请求适配和数据转换,每一个部分都可以独立开。

接着,基于对 OkHttp 的封装,能够利用 OkHttp 去实现最重要的网络请求功能。

最后,学习 Retrofit 的设计思想,并把它们用到我们的代码中去。

原创文章,转载请注明来源:    开源库之 Retrofit 源码分析