框架解读 | Retrofit设计剖析

作者:Calculus_小王
Retrofit是一个类型安全的HTTP客户端,可以通过注解将HTTP API转换为Java接口,并使用动态代理,CallAdapter和Converter来发起请求和解析响应。

本文 着重于 Retrofit的架构设计,对于其注解解析能力不作详细阐述

本文基于retrofit:2.6.2

示例

本示例仅以最基础的retrofit发起请求为例,关于kotlin suspend或rxjava等封装,会在源码阶段进行一同剖析

// 定义一个接口,用注解描述HTTP请求
public interface GitHubService {
    @GET("users/{user}/repos")
    Call<List<Repo>> listRepos(@Path("user") String user);
}

// 创建一个Retrofit实例,并指定基础URL和转换器
Retrofit retrofit = new Retrofit.Builder()
    .baseUrl("https://api.github.com/")
    .addConverterFactory(GsonConverterFactory.create())
    .build();

// 通过Retrofit创建接口的实现类
GitHubService service = retrofit.create(GitHubService.class);

// 调用接口方法,获取Call对象
Call<List<Repo>> repos = service.listRepos("octocat");

// 同步或异步执行Call对象,获取响应数据
repos.enqueue(new Callback<List<Repo>>() {
    @Override
    public void onResponse(Call<List<Repo>> call, Response<List<Repo>> response) {
    // 处理响应数据
    }

    @Override
    public void onFailure(Call<List<Repo>> call, Throwable t) {
    // 处理失败情况
    }
});

源码剖析

通常在retrofit的使用中,会结合rxjava或者kotlin suspend关键字一同使用,但与示例代码无异的是这两步:

  1. 得到Service对象

  2. 调用Service方法

    // 通过Retrofit创建接口的实现类
    GitHubService service = retrofit.create(GitHubService.class);

    // 调用接口方法,获取Call对象
    Call<List<Repo>> repos = service.listRepos("octocat");

而对于retrofit,我们最好奇的无非是其如何联动的OkHttp,借助其发出请求。OkHttp中发起请求是通过Call.enqueue(),那本文的目标方向就是这个方法的调用

得到Service对象

retrofit.create得到的是由动态代理newProxyInstance生成的Service对象,重点在于其中的invoke方法,对于通常情况,最终调用的都是loadServiceMethod(method)执行的 invoke() ,那接着看看它返回了什么对象,并且其 invoke() 究竟做了些什么

// Retrofit.java
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 @Nullable 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);
          }
          return loadServiceMethod(method).invoke(args != null ? args : emptyArgs);
        }
      });
}

Service的每个方法最终都对应着一个HttpServiceMethod,而其也有几个实现类CallAdapted(通常情况)、SuspendForResponse(suspend使用)、SuspendForBody

// Retrofit.java
ServiceMethod<?> loadServiceMethod(Method method) {
  // 这里还做了缓存,这里注意喔,缓存的是method,而不是service对应的class
  ServiceMethod<?> result = serviceMethodCache.get(method);
  if (result != null) return result;

  synchronized (serviceMethodCache) {
    result = serviceMethodCache.get(method);
    if (result == null) {
      // 最终return的是这个result,跟进去看看
      result = ServiceMethod.parseAnnotations(this, method);
      serviceMethodCache.put(method, result);
    }
  }
  return result;
}

// ServiceMethod.java
static <T> ServiceMethod<T> parseAnnotations(Retrofit retrofit, Method method) {
    // 这里进行了Request注解方面的解析,感兴趣的可以跟进去看,重点关注其中的`parseMethodAnnotation()`和`parseParameter()`
    // 内部区分了`methodAnnotations`和`parameterAnnotations`两种类型注解
    // 最终封装得到RequestFactory,也就转化为了okhttp请求时需要的一些内容
    RequestFactory requestFactory = RequestFactory.parseAnnotations(retrofit, method);
    // ......
    // 继续往下看,其实返的就是HttpServiceMethod对象
    return HttpServiceMethod.parseAnnotations(retrofit, method, requestFactory);
}

