跳到主要内容Android 网络请求框架 Retrofit 源码深度解析 | 极客日志Java大前端java
Android 网络请求框架 Retrofit 源码深度解析
深入分析了 Android 网络请求框架 Retrofit 的源码实现。内容涵盖基本使用流程、Retrofit 构建过程中的建造者与工厂模式、创建接口实例时的动态代理与外观模式、以及请求执行的同步与异步流程。文章详细解读了 ServiceMethod 解析、CallAdapter 与 Converter 的查找机制,并补充了线程调度、错误处理及性能优化等最佳实践。通过源码剖析,揭示了 Retrofit 如何基于 OkHttp 封装出简洁易用的 API。
GopherDev1 浏览 Android 网络请求框架 Retrofit 源码深度解析
一、基本使用流程
1. 定义 HTTP API,用于描述请求
首先定义一个接口来描述网络请求。方法上的注解表示请求的接口部分,返回类型是请求的返回值类型,方法的参数即为请求的参数。
public interface GitHubService {
@GET("users/{user}/repos")
Call<List<Repo>> listRepos(@Path("user") String user);
}
2. 创建 Retrofit 并生成 API 的实现
Retrofit 构建过程需要配置 BaseUrl,然后创建网络请求接口类实例。
Retrofit retrofit = new Retrofit.Builder()
.baseUrl("https://api.github.com/")
.build();
GitHubService service = retrofit.create(GitHubService.class);
3. 调用 API 方法,生成 Call,执行请求
Call<List<Repo>> repos = service.listRepos("octocat");
repos.execute()
repos.enqueue()
Retrofit 的基本使用流程很简洁,但内部架构设计优秀,使用了大量的设计模式。要想真正理解 Retrofit 内部的核心源码流程和设计思想,需要对以下九大设计模式有一定的了解:
- Retrofit 构建过程:建造者模式、工厂方法模式。
- 创建网络请求接口实例过程:外观模式、代理模式、单例模式、策略模式、装饰模式(建造者模式)。
- 生成并执行请求过程:适配器模式(代理模式、装饰模式)。
此外,还需要对 OkHttp 源码有一定的了解。下面按以上流程深入 Retrofit 源码内部。
二、Retrofit 构建过程
1. Retrofit 核心对象解析
Retrofit 中有一个全局变量非常关键,在 V2.5 之前的版本,使用的是 LinkedHashMap,它是一个网络请求配置对象,由网络请求接口中方法注解解析后得到。
public final class Retrofit {
private final Map<Method, ServiceMethod<?>> serviceMethodCache = new ConcurrentHashMap<>();
}
Retrofit 使用了建造者模式通过内部类 Builder 建立一个 Retrofit 实例。
public static final class Builder {
private final Platform platform;
private @Nullable okhttp3.Call.Factory callFactory;
private @Nullable HttpUrl baseUrl;
private final List<Converter.Factory> converterFactories = new ArrayList<>();
private final List<CallAdapter.Factory> callAdapterFactories = new ArrayList<>();
private @Nullable Executor callbackExecutor;
private boolean validateEagerly;
}
2. Builder 内部构造
Builder 内部构造时设置了默认 Platform、callAdapterFactories 和 callbackExecutor。
public static final class Builder {
Builder(Platform platform) {
this.platform = platform;
}
public Builder() {
this(Platform.get());
}
}
class Platform {
private static final Platform PLATFORM = findPlatform();
static Platform get() {
return PLATFORM;
}
private static Platform findPlatform() {
try {
Class.forName("android.os.Build");
if (Build.VERSION.SDK_INT != 0) {
return new Android();
}
} catch (ClassNotFoundException ignored) {
}
try {
Class.forName("java.util.Optional");
return new Java8();
} catch (ClassNotFoundException ignored) {
}
return new Platform();
}
static class Android extends Platform {
@Override public Executor defaultCallbackExecutor() {
return new MainThreadExecutor();
}
@Override List<? extends CallAdapter.Factory> defaultCallAdapterFactories(
@Nullable Executor callbackExecutor) {
if (callbackExecutor == null) throw new AssertionError();
ExecutorCallAdapterFactory executorFactory = new ExecutorCallAdapterFactory(callbackExecutor);
return Build.VERSION.SDK_INT >= 24
? asList(CompletableFutureCallAdapterFactory.INSTANCE, executorFactory)
: singletonList(executorFactory);
}
static class MainThreadExecutor implements Executor {
private final Handler handler = new Handler(Looper.getMainLooper());
@Override public void execute(Runnable r) {
handler.post(r);
}
}
}
}
3. 添加 baseUrl
将 String 类型的 url 转换为 OkHttp 的 HttpUrl 过程如下:
public Builder baseUrl(String baseUrl) {
checkNotNull(baseUrl, "baseUrl == null");
return baseUrl(HttpUrl.get(baseUrl));
}
public Builder baseUrl(HttpUrl baseUrl) {
checkNotNull(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;
}
4. 添加 GsonConverterFactory
GsonConverterFactory 用于处理 JSON 数据的转换。
public final class GsonConverterFactory extends Converter.Factory {
public static GsonConverterFactory create() {
return create(new Gson());
}
public static GsonConverterFactory create(Gson gson) {
if (gson == null) throw new NullPointerException("gson == null");
return new GsonConverterFactory(gson);
}
private final Gson gson;
private GsonConverterFactory(Gson gson) {
this.gson = gson;
}
}
addConverterFactory() 方法内部将含有 Gson 对象实例的 GsonConverterFactory 放入到了数据转换器工厂 converterFactories 里。
public Builder addConverterFactory(Converter.Factory factory) {
converterFactories.add(checkNotNull(factory, "factory null"));
return this;
}
5. build 过程
最终在 Builder 类中看到的 6 大核心对象都已经配置到 Retrofit 对象中了。
public Retrofit build() {
if (baseUrl == null) {
throw new IllegalStateException("Base URL required.");
}
okhttp3.Call.Factory callFactory = this.callFactory;
if (callFactory == null) {
callFactory = new OkHttpClient();
}
Executor callbackExecutor = this.callbackExecutor;
if (callbackExecutor == null) {
callbackExecutor = platform.defaultCallbackExecutor();
}
List<CallAdapter.Factory> callAdapterFactories = new ArrayList<>(this.callAdapterFactories);
callAdapterFactories.addAll(platform.defaultCallAdapterFactories(callbackExecutor));
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);
}
三、创建网络请求接口实例过程
retrofit.create() 使用了外观模式和代理模式创建了网络请求的接口实例。
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 Object invoke(Object proxy, Method method, @Nullable Object[] args)
throws Throwable {
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);
}
});
}
loadServiceMethod 内部流程解析注解配置并缓存:
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;
}
请求构造核心流程
根据 RequestFactory#Builder 构造方法和 parseAnnotations 方法的源码,它的作用就是用来解析注解配置的。
Builder(Retrofit retrofit, Method method) {
this.retrofit = retrofit;
this.method = method;
this.methodAnnotations = method.getAnnotations();
this.parameterTypes = method.getGenericParameterTypes();
this.parameterAnnotationsArray = method.getParameterAnnotations();
}
HttpServiceMethod.parseAnnotations() 的内部流程获取适配器和转换器:
static <ResponseT, ReturnT> HttpServiceMethod<ResponseT, ReturnT> parseAnnotations(
Retrofit retrofit, Method method, RequestFactory requestFactory) {
CallAdapter<ResponseT, ReturnT> callAdapter = createCallAdapter(retrofit, method);
Type responseType = callAdapter.responseType();
Converter<ResponseBody, ResponseT> responseConverter =
createResponseConverter(retrofit, method, responseType);
okhttp3.Call.Factory callFactory = retrofit.callFactory;
return new HttpServiceMethod<>(requestFactory, callFactory, callAdapter, responseConverter);
}
1. createCallAdapter
遍历 CallAdapter.Factory 集合寻找合适的工厂。
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;
}
}
}
2. createResponseConverter
遍历 Converter.Factory 集合并寻找合适的工厂,这里是 GsonResponseBodyConverter。
public <T> Converter<ResponseBody, T> nextResponseBodyConverter(
@Nullable Converter.Factory skipPast, Type type, Annotation[] annotations) {
int start = converterFactories.indexOf(skipPast) + 1;
for (int i = start, count = converterFactories.size(); i < count; i++) {
Converter<ResponseBody, ?> converter =
converterFactories.get(i).responseBodyConverter(type, annotations, this);
if (converter != null) {
return (Converter<ResponseBody, T>) converter;
}
}
}
最终执行 HttpServiceMethod 的 invoke 方法,在 adapt 中创建了一个 ExecutorCallbackCall 对象,它是一个装饰者,而在它内部真正去执行网络请求的还是 OkHttpCall。
四、创建网络请求接口类实例并执行请求过程
1. service.listRepos()
service 对象是动态代理对象 Proxy.newProxyInstance(),当调用方法时会被它拦截,然后调用自身的 InvocationHandler#invoke(),得到最终的 Call 对象。
2. 同步执行流程 repos.execute()
@Override public Response<T> execute() throws IOException {
okhttp3.Call call;
synchronized (this) {
if (executed) throw new IllegalStateException("Already executed.");
executed = true;
if (creationFailure != null) {
if (creationFailure instanceof IOException) {
throw (IOException) creationFailure;
} else if (creationFailure instanceof RuntimeException) {
throw (RuntimeException) creationFailure;
} else {
throw (Error) creationFailure;
}
}
call = rawCall;
if (call == null) {
try {
call = rawCall = createRawCall();
} catch (IOException | RuntimeException | Error e) {
throwIfFatal(e);
creationFailure = e;
throw e;
}
}
}
if (canceled) {
call.cancel();
}
return parseResponse(call.execute());
}
parseResponse 方法处理响应体,根据状态码判断是否成功,并使用转换器将响应体转为 Java 对象。
3. 异步请求流程 response.enqueue()
@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(new Runnable() {
@Override
public void run() {
if (delegate.isCanceled()) {
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);
}
});
}
});
}
delegate.enqueue 内部调用 OkHttp 的异步请求机制,并在回调中通过 callbackExecutor 进行线程切换,确保 UI 更新在主线程。
五、总结与最佳实践
从本质上来说,Retrofit 虽然只是一个 RESTful HTTP 网络请求框架的封装库,但它内部通过大量的设计模式封装了 OkHttp,让使用者感到它非常简洁、易懂。它内部主要是用动态代理的方式,动态将网络请求接口的注解解析成 HTTP 请求,最后执行请求的过程。
1. 线程调度机制
Retrofit 默认使用 MainThreadExecutor 将回调切换回主线程,这在 Android 开发中至关重要。开发者可以通过自定义 Executor 或 CallAdapter 来改变这一行为,例如在 RxJava 中结合 Scheduler 使用。
2. 错误处理
Retrofit 的 Response 对象包含了状态码和错误信息。对于非 2xx 的状态码,Retrofit 会抛出异常或通过 Response.error() 返回。建议在 onFailure 中统一处理网络异常,在 onResponse 中检查 isSuccessful() 以处理业务逻辑错误。
3. 性能优化
- 连接池复用:Retrofit 底层依赖 OkHttp,应合理配置 OkHttp 的 ConnectionPool 以减少握手开销。
- 缓存策略:利用 OkHttp 的缓存功能,在网络不稳定时提升用户体验。
- 拦截器链:合理使用 Interceptor 链,避免在循环中重复创建对象,注意日志打印的性能影响。
至此,我们的 Android 主流三方库源码分析的网络库分析部分已经完毕。接下来,将为大家带来最流行的图片加载框架 Glide 的源码分析,敬请期待。
相关免费在线工具
- Keycode 信息
查找任何按下的键的javascript键代码、代码、位置和修饰符。 在线工具,Keycode 信息在线工具,online
- Escape 与 Native 编解码
JavaScript 字符串转义/反转义;Java 风格 \uXXXX(Native2Ascii)编码与解码。 在线工具,Escape 与 Native 编解码在线工具,online
- JavaScript / HTML 格式化
使用 Prettier 在浏览器内格式化 JavaScript 或 HTML 片段。 在线工具,JavaScript / HTML 格式化在线工具,online
- JavaScript 压缩与混淆
Terser 压缩、变量名混淆,或 javascript-obfuscator 高强度混淆(体积会增大)。 在线工具,JavaScript 压缩与混淆在线工具,online
- Base64 字符串编码/解码
将字符串编码和解码为其 Base64 格式表示形式即可。 在线工具,Base64 字符串编码/解码在线工具,online
- Base64 文件转换器
将字符串、文件或图像转换为其 Base64 表示形式。 在线工具,Base64 文件转换器在线工具,online