目录
  1. 1. 一、Retrofit 概述与整体架构
    1. 1.1. 1.1 Retrofit 是什么
    2. 1.2. 1.2 一行代码的旅程
  2. 2. 二、动态代理 —— Retrofit 的核心魔法
    1. 2.1. 2.1 Proxy.newProxyInstance 原理
    2. 2.2. 2.2 ServiceMethod 解析管线
  3. 3. 三、注解处理 —— 从接口到 HTTP 请求
    1. 3.1. 3.1 HTTP 方法注解
    2. 3.2. 3.2 参数注解详解
      1. 3.2.1. @Path —— URL 动态替换
      2. 3.2.2. @Query / @QueryMap —— URL 查询参数
      3. 3.2.3. @Field / @FieldMap —— 表单提交
      4. 3.2.4. @Body —— 请求体
      5. 3.2.5. @Header / @Headers —— 请求头
      6. 3.2.6. @Url —— 动态 URL
      7. 3.2.7. @Multipart / @Part —— 文件上传
  4. 4. 四、Converter Factory —— 序列化与反序列化
    1. 4.1. 4.1 Converter 接口体系
    2. 4.2. 4.2 GsonConverterFactory
    3. 4.3. 4.3 Moshi 的 Kotlin 代码消除优势
    4. 4.4. 4.4 Kotlinx.serialization —— Kotlin 原生的序列化
    5. 4.5. 4.5 自定义 Converter(Protobuf 示例)
  5. 5. 五、CallAdapter Factory —— 返回值适配
    1. 5.1. 5.1 CallAdapter 接口体系
    2. 5.2. 5.2 默认 CallAdapter —— 直接返回 Call
    3. 5.3. 5.3 Kotlin Coroutine 适配 —— KotlinExtensions
    4. 5.4. 5.4 RxJava 适配 —— RxJava2CallAdapterFactory
  6. 6. 六、OkHttpCall —— 实际网络调用
    1. 6.1. 6.1 OkHttpCall 实现
    2. 6.2. 6.2 RequestFactory.create —— 构建 OkHttp Request
  7. 7. 七、错误处理
    1. 7.1. 7.1 HttpException
    2. 7.2. 7.2 Kotlin Result 包装
    3. 7.3. 7.3 网络错误 vs 服务器错误
  8. 8. 八、Interceptor 集成与 Base URL 解析
    1. 8.1. 8.1 Retrofit 中的 Interceptor
    2. 8.2. 8.2 Base URL 解析规则
  9. 9. 九、Retrofit vs Ktor-client 对比
    1. 9.1. 9.1 架构对比
    2. 9.2. 9.2 代码风格对比
  10. 10. 十、总结
  11. 11. 参考资源
【必知必会SDK】之Retrofit

Retrofit 中用到的设计模式以及Retrofit的类图结构.png

一、Retrofit 概述与整体架构

1.1 Retrofit 是什么

Retrofit 是 Square 公司开发的类型安全的 HTTP 客户端,基于 OkHttp 构建。它通过 Java 动态代理 机制,将 HTTP API 声明为接口方法,通过注解描述请求参数,在运行时生成实现类,完成网络调用。

源码仓库github.com/square/retrofit

核心设计思想:面向接口编程 + 动态代理 + 适配器模式 + 工厂模式 + 建造者模式。

┌────────────────────────────────────────────────────────┐
│ Retrofit │
│ │
│ Retrofit.Builder │
│ ├── baseUrl (HttpUrl) │
│ ├── callFactory (okhttp3.Call.Factory) │
│ ├── converterFactories (List<Converter.Factory>) │
│ ├── callAdapterFactories (List<CallAdapter.Factory>) │
│ └── callbackExecutor (Executor) │
│ │
│ Retrofit.create(MyApi::class.java) │
│ └── Proxy.newProxyInstance │
│ └── InvocationHandler.invoke │
│ └── loadServiceMethod(method) │
│ ├── parseMethodAnnotation │
│ ├── parseParameterAnnotations │
│ └── HttpServiceMethod.parseAnnotations │
│ └── invoke(callFactory, args) │
│ └── OkHttpCall.enqueue/execute │
│ └── parseResponse │
│ ├── Converter 反序列化 │
│ └── CallAdapter 适配 │
└────────────────────────────────────────────────────────┘

1.2 一行代码的旅程

// 定义接口
interface GitHubApi {
@GET("users/{user}/repos")
suspend fun listRepos(@Path("user") user: String): List<Repo>
}