// HttpServiceMethod.java
static <ResponseT, ReturnT> HttpServiceMethod<ResponseT, ReturnT> parseAnnotations(
      Retrofit retrofit, Method method, RequestFactory requestFactory) {
    Annotation[] annotations = method.getAnnotations();
    Type adapterType;
    // ......
    // 虽然suspend会有些区别,但你完全可以理解为 方法 return的对象类型
    // 比如联动rxjava,就会返回一个Observable<T>
    adapterType = method.getGenericReturnType();
    
    // ......
    // Call适配器,Call其实就是OkHttp的Call,适配器的意义就是将其转化为对应的类型,比如rxjava的Observable
    // 但其内部仍会调用Call的方法
    CallAdapter<ResponseT, ReturnT> callAdapter =
        createCallAdapter(retrofit, method, adapterType, annotations);
    
    // 这个是结果解析器,gson就是添加在这里
    Converter<ResponseBody, ResponseT> responseConverter =
        createResponseConverter(retrofit, method, responseType);

    okhttp3.Call.Factory callFactory = retrofit.callFactory;
    if (!isKotlinSuspendFunction) {
      return new CallAdapted<>(requestFactory, callFactory, responseConverter, callAdapter);
    } else if (continuationWantsResponse) {
      //noinspection unchecked Kotlin compiler guarantees ReturnT to be Object.
      return (HttpServiceMethod<ResponseT, ReturnT>) new SuspendForResponse<>(requestFactory,
          callFactory, responseConverter, (CallAdapter<ResponseT, Call<ResponseT>>) callAdapter);
    } else {
      //noinspection unchecked Kotlin compiler guarantees ReturnT to be Object.
      return (HttpServiceMethod<ResponseT, ReturnT>) new SuspendForBody<>(requestFactory,
          callFactory, responseConverter, (CallAdapter<ResponseT, Call<ResponseT>>) callAdapter,
          continuationBodyNullable);
    }
}

前面讲到了invoke(),我们继续。对于HttpServiceMethod的几个子类,均没有实现该方法,发现在其父类中,转而调用了adapt,留给其子类去实现

@Override final @Nullable ReturnT invoke(Object[] args) {
    // 这个Call不是OkHttp的Call
    Call<ResponseT> call = new OkHttpCall<>(requestFactory, args, callFactory, responseConverter);
    return adapt(call, args);
}

protected abstract @Nullable ReturnT adapt(Call<ResponseT> call, Object[] args);

// 其实在call的设计中,就完全能看到okhttp的影子了
public interface Call<T> extends Cloneable {
  /**
   * Synchronously send the request and return its response.
   *
   * @throws IOException if a problem occurred talking to the server.
   * @throws RuntimeException (and subclasses) if an unexpected error occurs creating the request
   * or decoding the response.
   */
  Response<T> execute() throws IOException;

  /**
   * Asynchronously send the request and notify {@code callback} of its response or if an error
   * occurred talking to the server, creating the request, or processing the response.
   */
  void enqueue(Callback<T> callback);

  /**
   * Returns true if this call has been either {@linkplain #execute() executed} or {@linkplain
   * #enqueue(Callback) enqueued}. It is an error to execute or enqueue a call more than once.
   */
  boolean isExecuted();

  /**
   * Cancel this call. An attempt will be made to cancel in-flight calls, and if the call has not
   * yet been executed it never will be.
   */
  void cancel();

  /** True if {@link #cancel()} was called. */
  boolean isCanceled();

  /**
   * Create a new, identical call to this one which can be enqueued or executed even if this call
   * has already been.
   */
  Call<T> clone();

  /** The original HTTP request. */
  Request request();
}

调用Service方法

create的动态代理细节中我们得知,retrofit service每个方法的调用,都将映射到对应类型的HttpServiceMethod.adapt()

普通调用

// 调用接口方法,获取Call对象
Call<List<Repo>> repos = service.listRepos("octocat");

// 同步或异步执行Call对象,获取响应数据
repos.enqueue(new Callback<List<Repo>>() {
    @Override
    public void onResponse(Call<List<Repo>> call, Response<List<Repo>> response) {
    // 处理响应数据
    }

    @Override
    public void onFailure(Call<List<Repo>> call, Throwable t) {
    // 处理失败情况
    }
});

