js css html

读懂 Retrofit 原理~

2022-07-20  本文已影响0人  Drew_MyINTYRE

什么是 REST ful API?

一句话概括 REST ful API:在我们使用 HTTP 协议做数据传输时应当遵守 HTTP 的规矩,包括请求方法、资源类型、Uri 格式等等..

为什么将请求设置为(接口+注解)形式?

迪米特法则:也称之为最小知道原则,即模块之间尽量减少不必要的依赖,即降低模块间的耦合性。

门面模式:基于迪米特法则拓展出来的一种设计模式,旨在将复杂的模块/系统访问入口控制的更加单一。举个例子:现要做一个获取图片功能,优先从本地缓存获取,没有缓存从网络获取随后再加入到本地缓存,假如不做任何处理,那每获取一张图片都要写一遍缓存逻辑,写的越多出错的可能就越高,其实调用者只是想获取一张图片而已,具体如何获取他不需要关心。此时可以通过门面模式将缓存功能做一个封装,只暴露出一个获取图片入口,这样调用者使用起来更加方便而且安全性更高。其实函数式编程也是门面模式的产物。

为什么通过门面模式设计 ApiService

Retrofit 做一次请求大致流程如下:

interface ApiService {
    /**
     * 获取首页数据
     */
    @GET("/article/list/{page}/json")
    suspend fun getHomeList(@Path("page") pageNo: Int)
    : ApiResponse<ArticleBean>
}

//构建Retrofit
val retrofit = Retrofit.Builder().build()

//创建ApiService实例
val apiService =retrofit.create(ApiService::class.java)

//发起请求(这里用的是suspend会自动发起请求,Java中可通过返回的call请求)
apiService.getHomeList(1)

我们都知道 Retrofit 只不过是对 OkHttp 做了封装。如果直接使用 OkHttp,当在构造 Request 时要做很多繁琐的工作,最要命的是 Request 可能在多处被构造(ViewModel、Repository...),写的越分散出错时排查的难度就越高。而 Retrofit 通过注解的形式将 Request 需要的必要信息全依附在方法上(还是个抽象方法,尽量撇除一切多余信息),作为使用者只需要调用对应方法即可实现请求。至于如何解析、构造、发起请求 Retrofit 内部会做处理,调用者不想也不需要知道,所以 Retrofit 通过门面模式帮调用者屏蔽了一些无用信息,只暴露出唯一入口,让调用者更专注于业务开发。像我们常用的 Room、GreenDao 也使用了这种模式。

动态代理其实不是工具

Retrofit 构建如下所示:

Retrofit.Builder()
    .client(okHttpClient)
    .addConverterFactory(GsonConverterFactory.create())
    .addCallAdapterFactory(RxJava2CallAdapterFactory.create())
    .baseUrl(ApiConstants.BASE_URL)
    .build()

很典型的构建者模式,可以配置 OkHttpGsonRxJava 等等,最后通过build() 做构建操作,跟一下 build() 代码:

# Retrofit.class

public Retrofit build() {

        // 1. CallAdapter 工厂集合
        List<CallAdapter.Factory> callAdapterFactories = new ArrayList<>(this.callAdapterFactories);
        callAdapterFactories.addAll(platform.defaultCallAdapterFactories(callbackExecutor));

        // 2. Converter 工厂集合
        List<Converter.Factory> converterFactories =
                new ArrayList<>(
                        1 + this.converterFactories.size() + platform.defaultConverterFactoriesSize());
        converterFactories.add(new BuiltInConverters());
        converterFactories.addAll(this.converterFactories);
        converterFactories.addAll(platform.defaultConverterFactories());

        return new Retrofit(
              callFactory,
                baseUrl,
                unmodifiableList(converterFactories),
                unmodifiableList(callAdapterFactories),
                callbackExecutor,
                validateEagerly);
    }

什么是动态代理?

动态代理是可以在运行期动态创建某个 interface 的实例,我们通过 Proxy.newProxyInstance 产生的代理类,当调用接口的任何方法时,都会被InvocationHandler#invoke 方法拦截,同时,在这个方法中可以拿到所传入的参数等,依照参数值再做相应的处理。