// 使用
val api = retrofit.create(GitHubApi::class.java)
val repos = api.listRepos("octocat")

这一行 api.listRepos("octocat") 的背后发生了什么:

  1. retrofit.create() 通过 Proxy.newProxyInstance 创建了 GitHubApi 的动态代理
  2. 调用 listRepos("octocat") 时,实际的调用被路由到 InvocationHandler.invoke()
  3. invoke() 中通过 loadServiceMethod(method) 解析方法上的注解
  4. ServiceMethod.parseAnnotations() 解析 @GET("users/{user}/repos")@Path("user")
  5. 因为是 suspend 函数,HttpServiceMethod 使用 KotlinExtensions.await() 适配
  6. 构建 OkHttpCall,调用 call.enqueue() 发起异步网络请求
  7. 响应返回后,Converter 将 Response Body(JSON)反序列化为 List<Repo>
  8. CallAdapterCall<List<Repo>> 适配为 suspend 函数的返回值

二、动态代理 —— Retrofit 的核心魔法

2.1 Proxy.newProxyInstance 原理

Java 的动态代理允许在运行时创建接口的实现类。Retrofit 正是利用了这一特性:

源码位置retrofit2/Retrofit.java

public final class Retrofit {
private final Map<Method, ServiceMethod<?>> serviceMethodCache =
new ConcurrentHashMap<>();
private final okhttp3.Call.Factory callFactory;
private final HttpUrl baseUrl;
private final List<Converter.Factory> converterFactories;
private final List<CallAdapter.Factory> callAdapterFactories;
private final Executor callbackExecutor;
private final boolean validateEagerly;

@SuppressWarnings("unchecked")
public <T> T create(final Class<T> service) {
validateServiceInterface(service); // 验证是否为接口
return (T) Proxy.newProxyInstance(
service.getClassLoader(),
new Class<?>[] { service },
new InvocationHandler() {
private final Object[] emptyArgs = new Object[0];

@Override
public Object invoke(Object proxy, Method method,
@Nullable Object[] args) throws Throwable {
// 如果是 Object 的方法(toString/equals/hashCode),正常处理
if (method.getDeclaringClass() == Object.class) {
return method.invoke(this, args);
}
args = args != null ? args : emptyArgs;
// platformDefaultMethod: Java 8 接口默认方法处理
return platform.isDefaultMethod(method)
? platform.invokeDefaultMethod(method, service, proxy, args)
: loadServiceMethod(method).invoke(args);
}
});
}

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;
}

private void validateServiceInterface(Class<?> service) {
if (!service.isInterface()) {
throw new IllegalArgumentException("API declarations must be interfaces.");
}
// 防止接口继承其他接口(Retrofit 不支持接口继承)
Deque<Class<?>> check = new ArrayDeque<>(1);
check.add(service);
while (!check.isEmpty()) {
Class<?> candidate = check.removeFirst();
if (candidate.getTypeParameters().length != 0) {
StringBuilder msg = new StringBuilder("Type parameters are unsupported on ")
.append(candidate.getName());
// ...
throw new IllegalArgumentException(msg.toString());
}
Collections.addAll(check, candidate.getInterfaces());
}
// 如果设置了 validateEagerly,立即解析所有方法
if (validateEagerly) {
for (Method method : service.getDeclaredMethods()) {
if (!platform.isDefaultMethod(method) && !Modifier.isStatic(method.getModifiers())) {
loadServiceMethod(method);
}
}
}
}
}

关键点

  • serviceMethodCacheConcurrentHashMap,线程安全,且方法解析只做一次
  • validateEagerly:开发阶段建议开启,可以在编译时捕获注解错误
  • Platform 抽象层:处理 Java 8 / Android 的差异(默认方法、Kotlin 协程)

2.2 ServiceMethod 解析管线

源码位置retrofit2/ServiceMethod.java

abstract class ServiceMethod<T> {
static <T> ServiceMethod<T> parseAnnotations(Retrofit retrofit, Method method) {
// 1. 解析注解,生成 RequestFactory
RequestFactory requestFactory = RequestFactory.parseAnnotations(retrofit, method);

// 2. 确定返回类型
Type returnType = method.getGenericReturnType();
if (Utils.hasUnresolvableType(returnType)) {
throw methodError(method,
"Method return type must not include a type variable or wildcard: %s", returnType);
}
if (returnType == void.class) {
throw methodError(method, "Service methods cannot return void.");
}

// 3. 创建 HttpServiceMethod
return HttpServiceMethod.parseAnnotations(retrofit, method, requestFactory);
}

abstract @Nullable T invoke(Object[] args);
}