最普通的情况,返回值为原始Call,那就对应的是CallAdapted,然而这里令人头疼的是,它又套了一层。这里设计的原因就是为了兼容如Rxjava等场景

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

那这些callAdapter来自于哪里呢?回到前面传入的地方

callAdapterFactories()
    >>> retrofit.callAdapter(returnType, annotations)
        >>> nextCallAdapter(null, returnType, annotations)

// nextCallAdapter()
for (int i = start, count = callAdapterFactories.size(); i < count; i++) {
    // 可以想象的是,callAdapterFactories会根据returnType去匹配给到对象的adpter
    // 至于这个,对比下rxjava联动时需要添加的代码.addCallAdapterFactory(RxJava2CallAdapterFactory.create())
    // 也就是说Retrofit在建造过程中,会逐步的传进去
    CallAdapter<?, ?> adapter = callAdapterFactories.get(i).get(returnType, annotations, this);
    if (adapter != null) {
        return adapter;
    }
}

// Retrofit.Builder.build()
List<CallAdapter.Factory> callAdapterFactories = new ArrayList<>(this.callAdapterFactories);
callAdapterFactories.addAll(platform.defaultCallAdapterFactories(callbackExecutor));

那对于最普通的这种调用,那必然就是DefaultCallAdapterFactory了,而得到adapter最终是ExecutorCallbackCall

// DefaultCallAdapterFactory.java
public @Nullable CallAdapter<?, ?> get(
    Type returnType, Annotation[] annotations, Retrofit retrofit) {
  if (getRawType(returnType) != Call.class) {
    return null;
  }
  if (!(returnType instanceof ParameterizedType)) {
    throw new IllegalArgumentException(
        "Call return type must be parameterized as Call<Foo> or Call<? extends Foo>");
  }
  final Type responseType = Utils.getParameterUpperBound(0, (ParameterizedType) returnType);

  final Executor executor = Utils.isAnnotationPresent(annotations, SkipCallbackExecutor.class)
      ? null
      : callbackExecutor;

  return new CallAdapter<Object, Call<?>>() {
    @Override public Type responseType() {
      return responseType;
    }

    @Override public Call<Object> adapt(Call<Object> call) {
      return executor == null
          ? call
          : new ExecutorCallbackCall<>(executor, call);
    }
  };
}

回顾下示例代码,调用service method得到Call后,调用其enqueue,那也就是说最终走入了ExecutorCallbackCall.enqueue

// ExecutorCallbackCall.java
@Override public void enqueue(final Callback<T> callback) {
  checkNotNull(callback, "callback == null");
  // 这里的delegate,其实就是构造时传入的`OkHttpCall`,当然这还不是实际OkHttp的Call
  delegate.enqueue(new Callback<T>() {
    @Override public void onResponse(Call<T> call, final Response<T> response) {
      // 当结果回来时,需要进行线程切换
      // 至于这个callbackExecutor感兴趣的可以自行追踪下,其实就是retrofit在build时生成的一个MainThreadExecutor
      // 由platform.defaultCallbackExecutor()得到,实际内部就是个handler......
      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);
          }
        }
      });
    }

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

那请求其实还是由OkHttpCall发起,中间的无论HttpServiceMethod还是CallAdapter,无非就是为了扩展性和兼容性进行的设计罢了

