一、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") 的背后发生了什么:
retrofit.create() 通过 Proxy.newProxyInstance 创建了 GitHubApi 的动态代理
调用 listRepos("octocat") 时,实际的调用被路由到 InvocationHandler.invoke()
invoke() 中通过 loadServiceMethod(method) 解析方法上的注解
ServiceMethod.parseAnnotations() 解析 @GET("users/{user}/repos") 和 @Path("user")
因为是 suspend 函数,HttpServiceMethod 使用 KotlinExtensions.await() 适配
构建 OkHttpCall,调用 call.enqueue() 发起异步网络请求
响应返回后,Converter 将 Response Body(JSON)反序列化为 List<Repo>
CallAdapter 将 Call<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 { 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); } }); } 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." ); } 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()); } if (validateEagerly) { for (Method method : service.getDeclaredMethods()) { if (!platform.isDefaultMethod(method) && !Modifier.isStatic(method.getModifiers())) { loadServiceMethod(method); } } } } }
关键点 :
serviceMethodCache 是 ConcurrentHashMap,线程安全,且方法解析只做一次
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) { RequestFactory requestFactory = RequestFactory.parseAnnotations(retrofit, method); 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." ); } 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; final HttpUrl baseUrl; final String relativeUrl; final Headers headers; final MediaType contentType; final boolean hasBody; final boolean isFormEncoded; final boolean isMultipart; final ParameterHandler<?>[] parameterHandlers; final boolean isKotlinSuspendFunction; }
三、注解处理 —— 从接口到 HTTP 请求 3.1 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 "" ; }@Documented @Target(METHOD) @Retention(RUNTIME) public @interface HTTP { String method () ; 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>@GET("users/{name}" ) suspend fun getUser (@Path(value = "name" , encoded = true) name: String ) : User
源码处理 (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@GET("list" ) suspend fun getList (@Query("filter" ) filter: String ?) : List<Item>@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 ; 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@FormUrlEncoded @POST("profile" ) suspend fun updateProfile (@FieldMap fields: Map <String , String>) : User
@Body —— 请求体 @POST("users" ) suspend fun createUser (@Body user: CreateUserRequest ) : User
源码约束检查 (RequestFactory.parseParameter):
if (gotBody) { throw parameterError(method, p, "Multiple @Body method annotations found." ); }
@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>@GET("users" ) suspend fun getUsers (@HeaderMap headers: Map <String , String>) : List<User>
@Url —— 动态 URL @GET suspend fun downloadFile (@Url url: String ) : ResponseBody
HttpUrl.resolve 的解析逻辑 :
public HttpUrl resolve (String link) { Builder builder = newBuilder(link); return builder != null ? builder.build() : null ; }
@Multipart / @Part —— 文件上传 @Multipart @POST("upload" ) suspend fun uploadFile ( @Part file: MultipartBody .Part , @Part("description" ) description: RequestBody ) : UploadResponseval 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 { public @Nullable Converter<?, RequestBody> requestBodyConverter( Type type, Annotation[] parameterAnnotations, Annotation[] methodAnnotations, Retrofit retrofit) { return null ; } public @Nullable Converter<ResponseBody, ?> responseBodyConverter( Type type, Annotation[] annotations, Retrofit retrofit) { return null ; } } }
4.2 GsonConverterFactory 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 项目中):
@JsonClass(generateAdapter = true) data class Repo ( val id: Long , val name: String, @Json(name = "full_name" ) val fullName: String )
4.4 Kotlinx.serialization —— Kotlin 原生的序列化 @Serializable data class Repo ( val id: Long , val name: String, @SerialName("full_name" ) val fullName: String ) 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" ); 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> { Type responseType () ; T adapt (Call<R> call) ; abstract class Factory { public abstract @Nullable CallAdapter<?, ?> get( Type returnType, Annotation[] annotations, Retrofit retrofit); } }
5.2 默认 CallAdapter —— 直接返回 Call final class DefaultCallAdapterFactory extends CallAdapter .Factory { @Override 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 = 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 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 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 流程中的位置 :
if (isKotlinSuspendFunction) { boolean continuationWantsResponse = false ; boolean isNullable = false ; }
5.4 RxJava 适配 —— RxJava2CallAdapterFactory 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 ; } } 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; 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(); } } }); } 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; } 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 { 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); } 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 try { val user = api.getUser("nonexistent" ) } catch (e: HttpException) { when (e.code()) { 401 -> 404 -> 429 -> 500 -> } val errorBody = e.response()?.errorBody()?.string() } catch (e: IOException) { } catch (e: Exception) { }
7.2 Kotlin Result 包装 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) .addConverterFactory(GsonConverterFactory.create()) .build()
所有 OkHttp Interceptor 的能力都继承到 Retrofit。
8.2 Base URL 解析规则 Retrofit 对 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 的解决方案 :
@GET suspend fun downloadFile (@Url url: String ) : ResponseBodyapi.downloadFile("https://cdn.example.com/file.pdf" ) 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) } } 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 客户端框架。其核心设计哲学:
关注点分离 :接口声明(注解)、请求构建(RequestFactory)、序列化(Converter)、适配(CallAdapter)、执行(OkHttpCall)每个环节职责清晰
可扩展性 :Converter.Factory 和 CallAdapter.Factory 的抽象设计,使得序列化和返回值适配可以无限扩展
缓存方法解析结果 :serviceMethodCache 确保每个方法的注解仅解析一次
与 OkHttp 的深度整合 :Interceptor、ConnectionPool、DNS、缓存等 OkHttp 能力全部可用
Kotlin 协程原生支持 :suspend 函数通过 suspendCancellableCoroutine 转换 Call 为协程,支持取消
理解 Retrofit 的源码,是深入掌握 Java 动态代理、注解处理、网络框架设计的绝佳途径。
参考资源