RequestFactory.parseAnnotations 负责解析方法上的 HTTP 方法注解(@GET, @POST 等)和参数注解(@Path, @Query 等),生成:

final class RequestFactory {
final String httpMethod; // "GET", "POST", "PUT", "DELETE" 等
final HttpUrl baseUrl;
final String relativeUrl; // "users/{user}/repos"
final Headers headers; // 固定头部
final MediaType contentType; // Content-Type(如 POST 的 @Body)
final boolean hasBody;
final boolean isFormEncoded;
final boolean isMultipart;
final ParameterHandler<?>[] parameterHandlers; // 参数处理器数组
final boolean isKotlinSuspendFunction;
}

三、注解处理 —— 从接口到 HTTP 请求

3.1 HTTP 方法注解

// 内置的 HTTP 方法注解
@Documented @Target(METHOD) @Retention(RUNTIME) public @interface GET { String value() default ""; }
@Documented @Target(METHOD) @Retention(RUNTIME) public @interface POST { String value() default ""; }
@Documented @Target(METHOD) @Retention(RUNTIME) public @interface PUT { String value() default ""; }
@Documented @Target(METHOD) @Retention(RUNTIME) public @interface DELETE { String value() default ""; }
@Documented @Target(METHOD) @Retention(RUNTIME) public @interface PATCH { String value() default ""; }
@Documented @Target(METHOD) @Retention(RUNTIME) public @interface OPTIONS { String value() default ""; }
@Documented @Target(METHOD) @Retention(RUNTIME) public @interface HEAD { String value() default ""; }

// 通用 HTTP 方法(自定义方法名 + 路径 + hasBody)
@Documented @Target(METHOD) @Retention(RUNTIME)
public @interface HTTP {
String method(); // 如 "MOVE", "COPY", "PROPFIND" 等 WebDAV 方法
String path() default "";
boolean hasBody() default false;
}

解析过程RequestFactory.parseMethodAnnotation):

private void parseMethodAnnotation(Annotation annotation) {
if (annotation instanceof GET) {
parseHttpMethodAndPath("GET", ((GET) annotation).value(), false);
} else if (annotation instanceof POST) {
parseHttpMethodAndPath("POST", ((POST) annotation).value(), true);
} else if (annotation instanceof PUT) {
parseHttpMethodAndPath("PUT", ((PUT) annotation).value(), true);
} else if (annotation instanceof DELETE) {
parseHttpMethodAndPath("DELETE", ((DELETE) annotation).value(), false);
} else if (annotation instanceof PATCH) {
parseHttpMethodAndPath("PATCH", ((PATCH) annotation).value(), true);
} else if (annotation instanceof HEAD) {
parseHttpMethodAndPath("HEAD", ((HEAD) annotation).value(), false);
} else if (annotation instanceof HTTP) {
HTTP http = (HTTP) annotation;
parseHttpMethodAndPath(http.method(), http.path(), http.hasBody());
}
}

3.2 参数注解详解

@Path —— URL 动态替换

@GET("users/{user}/repos")
suspend fun listRepos(@Path("user") user: String): List<Repo>
// 调用: listRepos("octocat")
// 生成 URL: https://api.github.com/users/octocat/repos

// 编码控制
@GET("users/{name}")
suspend fun getUser(@Path(value = "name", encoded = true) name: String): User
// encoded=true: 不对参数进行 URL 编码

源码处理ParameterHandler.Path):

static final class Path<T> extends ParameterHandler<T> {
private final String name;
private final Converter<T, String> valueConverter;
private final boolean encoded;

@Override
void apply(RequestBuilder builder, @Nullable T value) throws IOException {
if (value == null) {
throw new IllegalArgumentException(
"Path parameter \"" + name + "\" value must not be null.");
}
builder.addPathParam(name, valueConverter.convert(value), encoded);
}
}

@Query / @QueryMap —— URL 查询参数

@GET("search/repositories")
suspend fun searchRepos(
@Query("q") query: String,
@Query("sort") sort: String = "stars",
@Query("page") page: Int
): SearchResult
// 生成 URL: https://api.github.com/search/repositories?q=retrofit&sort=stars&page=1

// 查询参数为 null 时自动忽略
@GET("list")
suspend fun getList(@Query("filter") filter: String?): List<Item>
// 当 filter=null 时,不会添加到 URL 中