// OkHttpCall.java
@Override public void enqueue(final Callback<T> callback) {
  checkNotNull(callback, "callback == null");

  okhttp3.Call call;
  Throwable failure;

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

    call = rawCall;
    failure = creationFailure;
    if (call == null && failure == null) {
      try {
        // 这里由callFactory.newCall生成
        // 而这个callFactory就是retrofit.client(okHttpClient)
        // 到这明白了吧,这个OkHttpCall终究还是OkHttpClient.RealCall套了一层壳
        call = rawCall = createRawCall();
      } catch (Throwable t) {
        throwIfFatal(t);
        failure = creationFailure = t;
      }
    }
  }

  if (failure != null) {
    callback.onFailure(this, failure);
    return;
  }

  if (canceled) {
    call.cancel();
  }
  // 下面的内容,对于熟悉OkHttp的小伙伴来说已经毫无悬念了
  call.enqueue(new okhttp3.Callback() {
    @Override public void onResponse(okhttp3.Call call, okhttp3.Response rawResponse) {
      Response<T> response;
      try {
        // 这里进行了结果的解析,其中用到了`responseConverter`,也就是addConverterFactory
        // 同样的,也是类似于CallAdapter的设计
        response = parseResponse(rawResponse);
      } catch (Throwable e) {
        throwIfFatal(e);
        callFailure(e);
        return;
      }

      try {
        callback.onResponse(OkHttpCall.this, response);
      } catch (Throwable t) {
        throwIfFatal(t);
        t.printStackTrace(); // TODO this is not great
      }
    }

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

    private void callFailure(Throwable e) {
      try {
        callback.onFailure(OkHttpCall.this, e);
      } catch (Throwable t) {
        throwIfFatal(t);
        t.printStackTrace(); // TODO this is not great
      }
    }
  });
}

其实到这,Retrofit的大体设计已经结束了。其中HttpServiceMethodCallAdapter是其中的关键

Rxjava调用

书接上文普通调用时,对于Rxjava的联动时,区分点在于CallAdapter

addCallAdapterFactory(RxJava2CallAdapterFactory.create());

同样去看factory.get会得到RxJavaCallAdapter

// RxJavaCallAdapter.java
@Override public Object adapt(Call<R> call) {
  // 这里根据同步\异步 将OkHttpCall传了进去,结合rxjava操作符,发起okhttp请求,并把结果发射出去
  // 可以看对应类中的call(),这里就不展开了蛤,相信你已经可以独当一面了
  // 当然,主要是我对rxjava操作符不太熟......
  OnSubscribe<Response<R>> callFunc = isAsync
      ? new CallEnqueueOnSubscribe<>(call)
      : new CallExecuteOnSubscribe<>(call);

  OnSubscribe<?> func;
  if (isResult) {
    func = new ResultOnSubscribe<>(callFunc);
  } else if (isBody) {
    func = new BodyOnSubscribe<>(callFunc);
  } else {
    func = callFunc;
  }
  // 这里包装之后,最终返回了出去,得到了通常使用的Observable
  Observable<?> observable = Observable.create(func);

  if (scheduler != null) {
    observable = observable.subscribeOn(scheduler);
  }

  if (isSingle) {
    return observable.toSingle();
  }
  if (isCompletable) {
    return observable.toCompletable();
  }
  return observable;
}

Suspend调用

对于协程结合retrofit,通常就是scope.launch{ service.method() }

CoroutineScope(Dispatchers.IO).launch {
    try {
        val repos = service.listRepos("octocat")
        // 处理响应数据,通常会给一个callback,外面再通过livedata去发射处理好的数据
    } catch (e: Exception) {
        // 处理异常情况
    }
}

对于suspend,必然结合协程,也就取代了rxjava的线程切换能力,那HttpServiceMethod对应SuspendForResponse(主体差不多,那就挑这个将)\SuspendForBody,而CallAdapter没啥花里胡哨的,得到的Call就是最普通的ExecutorCallbackCall

// SuspendForResponse.java
@Override protected Object adapt(Call<ResponseT> call, Object[] args) {
  call = callAdapter.adapt(call);

  //noinspection unchecked Checked by reflection inside RequestFactory.
  Continuation<Response<ResponseT>> continuation =
      (Continuation<Response<ResponseT>>) args[args.length - 1];

  // See SuspendForBody for explanation about this try/catch.
  try {
    // 对于suspend的阻塞,await是再熟悉不过了的
    return KotlinExtensions.awaitResponse(call, continuation);
  } catch (Exception e) {
    return KotlinExtensions.yieldAndThrow(e, continuation);
  }
}

寻寻觅觅,蓦然回首那人却在灯火阑珊处,最终ExecutorCallbackCall得到了调用,这里是个suspendCancellableCoroutine的挂起使用,结合resume进行唤起

