Retrofit 创建网络请求接口实例过程
作者:阿健叔 发布时间:2023-11-23 22:26:43
Retrofit 基本使用
implementation 'com.squareup.retrofit2:retrofit:2.9.0'
implementation 'com.squareup.retrofit2:converter-gson:2.9.0'
interface NetApi {
@GET("/hotkey/json")
fun getHotKey(): Call<Response>
companion object {
private const val BASE_URL = "https://www.wanandroid.com/"
fun createApi(): NetApi =
Retrofit.Builder().baseUrl(BASE_URL).addConverterFactory(GsonConverterFactory.create())
.build().create(NetApi::class.java)
}
}
data class HotWords(
val id: String,
val name: String,
)
data class Response(
val errorCode: Int,
val errorMsg: String,
val data: List<HotWords>
)
NetApi.createApi().getHotKey().enqueue(object : Callback<Response> {
override fun onResponse(call: Call<Response>, response: retrofit2.Response<Response>) {
Log.i(tag, "onResponse: ${response.body()?.data}")
}
override fun onFailure(call: Call<Response>, t: Throwable) {
Log.i(tag, "onFailure: ${t.message}")
}
})
这样一个基本的网络请求就搞定了,使用很简洁,正是因为其内部使用了大量的设计模式和优秀的架构设计,才得以使其如此方便地进行网络请求,下面来一起瞧瞧 Retrofit 的源码吧~
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<>();
// 网络请求适配器工厂的集合,默认是 ExecutorCallAdapterFactory
private final List<CallAdapter.Factory> callAdapterFactories = new ArrayList<>();
// 回调方法执行器,用于切换线程
private @Nullable Executor callbackExecutor;
// 一个开关,为 true 则会缓存创建的 ServiceMethod
private boolean validateEagerly;
...
public Builder baseUrl(String baseUrl) {
Objects.requireNonNull(baseUrl, "baseUrl == null");
return baseUrl(HttpUrl.get(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;
}
// 将一个含有 Gson 对象实例的 GsonConverterFactory 放入数据转换器工厂
public Builder addConverterFactory(Converter.Factory factory) {
converterFactories.add(Objects.requireNonNull(factory, "factory == null"));
return this;
}
...
}
通过 build,我们上面 Builder 类中的参数对象都配置到了 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();
}
// Make a defensive copy of the adapters and add the default Call adapter.
List<CallAdapter.Factory> callAdapterFactories = new ArrayList<>(this.callAdapterFactories);
callAdapterFactories.addAll(platform.defaultCallAdapterFactories(callbackExecutor));
// Make a defensive copy of the converters.
List<Converter.Factory> converterFactories =
new ArrayList<>(
1 + this.converterFactories.size() + platform.defaultConverterFactoriesSize());
// Add the built-in converter factory first. This prevents overriding its behavior but also
// ensures correct behavior when using converters that consume all types.
converterFactories.add(new BuiltInConverters());
converterFactories.addAll(this.converterFactories);
converterFactories.addAll(platform.defaultConverterFactories());
return new Retrofit(
callFactory,
baseUrl,
unmodifiableList(converterFactories),
unmodifiableList(callAdapterFactories),
callbackExecutor,
validateEagerly);
}
创建网络请求接口实例过程
使用 * 的方式拿到所有注解配置后,创建网络请求接口实例。
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 the method is a method from Object then defer to normal invocation.
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);
}
});
}
跟踪 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;
}
parseAnnotations 解析注解配置得到 ServiceMethod,然后加入到 serviceMethodCache 缓存中,是一个 ConcurrentHashMap 。
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 方法,解析接口方法上的注解,然后封装在 RequestFactory 对象中,将其返回,这个 RequestFactory,主要用于后续创建 OkHttp 请求所需要的 Request 对象。那后面的 HttpServiceMethod.parseAnnotations 又是干什么的呢?往下看。
static <ResponseT, ReturnT> HttpServiceMethod<ResponseT, ReturnT> parseAnnotations(
Retrofit retrofit, Method method, RequestFactory requestFactory) {
...
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);
}
}
如果不是 kotlin suspend 函数,使用 CallAdapted 类,如果是 kotlin suspend 函数返回类型是 Response,则使用 SuspendForResponse 类,其余情况使用 SuspendForBody,如 suspend 函数返回类型不是 Response 。一般情况下,我们使用的基本上是属于其余情况,我们来看下 SuspendForBody 类
static final class SuspendForBody<ResponseT> extends HttpServiceMethod<ResponseT, Object> {
private final CallAdapter<ResponseT, Call<ResponseT>> callAdapter;
private final boolean isNullable;
...
@Override
protected Object adapt(Call<ResponseT> call, Object[] args) {
call = callAdapter.adapt(call);
Continuation<ResponseT> continuation = (Continuation<ResponseT>) args[args.length - 1];
try {
return isNullable
? KotlinExtensions.awaitNullable(call, continuation)
: KotlinExtensions.await(call, continuation);
} catch (Exception e) {
return KotlinExtensions.suspendAndThrow(e, continuation);
}
}
}
跟进 KotlinExtensions.awaitNullable,我们可以看到 SuspendForBody 会将 Response.body 作为协程挂起点的返回值。
suspend fun <T : Any> Call<T?>.await(): T? {
return suspendCancellableCoroutine { continuation ->
//协程取消是调用 cancel
continuation.invokeOnCancellation {
cancel()
}
enqueue(object : Callback<T?> {
override fun onResponse(call: Call<T?>, response: Response<T?>) {
if (response.isSuccessful) {
//继续执行相应的协程,将 response.body 作为最后一个挂起点的返回值。
continuation.resume(response.body())
} else {
continuation.resumeWithException(HttpException(response))
}
}
override fun onFailure(call: Call<T?>, t: Throwable) {
continuation.resumeWithException(t)
}
})
}
}
执行请求过程
public void enqueue(final Callback<T> callback) {
Objects.requireNonNull(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 {
// 创建 OkHttp 的 Call 对象
call = rawCall = createRawCall();
} catch (Throwable t) {
throwIfFatal(t);
failure = creationFailure = t;
}
}
}
if (failure != null) {
callback.onFailure(this, failure);
return;
}
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(); // 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
}
}
});
}
Response<T> parseResponse(okhttp3.Response rawResponse) throws IOException {
ResponseBody rawBody = rawResponse.body();
// Remove the body's source (the only stateful object) so we can pass the response along.
rawResponse =
rawResponse
.newBuilder()
.body(new NoContentResponseBody(rawBody.contentType(), rawBody.contentLength()))
.build();
int code = rawResponse.code();
// 根据响应返回的状态码进行处理
if (code < 200 || code >= 300) {
try {
// Buffer the entire body to avoid future I/O.
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);
}
//包装 RequestBody
ExceptionCatchingResponseBody catchingBody = new ExceptionCatchingResponseBody(rawBody);
try {
// 将响应体转为 Java 对象
T body = responseConverter.convert(catchingBody);
return Response.success(body, rawResponse);
} catch (RuntimeException e) {
// If the underlying source threw an exception, propagate that rather than indicating it was
// a runtime exception.
catchingBody.throwIfCaught();
throw e;
}
}
来源:https://juejin.cn/post/7178010480672751653