// @QueryMap 动态拼接
@GET("search")
suspend fun search(@QueryMap filters: Map<String, String>): SearchResult

@Query 的编码行为

static final class Query<T> extends ParameterHandler<T> {
private final String name;
private final Converter<T, String> valueConverter;
private final boolean encoded;

@Override
void apply(RequestBuilder builder, @Nullable T value) throws IOException {
if (value == null) return; // null 值自动忽略
String queryValue = valueConverter.convert(value);
if (queryValue == null) return;
builder.addQueryParam(name, queryValue, encoded);
}
}

@Field / @FieldMap —— 表单提交

@FormUrlEncoded  // 必须配合此注解
@POST("login")
suspend fun login(
@Field("username") username: String,
@Field("password") password: String
): LoginResponse
// 生成 Body: username=octocat&password=secret

// @FieldMap 动态表单
@FormUrlEncoded
@POST("profile")
suspend fun updateProfile(@FieldMap fields: Map<String, String>): User

@Body —— 请求体

@POST("users")
suspend fun createUser(@Body user: CreateUserRequest): User
// 使用 Converter 将 CreateUserRequest 序列化为 JSON → Request Body

// @Body 有且只能有一个
// @Body 不能与 @FormUrlEncoded / @Multipart 同时使用

源码约束检查RequestFactory.parseParameter):

// Retrofit 确保 @Body 只有一个
if (gotBody) {
throw parameterError(method, p,
"Multiple @Body method annotations found.");
}

@Header / @Headers —— 请求头

// 静态头部
@Headers(
"Accept: application/json",
"Cache-Control: max-age=640000"
)
@GET("users")
suspend fun getUsers(): List<User>

// 动态头部
@GET("users")
suspend fun getUsers(@Header("Authorization") token: String): List<User>

// @HeaderMap 动态批量添加
@GET("users")
suspend fun getUsers(@HeaderMap headers: Map<String, String>): List<User>

@Url —— 动态 URL

@GET  // 不写路径
suspend fun downloadFile(@Url url: String): ResponseBody
// url = "https://example.com/path/to/file.pdf"

// @Url 和 baseUrl 的关系:
// 如果 @Url 是绝对路径 → 完全替换 baseUrl
// 如果 @Url 不以 / 开头 → 贴在 baseUrl 后面

HttpUrl.resolve 的解析逻辑

// okhttp3.HttpUrl
public HttpUrl resolve(String link) {
Builder builder = newBuilder(link);
return builder != null ? builder.build() : null;
}
// 如果 link 是绝对 URL → 完全替换
// 如果 link 以 / 开头 → 替换路径部分
// 如果 link 不以 / 开头 → 替换最后一个路径段

@Multipart / @Part —— 文件上传

@Multipart
@POST("upload")
suspend fun uploadFile(
@Part file: MultipartBody.Part,
@Part("description") description: RequestBody
): UploadResponse

// 使用
val requestBody = RequestBody.create(MediaType.parse("image/jpeg"), file)
val multipart = MultipartBody.Part.createFormData("file", file.name, requestBody)
api.uploadFile(multipart, RequestBody.create(MediaType.parse("text/plain"), "avatar"))

四、Converter Factory —— 序列化与反序列化

4.1 Converter 接口体系

源码位置retrofit2/Converter.java

public interface Converter<F, T> {
@Nullable T convert(F value) throws IOException;

abstract class Factory {
// 请求体的转换器:Java对象 → RequestBody
public @Nullable Converter<?, RequestBody> requestBodyConverter(
Type type, Annotation[] parameterAnnotations,
Annotation[] methodAnnotations, Retrofit retrofit) {
return null;
}

// 响应体的转换器:ResponseBody → Java对象
public @Nullable Converter<ResponseBody, ?> responseBodyConverter(
Type type, Annotation[] annotations, Retrofit retrofit) {
return null;
}
}
}

4.2 GsonConverterFactory

// retrofit2:converter-gson
public final class GsonConverterFactory extends Converter.Factory {
private final Gson gson;

public static GsonConverterFactory create() {
return create(new Gson());
}

@Override
public Converter<?, RequestBody> requestBodyConverter(Type type,
Annotation[] parameterAnnotations, Annotation[] methodAnnotations,
Retrofit retrofit) {
TypeAdapter<?> adapter = gson.getAdapter(TypeToken.get(type));
return new GsonRequestBodyConverter<>(gson, adapter);
}

@Override
public Converter<ResponseBody, ?> responseBodyConverter(Type type,
Annotation[] annotations, Retrofit retrofit) {
TypeAdapter<?> adapter = gson.getAdapter(TypeToken.get(type));
return new GsonResponseBodyConverter<>(gson, adapter);
}
}