suspend fun <T : Any> Call<T>.awaitResponse(): Response<T> {
  return suspendCancellableCoroutine { continuation ->
    continuation.invokeOnCancellation {
      cancel()
    }
    enqueue(object : Callback<T> {
      override fun onResponse(call: Call<T>, response: Response<T>) {
        continuation.resume(response)
      }

      override fun onFailure(call: Call<T>, t: Throwable) {
        continuation.resumeWithException(t)
      }
    })
  }
}

总结

其实对于Retrofit的设计最耐人寻味的莫非是通过动态代理机制配合HttpServiceMethod,实现了在method的颗粒度上完成了扩展设计,而CallAdapterConverter无异于在整体的兼容性和解耦上更加锦上添花。

这其实也给注解的适用场景提供了非常好的借鉴意义,通常可能会局限于APT去进行插桩或代码生成

• Retrofit使用动态代理机制,通过Proxy.newProxyInstance方法创建接口的代理类

并将所有方法调用委托给内部的InvocationHandler处理

• InvocationHandler负责根据方法的注解创建ServiceMethod对象

ServiceMethod是一个抽象类,它封装了请求的相关信息,如URL,HTTP方法,参数,请求体等

ServiceMethod的具体实现类是HttpServiceMethod

它根据Retrofit配置的CallAdapter和Converter来创建Call对象和解析响应数据

• CallAdapter是一个接口,它定义了如何将Call对象转换为其他类型,如Observable,Deferred等

Retrofit提供了默认的CallAdapter,也可以通过addCallAdapterFactory方法添加自定义的CallAdapter

• Converter是一个接口,它定义了如何将请求体和响应体转换为Java对象

• Retrofit提供了一些内置的Converter,如GsonConverter,也可以通过addConverterFactory方法添加自定义的Converter

Android 学习笔录

Android 性能优化篇:https://qr18.cn/FVlo89
Android 车载篇:https://qr18.cn/F05ZCM
Android 逆向安全学习笔记:https://qr18.cn/CQ5TcL
Android Framework底层原理篇:https://qr18.cn/AQpN4J
Android 音视频篇:https://qr18.cn/Ei3VPD
Jetpack全家桶篇(内含Compose):https://qr18.cn/A0gajp
Kotlin 篇:https://qr18.cn/CdjtAF
Gradle 篇:https://qr18.cn/DzrmMB
OkHttp 源码解析笔记:https://qr18.cn/Cw0pBD
Flutter 篇:https://qr18.cn/DIvKma
Android 八大知识体:https://qr18.cn/CyxarU
Android 核心笔记:https://qr21.cn/CaZQLo
Android 往年面试题锦:https://qr18.cn/CKV8OZ
2023年最新Android 面试题集:https://qr18.cn/CgxrRy
Android 车载开发岗位面试习题:https://qr18.cn/FTlyCJ
音视频面试题锦:https://qr18.cn/AcV6Ap

相关推荐
拭心5 小时前
Google 提供的 Android 端上大模型组件:MediaPipe LLM 介绍
android
fantasy_arch5 小时前
CPU性能优化-磁盘空间和解析时间
网络·性能优化
带电的小王7 小时前
WhisperKit: Android 端测试 Whisper -- Android手机(Qualcomm GPU)部署音频大模型
android·智能手机·whisper·qualcomm
是Dream呀7 小时前
Python从0到100(七十八):神经网络--从0开始搭建全连接网络和CNN网络
网络·python·神经网络
梦想平凡7 小时前
PHP 微信棋牌开发全解析:高级教程
android·数据库·oracle
元争栈道8 小时前
webview和H5来实现的android短视频(短剧)音视频播放依赖控件
android·音视频
kaixin_learn_qt_ing8 小时前
了解RPC
网络·网络协议·rpc
阿甘知识库8 小时前
宝塔面板跨服务器数据同步教程:双机备份零停机
android·运维·服务器·备份·同步·宝塔面板·建站
安全小王子9 小时前
Kali操作系统简单介绍
网络·web安全
元争栈道9 小时前
webview+H5来实现的android短视频(短剧)音视频播放依赖控件资源
android·音视频