每次 CRUD 都会手动做一次上报操作,这显然是模版代码,如何解决?下面来看动态代理:

interface ToDo {
    fun buyHouse()
}

/**
 * 中介
 */
class Middlemen(private val any: Any) : InvocationHandler {
    override fun invoke(proxy: Any?, method: Method?, args: Array<out Any>?): Any? {
        return method?.invoke(any, *(args ?: arrayOfNulls<Any>(0)))
    }
}

/**
 * 买家
 */
class Buyer : ToDo {
    override fun buyHouse() {
        print("中介,请帮我买房子")
    }
}

// 最后利用代理对象实现购房的动作:
fun main(args: Array<String>) {
        val toDo: ToDo = Buyer()
        val dynamicMid = Proxy.newProxyInstance(
            toDo.javaClass.classLoader, toDo.javaClass.interfaces,
            Middlemen(toDo)
        ) as ToDo
        dynamicMid.buyHouse()
    }

动态代理最主要的部分在于代理对象实现 InvocationHandler,并重写 invoke 方法。 当代理对象代理了委托者的要求,不管要求有多少,当代理执行时,都会走进 invoke() 方法中。这是重点,圈起来后面要考。

动态代理获取 ApiService

# Retrofit.class

  public <T> T create(final Class<T> service) {
    validateServiceInterface(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 @Nullable Object invoke(Object proxy, Method method, @Nullable Object[] args)
                  throws Throwable {
                // ⚠️
                if (method.getDeclaringClass() == Object.class) {
                  return method.invoke(this, args);
                }
                args = args != null ? args : emptyArgs;
                
                // ⚠️
                return platform.isDefaultMethod(method)
                    ? platform.invokeDefaultMethod(method, service, proxy, args)
                    : loadServiceMethod(method).invoke(args);
              }
            });
  }

invoke() 这是一个代理方法,调用 ApiService中的任一方法并执行,其中参数 methodargs 代表 ApiService 对应的方法和参数。返回值中有一个isDefaultMethod,这里如果是 Java8 的默认方法直接执行,毕竟我们只需要代理 ApiService 中方法即可。经过反复筛选最后重任落在了loadServiceMethod,这也是 Retrofit 中最核心的一个方法,下面我们来跟一下:

loadServiceMethod(method) 它主要是将网络请求方法中的信息进行初步的处理,我们在创建 api service 具体接口时,会加上注解( @GET@POST@PUT...),参数( @Path@Query ... )等,该方法就是对接口中的注解、参数等进行解析,解析接口后又生成了一个 RequestFactory 请求工厂对象,并且利用这个 RequestFactory 对象创建了一个 CallAdapter

CallAdapter: 适配器,我们默认定义 API Service 方法的返回值为 Call 类型,但是有时候会自定义返回类型,例如和 RxJava 相结合,返回Observable 或者 Single 类型的时候应该怎么处理?CallAdapter 的作用就是帮助开发者去适配这些返回类型,你定义了什么类型的数据,就可以通过CallAdapter#adapt 进行返回。

# Retrofit.class
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;
  }

创建 ServiceMethod 对象是通过其静态方法 parseAnnotations 实现的,再跟一下这个方法:

# HttpServiceMethod.class