4.3 Moshi 的 Kotlin 代码消除优势

Moshi 是 Square 官方推荐(尤其在 Kotlin 项目中):

// Moshi 对 Kotlin data class 的原生支持
@JsonClass(generateAdapter = true)
data class Repo(
val id: Long,
val name: String,
@Json(name = "full_name") val fullName: String
)
// 配合 Moshi 的代码生成(kapt/kotlin-codegen),零反射

4.4 Kotlinx.serialization —— Kotlin 原生的序列化

@Serializable
data class Repo(
val id: Long,
val name: String,
@SerialName("full_name") val fullName: String
)

// Retrofit 集成
val contentType = "application/json".toMediaType()
val retrofit = Retrofit.Builder()
.baseUrl("https://api.github.com/")
.addConverterFactory(Json.asConverterFactory(contentType))
.build()

4.5 自定义 Converter(Protobuf 示例)

public final class ProtoConverterFactory extends Converter.Factory {
private final ExtensionRegistryLite registry;

public static ProtoConverterFactory create(ExtensionRegistryLite registry) {
return new ProtoConverterFactory(registry);
}

@Override
public Converter<ResponseBody, ?> responseBodyConverter(Type type,
Annotation[] annotations, Retrofit retrofit) {
if (!(type instanceof Class<?>)) return null;
Class<?> c = (Class<?>) type;
if (!MessageLite.class.isAssignableFrom(c)) return null;

Parser<? extends MessageLite> parser;
try {
Method method = c.getDeclaredMethod("parser");
//noinspection unchecked
parser = (Parser<? extends MessageLite>) method.invoke(null);
} catch (Exception e) {
throw new RuntimeException(e);
}

return new ProtoResponseBodyConverter<>(parser, registry);
}

static final class ProtoResponseBodyConverter<T extends MessageLite>
implements Converter<ResponseBody, T> {
private final Parser<T> parser;
private final ExtensionRegistryLite registry;

@Override
public T convert(ResponseBody value) throws IOException {
try {
return parser.parseFrom(value.byteStream(), registry);
} finally {
value.close();
}
}
}
}

五、CallAdapter Factory —— 返回值适配

5.1 CallAdapter 接口体系

源码位置retrofit2/CallAdapter.java

public interface CallAdapter<R, T> {
// 响应的原始类型(如 ResponseBody, okhttp3.Response)
Type responseType();

// 将 Call 适配为目标返回类型
T adapt(Call<R> call);

abstract class Factory {
// 返回 null 表示不处理此类型
public abstract @Nullable CallAdapter<?, ?> get(
Type returnType, Annotation[] annotations, Retrofit retrofit);
}
}

5.2 默认 CallAdapter —— 直接返回 Call

// 内置 DefaultCallAdapterFactory
final class DefaultCallAdapterFactory extends CallAdapter.Factory {
@Override
public @Nullable CallAdapter<?, ?> get(Type returnType,
Annotation[] annotations, Retrofit retrofit) {
if (getRawType(returnType) != Call.class) {
return null; // 不是 Call<T> 类型,不处理
}
if (!(returnType instanceof ParameterizedType)) {
throw new IllegalArgumentException(
"Call return type must be parameterized as Call<Foo> or Call<? extends Foo>");
}
final Type responseType = getParameterUpperBound(0, (ParameterizedType) returnType);
return new CallAdapter<Object, Call<?>>() {
@Override public Type responseType() { return responseType; }
@Override public Call<Object> adapt(Call<Object> call) { return call; }
};
}
}

5.3 Kotlin Coroutine 适配 —— KotlinExtensions

Retrofit 从 2.6 开始原生支持 suspend 函数:

源码位置retrofit2/KotlinExtensions.kt

// suspend 函数的 CallAdapter —— 使用 kotlinx.coroutines
// 原理:将 suspend 函数适配为 Call<T>.await()

// KotlinExtensions.kt (Retrofit 2.6+)
suspend fun <T> Call<T>.await(): T {
return suspendCancellableCoroutine { continuation ->
continuation.invokeOnCancellation {
cancel()
}
enqueue(object : Callback<T> {
override fun onResponse(call: Call<T>, response: Response<T>) {
if (response.isSuccessful) {
val body = response.body()
if (body == null) {
continuation.resumeWithException(
KotlinNullPointerException("Response body is null"))
} else {
continuation.resume(body)
}
} else {
continuation.resumeWithException(HttpException(response))
}
}

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

// suspend 函数的响应类型适配
suspend fun <T> 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)
}
})
}
}