猜你喜欢
- 本文实例讲述了C#中WPF使用多线程调用窗体组件的方法。分享给大家供大家参考。具体如下:Thread thread=new Thread(n
- 今天写一个小程序中使用到了全局快捷键,找到了我之前写的文章在c#中使用全局快捷键翻了一下,发现它是WinForm版本的,而我现在大部分写WP
- 1.将本地jar包放入本地仓库。只需执行如下命令即可:mvn install:install-file -Dfile=D:/demo/fib
- Java下常见的Json类库有Gson、JSON-lib和Jackson等,Jackson相对来说比较高效,在项目中主要使用Jackson进
- 什么是代理模式代理模式是开发中常见的一种设计模式,使用代理模式可以很好的对程序进行横向扩展。代理,顾名思义就是一个真实对象会存在一个代理对象
- 文件目录结构文件目录结构很重要,特别注意的是rule文件要放在主启动类上一级位置,才能够扫描。写pom<dependencies>
- createcriteria和or的区别mybatis generator插件生成的example中,有createcriteria和or方
- java中Path是什么?在计算机上安装Java后,需要设置PATH环境变量以便从任何目录方便地运行可执行文件(javac.exe,java
- 新版本的xlsx是使用新的存储格式,貌似是处理过的XML。对于OpenXML我网上搜了一下,很多人没有介绍。所以我就这里推荐下,相信会成为信
- 前言上一篇我们介绍了 Animation 和 AnimationController 的使用,这是最
- string filePath = @"E:\Randy0528\中文目录\JustTest.rar"; &n
- 本文为大家分享了Android仿小度语音助手的贝塞尔曲线动画,供大家参考,具体内容如下废话不多说,看下面的动图,和百度的还是有点点差别,我也
- Csv文件基本的属性csv文件可以在excel或者wps中以表格形式打开,本质上是每一列以,逗号为分隔符的一种格式,在C#中操作可以把他当做
- Android Q 禁止获取 IMEI 和设备序列号;官方的解决方案是这样:https://developer.android.com/tr
- Flutter中的生命周期类似于Vue、React中的生命周期一样,有初始化、状态更新、停用、销毁等。在React中,组件分为函数式组件和类
- Android在自定义控件时,经常需要获得屏幕的宽高,每次都要写,不妨直接把他封装成工具类,直接拿来用,废话不说,直接上代码/** * &n
- 1. Spring Boot 入门 Spring Boot是Spring社区较新的一个项目。该项目的目的是帮助开发者更容易的创建基于Spri
- 结束firefox的进程,一句代码就够了,如下:Runtime.getRuntime().exec("taskkill /F /I
- 一般在web应用中,对客户端提交上来的图片肯定需要进行压缩的。尤其是比较大的图片,如果不经过压缩会导致页面变的很大,打开速度比较慢,影响用户
- Java多线程下载网图案例此案例依赖——文件操作工具类(FileUtils)使用 apache 的commons-io包下的FileUtil