static <ResponseT, ReturnT> HttpServiceMethod<ResponseT, ReturnT> parseAnnotations(
            Retrofit retrofit, Method method, RequestFactory requestFactory) {
        boolean isKotlinSuspendFunction = requestFactory.isKotlinSuspendFunction;
        boolean continuationWantsResponse = false;
        boolean continuationBodyNullable = false;

        Annotation[] annotations = method.getAnnotations();
        Type adapterType;
        //1.获取adapterType,默认为method返回值类型
        if (isKotlinSuspendFunction) {
            Type[] parameterTypes = method.getGenericParameterTypes();
            Type responseType =
                    Utils.getParameterLowerBound(
                            0, (ParameterizedType) parameterTypes[parameterTypes.length - 1]);
            if (getRawType(responseType) == Response.class && responseType instanceof ParameterizedType) {
                // Unwrap the actual body type from Response<T>.
                responseType = Utils.getParameterUpperBound(0, (ParameterizedType) responseType);
                continuationWantsResponse = true;
            } else {
            }
            adapterType = new Utils.ParameterizedTypeImpl(null, Call.class, responseType);
            annotations = SkipCallbackExecutorImpl.ensurePresent(annotations);
        } else {
            adapterType = method.getGenericReturnType();
        }
        //2.创建CallAdapter
        CallAdapter<ResponseT, ReturnT> callAdapter =
                createCallAdapter(retrofit, method, adapterType, annotations);
        Type responseType = callAdapter.responseType();
        //3.创建responseConverter
        Converter<ResponseBody, ResponseT> responseConverter =
                createResponseConverter(retrofit, method, responseType);

        okhttp3.Call.Factory callFactory = retrofit.callFactory;
        //4.创建HttpServiceMethod类型具体实例
        if (!isKotlinSuspendFunction) {
            return new HttpServiceMethod.CallAdapted<>(requestFactory, callFactory, responseConverter, callAdapter);
        }
        //兼容kotlin suspend方法
        else if (continuationWantsResponse) {
            //noinspection unchecked Kotlin compiler guarantees ReturnT to be Object.
            return (HttpServiceMethod<ResponseT, ReturnT>)
                    new HttpServiceMethod.SuspendForResponse<>(
                            requestFactory,
                            callFactory,
                            responseConverter,
                            (CallAdapter<ResponseT, Call<ResponseT>>) callAdapter);
        } else {
            //noinspection unchecked Kotlin compiler guarantees ReturnT to be Object.
            return (HttpServiceMethod<ResponseT, ReturnT>)
                    new HttpServiceMethod.SuspendForBody<>(
                            requestFactory,
                            callFactory,
                            responseConverter,
                            (CallAdapter<ResponseT, Call<ResponseT>>) callAdapter,
                            continuationBodyNullable);
        }
    }
# ServiceMethod.class
 static <T> ServiceMethod<T> parseAnnotations(Retrofit retrofit, Method method) {
        // ⚠️
        RequestFactory requestFactory =
            RequestFactory.parseAnnotations(retrofit, method);
        Type returnType = method.getGenericReturnType();
        ...
        // ⚠️
        return HttpServiceMethod.parseAnnotations(retrofit,
                method, requestFactory);
    }
# HttpServiceMethod.class
@Override
  final @Nullable ReturnT invoke(Object[] args) {
    Call<ResponseT> call = new OkHttpCall<>(requestFactory, args, callFactory, responseConverter);
    return adapt(call, args);
  }

OkHttpCall 其实是对 OkHttp 中的 realCall 进行了一层包装,realCall 是如何创建的?

先创建 OkHttpClientRequest 对象,以 Request 来创建一个 RealCall 对象,利用它执行异步 enqueue 或者同步 execute 操作将请求发送出去,并监听请求失败或者成功的反馈 Callback

val okHttpClient = OkHttpClient()
val request: Request = Request.Builder()
    .url("https://cn.bing.com/")
    .build()

okHttpClient.newCall(request).enqueue(object : Callback{
    override fun onFailure(call: Call, e: IOException) {
    }

    override fun onResponse(call: Call, response: Response) {
    }
})

这里有三个主要的类需要说明一下:OkHttpClientRequest 以及 RealCall

loadServiceMethod(method).invoke(args);, loadServiceMethod(method) 返回的是 HttpServiceMethod, invoke() 创建了一个 OkHttpCall 实例,它内部其实就是对 OkHttp 的一系列操作。