suspend 函数在整个 Retrofit 流程中的位置

// HttpServiceMethod.parseAnnotations 中的判断
if (isKotlinSuspendFunction) {
// 使用 SuspendForResponse 或 SuspendForBody
boolean continuationWantsResponse = false;
boolean isNullable = false;
// 解析 suspend 函数的参数列表,找到 Continuation<T>
// 根据 T 的类型决定是返回 Response<T> 还是 T
// ...
}

5.4 RxJava 适配 —— RxJava2CallAdapterFactory

// retrofit2:adapter-rxjava2
public final class RxJava2CallAdapterFactory extends CallAdapter.Factory {
private final @Nullable Scheduler scheduler;
private final boolean isAsync;

@Override
public @Nullable CallAdapter<?, ?> get(Type returnType,
Annotation[] annotations, Retrofit retrofit) {
Class<?> rawType = getRawType(returnType);
if (rawType == Completable.class) {
return new RxJava2CallAdapter(Void.class, scheduler, isAsync,
false, true, false, true);
}
if (rawType == Maybe.class) {
return new RxJava2CallAdapter(getParameterUpperBound(0,
(ParameterizedType) returnType), scheduler, isAsync,
false, true, false, false);
}
if (rawType == Single.class) {
return new RxJava2CallAdapter(getParameterUpperBound(0,
(ParameterizedType) returnType), scheduler, isAsync,
false, false, false, true);
}
if (rawType == Observable.class) {
return new RxJava2CallAdapter(getParameterUpperBound(0,
(ParameterizedType) returnType), scheduler, isAsync,
false, false, true, false);
}
if (rawType == Flowable.class) {
return new RxJava2CallAdapter(getParameterUpperBound(0,
(ParameterizedType) returnType), scheduler, isAsync,
true, false, false, false);
}
return null;
}
}

// 适配逻辑 —— Call → Observable
final class RxJava2CallAdapter<R> implements CallAdapter<R, Object> {
@Override
public Object adapt(Call<R> call) {
Observable<Response<R>> responseObservable = isAsync
? new CallEnqueueObservable<>(call)
: new CallExecuteObservable<>(call);
// ...
}
}

六、OkHttpCall —— 实际网络调用

6.1 OkHttpCall 实现

源码位置retrofit2/OkHttpCall.java

final class OkHttpCall<T> implements Call<T> {
private final okhttp3.Call.Factory callFactory; // 通常是 OkHttpClient
private final Object[] args;
private final RequestFactory requestFactory;
private final Converter<ResponseBody, T> responseConverter;

private volatile @Nullable okhttp3.Call rawCall;
private boolean executed;
private volatile boolean canceled;

@Override
public Response<T> execute() throws IOException {
okhttp3.Call call;
synchronized (this) {
if (executed) throw new IllegalStateException("Already executed.");
executed = true;
call = getRawCall();
}

if (canceled) {
call.cancel();
}

return parseResponse(call.execute());
}

@Override
public void enqueue(final Callback<T> callback) {
okhttp3.Call call;
synchronized (this) {
if (executed) throw new IllegalStateException("Already executed.");
executed = true;
call = getRawCall();
}

if (canceled) {
call.cancel();
}

call.enqueue(new okhttp3.Callback() {
@Override
public void onResponse(okhttp3.Call call, okhttp3.Response rawResponse) {
Response<T> response;
try {
response = parseResponse(rawResponse);
} catch (Throwable e) {
throwIfFatal(e);
callFailure(e);
return;
}
try {
callback.onResponse(OkHttpCall.this, response);
} catch (Throwable t) {
throwIfFatal(t);
t.printStackTrace();
}
}

@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();
}
}
});
}

// 核心:创建 OkHttp 的 Request,设置给 OkHttpClient → OkHttpCall
private okhttp3.Call createRawCall() throws IOException {
Request request = requestFactory.create(args);
okhttp3.Call call = callFactory.newCall(request);
if (call == null) {
throw new NullPointerException("Call.Factory returned null.");
}
return call;
}

// 核心:解析 OkHttp Response 为 Retrofit Response
Response<T> parseResponse(okhttp3.Response rawResponse) throws IOException {
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 {
// 错误响应:缓冲 body 并关闭
ResponseBody bufferedBody = Utils.buffer(rawBody);
return Response.error(bufferedBody, rawResponse);
} finally {
rawBody.close();
}
}

if (code == 204 || code == 205) {
rawBody.close();
return Response.success(null, rawResponse);
}

// 成功响应:使用 Converter 反序列化
ExceptionCatchingResponseBody catchingBody =
new ExceptionCatchingResponseBody(rawBody);
try {
T body = responseConverter.convert(catchingBody);
return Response.success(body, rawResponse);
} catch (RuntimeException e) {
catchingBody.throwIfCaught();
throw e;
}
}
}

6.2 RequestFactory.create —— 构建 OkHttp Request

okhttp3.Request create(Object[] args) throws IOException {
ParameterHandler<Object>[] handlers = (ParameterHandler<Object>[]) parameterHandlers;

RequestBuilder builder = new RequestBuilder(
httpMethod, baseUrl, relativeUrl, headers, contentType, hasBody, isFormEncoded, isMultipart);

int argumentCount = args.length;
for (int p = 0; p < argumentCount; p++) {
handlers[p].apply(builder, args[p]);
}

return builder.get().tag(Invocation.class, new Invocation(method, args)).build();
}

ParameterHandler 的子类型

  • Header:添加请求头
  • Path:替换 URL 中的 {placeholder}
  • Query:添加 URL 查询参数
  • QueryName:动态查询参数名
  • QueryMap:批量添加查询参数
  • Field:表单字段
  • FieldMap:批量表单字段
  • Part:Multipart 部件
  • PartMap:批量 Multipart 部件
  • Body:请求体
  • RawPart:原始 Multipart 部件
  • Tag:为 Request 添加 Tag

七、错误处理

7.1 HttpException

// Retrofit 自动为非 2xx 响应抛出 HttpException
try {
val user = api.getUser("nonexistent") // 404
} catch (e: HttpException) {
when (e.code()) {
401 -> // 未授权
404 -> // 未找到
429 -> // 限流
500 -> // 服务器错误
}
// 可以获取错误响应体
val errorBody = e.response()?.errorBody()?.string()
} catch (e: IOException) {
// 网络异常(连接超时、DNS 失败等)
} catch (e: Exception) {
// 其他异常(序列化失败等)
}

7.2 Kotlin Result 包装

// 使用 sealed class 包装结果
sealed class NetworkResult<out T> {
data class Success<T>(val data: T) : NetworkResult<T>()
data class Error(val message: String, val code: Int? = null) : NetworkResult<Nothing>()
object Loading : NetworkResult<Nothing>()
}

// 安全调用扩展函数
suspend fun <T> safeApiCall(apiCall: suspend () -> T): NetworkResult<T> {
return try {
NetworkResult.Success(apiCall())
} catch (e: HttpException) {
NetworkResult.Error(
message = e.response()?.errorBody()?.string() ?: e.message(),
code = e.code()
)
} catch (e: IOException) {
NetworkResult.Error(message = "网络异常: ${e.message}")
} catch (e: Exception) {
NetworkResult.Error(message = "未知错误: ${e.message}")
}
}

// 使用
val result = safeApiCall { api.getUser("octocat") }
when (result) {
is NetworkResult.Success -> showUser(result.data)
is NetworkResult.Error -> showError(result.message)
is NetworkResult.Loading -> showLoading()
}

7.3 网络错误 vs 服务器错误

// 区分不同异常类型
when (exception) {
is UnknownHostException ->
"DNS 解析失败:请检查域名和网络连接"
is SocketTimeoutException ->
"连接超时:服务器响应过慢"
is ConnectException ->
"连接被拒绝:服务器不可用"
is SSLHandshakeException ->
"SSL 握手失败:证书问题或中间人攻击"
is HttpException -> {
when (exception.code()) {
400 -> "请求参数错误"
401 -> "未授权,请重新登录"
403 -> "权限不足"
404 -> "资源不存在"
429 -> "请求过于频繁"
500 -> "服务器内部错误"
502 -> "网关错误"
503 -> "服务暂不可用"
else -> "HTTP 错误: ${exception.code()}"
}
}
else -> "未知错误: ${exception.message}"
}

八、Interceptor 集成与 Base URL 解析

8.1 Retrofit 中的 Interceptor

Retrofit 本身不处理 Interceptor —— 它通过 callFactory 将请求委托给 OkHttpClient:

val okHttpClient = OkHttpClient.Builder()
.addInterceptor(loggingInterceptor)
.addInterceptor(authInterceptor)
.addNetworkInterceptor(cacheInterceptor)
.build()