我们再来看看 OkHttpCall 中网络请求的细节:

  @Override
  public void enqueue(final Callback<T> callback) {
    Objects.requireNonNull(callback, "callback == null");

    okhttp3.Call call;
    synchronized (this) {
      ...
      if (call == null && failure == null) {
        try {
          call = rawCall = createRawCall();
        }
      }
    ...
    call.enqueue(
        new okhttp3.Callback() {
          @Override
          public void onResponse(okhttp3.Call call, okhttp3.Response rawResponse) {
            Response<T> response;
            try {
              //解析请求返回值
              response = parseResponse(rawResponse);
            }
              ...
            try {
              callback.onResponse(OkHttpCall.this, response);
            }

          @Override
          public void onFailure(okhttp3.Call call, IOException e) {
            callFailure(e);
          }

          private void callFailure(Throwable e) {
            try {
              callback.onFailure(OkHttpCall.this, e);
            }
          }
        });
  }

分析上面的源码:

1,内部首先调用了一个 createRawCall() 方法,创建 rawCall,这个 rawCall 其实指代的就是 Okhttp3 的 call,也就是 OkHttp 进行网络请求调用的一个调度器;

2,创建好 OkHttp 的 call 后,就开始调用 enqueue 进行异步请求,发现在异步请求内响应的回调属于 okhttp3.Callback,所返回来的结果,也都是 okhttp3.Response,到这里就可以大概知道了,retrofit 的网络请求其实还是由 OkHttp 来实现。

3,okhttp3.Response 这个响应不方便开发者直接使用,所以 retrofit 在收到结果后,又对响应结果进行新一轮的解析 response = parseResponse(rawResponse),以 Response 对象的形式返回给开发者。

另外还有最后一行: adapt(call, args) ; 它的内部其实是由一个适配器CallAdapted 来调用,如下:

  protected ReturnT adapt(Call<ResponseT> call, Object[] args) {
      return callAdapter.adapt(call);
    }

adapt 是一个抽象方法,里面传入了上面刚创建的 OkHttpCall 作为参数。adapt 即 adapter,适配器的意思,那我们可以猜测一下,传入 OkHttpCall,是不是就是为了对 OkHttpCall 再进行适配的工作?但,OkHttpCall 已经可以进行网络请求了,为什么还需要使用 CallAdapted 进行再次的适配呢? ⁉️这个问题还是先记下,抛到后面解答。

从上面分析知道,Retrofit 之所以调用简便,是因为内部对 @GET@POST@Path 等注解以及请求参数进行了解析,让开发者只需要关心自己新增的请求方法是不是符合规范。而上述动态代理最主要做的事情就是创建了 CallAdaptedCallAdapted 其实只是一个适配器,主要需要了解的是它适配的是什么? 是否还记得我们在上面抛出了一个关于 CallAdapted 的问题:那就先来看看他是如何创建的?⁉️

abstract class HttpServiceMethod<ResponseT, ReturnT> extends ServiceMethod<ReturnT> {

  static <ResponseT, ReturnT> HttpServiceMethod<ResponseT, ReturnT> parseAnnotations(
      Retrofit retrofit, Method method, RequestFactory requestFactory) {

    Type adapterType;
    if (isKotlinSuspendFunction) {
      Type[] parameterTypes = method.getGenericParameterTypes();
      Type responseType =
          Utils.getParameterLowerBound(
              0, (ParameterizedType) parameterTypes[parameterTypes.length - 1]);
      if (getRawType(responseType) == Response.class && responseType instanceof ParameterizedType) {
        // Unwrap the actual body type from Response<T>.
        responseType = Utils.getParameterUpperBound(0, (ParameterizedType) responseType);
        continuationWantsResponse = true;
      }

      adapterType = new Utils.ParameterizedTypeImpl(null, Call.class, responseType);
      annotations = SkipCallbackExecutorImpl.ensurePresent(annotations);
    } else {
      adapterType = method.getGenericReturnType();
    }

    CallAdapter<ResponseT, ReturnT> callAdapter =
        createCallAdapter(retrofit, method, adapterType, annotations);
  ...

    Converter<ResponseBody, ResponseT> responseConverter =
        createResponseConverter(retrofit, method, responseType);

    okhttp3.Call.Factory callFactory = retrofit.callFactory;
    if (!isKotlinSuspendFunction) {
      return new CallAdapted<>(requestFactory, callFactory, responseConverter, callAdapter);
    }

public CallAdapter<?, ?> nextCallAdapter(
      @Nullable CallAdapter.Factory skipPast, Type returnType, Annotation[] annotations) {
    int start = callAdapterFactories.indexOf(skipPast) + 1;
    for (int i = start, count = callAdapterFactories.size(); i < count; i++) {
      CallAdapter<?, ?> adapter = callAdapterFactories.get(i).get(returnType, annotations, this);
      if (adapter != null) {
        return adapter;
      }
    }

CallAdapter 是根据 returnTypeannotations 的类型,从 callAdapterFactories 工厂中进行查找,从而返回所对应的网络请求适配器,这里 returnType 指的是网络请求接口里方法的返回值类型,如 CallObservable 等。annotations 则指代的是注解类型,如 @GET@POST 等。

这里也提到了一个适配器工厂 callAdapterFactories,不同的 CallAdapter 就是从这个工厂中查询出来的,有查找那就必定有添加,那适配器 CallAdapter 是怎么添加到工厂类中的? ⁉️callAdapterFactories 这个变量是属于 Retrofit 类,跟踪发现是由 Retrofit 构造函数传入,也就是 Retrofit 初始化时进行了赋值。

Retrofit 的初始化是由一种建造者模式来创建,在 Retrofitbuild() 方法中,找到了适配器工厂对其适配器的添加:

 public Retrofit build() {
     ...
      Executor callbackExecutor = this.callbackExecutor;
      if (callbackExecutor == null) {
        callbackExecutor = platform.defaultCallbackExecutor();
      }

      List<CallAdapter.Factory> callAdapterFactories = new ArrayList<>(this.callAdapterFactories);
      //添加适配器callAdapter
      callAdapterFactories.addAll(platform.defaultCallAdapterFactories(callbackExecutor));
     ...
      return new Retrofit(
          callFactory,
          baseUrl,
          unmodifiableList(converterFactories),
          unmodifiableList(callAdapterFactories),
          callbackExecutor,
          validateEagerly);
    }

platform.defaultCallAdapterFactories 指的是 Android 平台的一个默认的适配器工厂,当我们不使用自定义适配器工厂时,则添加的就是这默认的工厂。这里提到了自定义适配器工厂,其实我们在使用 Retrofit 的时候,有时候会和RxJava 结合,例如在创建 Retrofit 时,也会 addCallAdapterFactory,将RxJava2CallAdapterFactory 添加到 callAdapterFactories 中。

mRetrofit = new Retrofit.Builder()
                .client(okHttpClient)
                .baseUrl(baseUrl)
                .addCallAdapterFactory(RxJava2CallAdapterFactory.create())
                .build();

添加调用适配器工厂的目的就是支持 Call 以外的服务方法返回类型,如支持ObservableSingle 返回类型等。 在 callAdapterFactories 集合器添加一个默认适配器工厂时,也附带传进去了一个参数 callbackExecutorcallbackExecutor 是 Java8 或者 Android 平台的一个默认线程调度器,它的作用涉及到一个线程切换的问题,也就是后续需要分析的 Retrofit 是如何将子线程切换到主线程?⁉️

在上面源码创建 CallAdapter时,有一个对象也同时被创建,那就是Converter

Converter<ResponseBody, ResponseT> responseConverter = createResponseConverter(retrofit, method, responseType);

Converter 数据装换器。它的添加和获取方式和 CallAdapter 类似。在Retrofit 初始化时添加到 List<Converter.Factory> 工厂集合,根据类型 Type 取出不同的 Converter,对返回的网络响应,做出数据的转换,例如转换成实体类。基本逻辑与 CallAdapter 类似。

mRetrofit = new Retrofit.Builder()
                .client(mOkHttpClient)
                .baseUrl(baseUrl)
                .addConverterFactory(GsonConverterFactory.create()
                .build();

总结一下上面讲的:

1,一开始动态代理中调用 loadServiceMethod 方法,解析接口方法中的注解,参数,头部信息等;

2,依据接口方法的返回类型,从适配器工厂集合里进行查询,生成相应的适配器 CallAdapter,区分是 RxJavaObservableSingle 还是 Call 或者其他类型,(适配器工厂集合的数据是由构建 RetrofitaddCallAdapterFactory() 添加,如无自定义,则添加 Android 平台默认适配器)。以相同的方式取出数据转换器 Converter

3,利用上面生成的 CallAdapter,调用 invoke 方法,创建 OkHttpCall 对象,即针对请求信息,利用 OkHttp 进行异步或者同步网络请求,并且对响应结果进行实体类转换;

4,创建好 OkHttpCall 后,又利用上面查询到的适配器 CallAdapter 调用adapt,返回 RxJavaObservableSingle 或者 Call 对象。

Retrofit 是如何将子线程切换到主线程?

在添加默认适配器工厂 defaultCallAdapterFactories 时,将 callbackExecutor 作为了一个参数,那么它的具体实现也就是在这个默认适配器工厂中。 我们来看下 callbackExecutor 在里面做了些啥。

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

    @Override
    public void enqueue(final Callback<T> callback) {

      delegate.enqueue(
          new Callback<T>() {
            @Override
            public void onResponse(Call<T> call, final Response<T> response) {
              callbackExecutor.execute(
                  () -> {
                    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);
                    }
                  });
            }

            @Override
            public void onFailure(Call<T> call, final Throwable t) {
              callbackExecutor.execute(() -> callback.onFailure(ExecutorCallbackCall.this, t));
            }
          });
    }

在上述代码里了解到,callbackExecutorExecutor,一个线程调度器。在Call 的 enqueue 实现里执行了一个异步网络请求 delegate.enqueue,在请求的响应 onResponseonFailureExecutor 也同样执行了一个线程,这里就有个疑问,为什么要在一个异步请求里又调用一个线程?我们知道callbackExecutor 是一个线程调度器,那他内部到底实现的是什么?
默认 callbackExecutor 的创建在 Retrofit 的初始化中,callbackExecutor = platform.defaultCallbackExecutor();

static final class Android extends Platform {

    @Override
    public Executor defaultCallbackExecutor() {
      return new MainThreadExecutor();
    }

    static final class MainThreadExecutor implements Executor {
      private final Handler handler = new Handler(Looper.getMainLooper());

      @Override
      public void execute(Runnable r) {
        handler.post(r);
      }
    }
  }
}

platform 是一个 Android 平台,defaultCallbackExecutor 内部其实调用的是 new MainThreadExecutor() ,很清楚的看到, handler.post(r) 内部使用Handler将响应抛到了主线程。这就是 Retrofit 将子线程切换到主线程的核心所在。

Retrofit 为什么要使用动态代理?

动态代理的优势是什么?不用暴露真实的委托者,根据不同的委托创建不同的代理,通过代理去做事情。

那 Retrofit 弥补了OkHttp 的哪些缺点呢?

OkHttp 在使用的时候,请求参数的配置是不是很繁琐,尤其当有一些表单提交时,又臭又长,而 Retrofit 就是弥补了这个缺点,利用 @GET、@POST、@Path、@Body 等注解以及一些参数很简便的就构造出了请求。

当 Retrofit 创建了不同的接口,动态代理就发挥出了作用。每当不同接口方法执行时,动态代理都会拦截该请求,对接口中的注解,参数进行解析,构建出不同的 Request,最后则交给 OkHttp 去真正执行。

Retrofit 结合动态代理,不用关心真正的接口方法,对符合规范的接口进行统一化的管理,以统一的方式解析注解和参数,拼接成 request。

总结:

Retrofit 其实是 OkHttp 的封装类,内部网络请求靠的还是 OkHttp,那 Retrofit 封装后改变了什么?

上一篇下一篇

猜你喜欢

热点阅读