val retrofit = Retrofit.Builder()
.baseUrl("https://api.example.com/")
.client(okHttpClient) // 传入配置好的 OkHttpClient
.addConverterFactory(GsonConverterFactory.create())
.build()

所有 OkHttp Interceptor 的能力都继承到 Retrofit。

8.2 Base URL 解析规则

Retrofit 对 baseUrl 的严格要求

// Retrofit.Builder.baseUrl()
public Builder baseUrl(HttpUrl baseUrl) {
Objects.requireNonNull(baseUrl, "baseUrl == null");
List<String> pathSegments = baseUrl.pathSegments();
// 最后一个路径段必须以 "/" 结尾
if (!"".equals(pathSegments.get(pathSegments.size() - 1))) {
throw new IllegalArgumentException("baseUrl must end in /: " + baseUrl);
}
this.baseUrl = baseUrl;
return this;
}

多 Base URL 的解决方案

// 方案一:@Url 动态指定
@GET
suspend fun downloadFile(@Url url: String): ResponseBody
api.downloadFile("https://cdn.example.com/file.pdf")

// 方案二:Interceptor 动态替换
class DynamicBaseUrlInterceptor : Interceptor {
override fun intercept(chain: Interceptor.Chain): Response {
val original = chain.request()
val newUrl = when {
original.url.host.contains("api") -> "https://api-v2.example.com"
original.url.host.contains("cdn") -> "https://cdn-new.example.com"
else -> return chain.proceed(original)
}
val request = original.newBuilder()
.url(original.url.toString().replace(original.url.host,
HttpUrl.parse(newUrl)!!.host))
.build()
return chain.proceed(request)
}
}

// 方案三:多个 Retrofit 实例
val apiRetrofit = createRetrofit("https://api.example.com/")
val cdnRetrofit = createRetrofit("https://cdn.example.com/")

九、Retrofit vs Ktor-client 对比

9.1 架构对比

特性 Retrofit Ktor-client
开发方 Square JetBrains
语言 Java (Kotlin 扩展) Kotlin (Kotlin Multiplatform)
平台 JVM/Android JVM/Android/iOS/JS/Native
声明方式 注解 + 接口 类型安全的 DSL
底层引擎 OkHttp 可插拔(OkHttp/CIO/Java/Darwin)
序列化 Gson/Moshi/Kotlinx.serialization Kotlinx.serialization
协程 Java Callback → suspend Kotlin 原生
学习曲线 平缓 中等

9.2 代码风格对比

Retrofit

interface ApiService {
@GET("users/{id}")
suspend fun getUser(@Path("id") id: String): User

@POST("users")
suspend fun createUser(@Body user: CreateUserRequest): User
}

Ktor-client

class ApiService(private val client: HttpClient) {
suspend fun getUser(id: String): User =
client.get("https://api.example.com/users/$id").body()

suspend fun createUser(user: CreateUserRequest): User =
client.post("https://api.example.com/users") {
contentType(ContentType.Application.Json)
setBody(user)
}.body()
}

选择建议

  • 纯 Android/JVM 项目 → Retrofit(生态成熟,团队熟悉)
  • Kotlin Multiplatform → Ktor-client(跨平台)
  • 纯 Compose Multiplatform → Ktor-client
  • 需要 SSE/WebSocket 内置支持 → Ktor-client
  • 需要与大量第三方库集成 → Retrofit

十、总结

Retrofit 用短短数千行核心代码,通过 Java 动态代理和精心设计的工厂/适配器模式,实现了类型安全的 HTTP 客户端框架。其核心设计哲学:

  1. 关注点分离:接口声明(注解)、请求构建(RequestFactory)、序列化(Converter)、适配(CallAdapter)、执行(OkHttpCall)每个环节职责清晰
  2. 可扩展性:Converter.Factory 和 CallAdapter.Factory 的抽象设计,使得序列化和返回值适配可以无限扩展
  3. 缓存方法解析结果serviceMethodCache 确保每个方法的注解仅解析一次
  4. 与 OkHttp 的深度整合:Interceptor、ConnectionPool、DNS、缓存等 OkHttp 能力全部可用
  5. Kotlin 协程原生支持suspend 函数通过 suspendCancellableCoroutine 转换 Call 为协程,支持取消

理解 Retrofit 的源码,是深入掌握 Java 动态代理、注解处理、网络框架设计的绝佳途径。


参考资源

打赏
  • 微信
  • 支付宝

评论