读懂 Retrofit 原理~
什么是 REST ful API?
一句话概括 REST ful API:在我们使用 HTTP 协议做数据传输时应当遵守 HTTP 的规矩,包括请求方法、资源类型、Uri 格式等等..
为什么将请求设置为(接口+注解)形式?
迪米特法则
:也称之为最小知道原则,即模块之间尽量减少不必要的依赖,即降低模块间的耦合性。
门面模式
:基于迪米特法则拓展出来的一种设计模式,旨在将复杂的模块/系统访问入口控制的更加单一。举个例子:现要做一个获取图片功能,优先从本地缓存获取,没有缓存从网络获取随后再加入到本地缓存,假如不做任何处理,那每获取一张图片都要写一遍缓存逻辑,写的越多出错的可能就越高,其实调用者只是想获取一张图片而已,具体如何获取他不需要关心。此时可以通过门面模式将缓存功能做一个封装,只暴露出一个获取图片入口,这样调用者使用起来更加方便而且安全性更高。其实函数式编程也是门面模式的产物。
为什么通过门面模式设计 ApiService
?
用 Retrofit
做一次请求大致流程如下:
interface ApiService {
/**
* 获取首页数据
*/
@GET("/article/list/{page}/json")
suspend fun getHomeList(@Path("page") pageNo: Int)
: ApiResponse<ArticleBean>
}
//构建Retrofit
val retrofit = Retrofit.Builder().build()
//创建ApiService实例
val apiService =retrofit.create(ApiService::class.java)
//发起请求(这里用的是suspend会自动发起请求,Java中可通过返回的call请求)
apiService.getHomeList(1)
我们都知道 Retrofit
只不过是对 OkHttp
做了封装。如果直接使用 OkHttp
,当在构造 Request
时要做很多繁琐的工作,最要命的是 Request 可能在多处被构造(ViewModel、Repository...),写的越分散出错时排查的难度就越高。而 Retrofit 通过注解的形式将 Request 需要的必要信息全依附在方法上(还是个抽象方法,尽量撇除一切多余信息),作为使用者只需要调用对应方法即可实现请求。至于如何解析、构造、发起请求 Retrofit 内部会做处理,调用者不想也不需要知道,所以 Retrofit 通过门面模式帮调用者屏蔽了一些无用信息,只暴露出唯一入口,让调用者更专注于业务开发。像我们常用的 Room、GreenDao 也使用了这种模式。
动态代理其实不是工具
Retrofit 构建如下所示:
Retrofit.Builder()
.client(okHttpClient)
.addConverterFactory(GsonConverterFactory.create())
.addCallAdapterFactory(RxJava2CallAdapterFactory.create())
.baseUrl(ApiConstants.BASE_URL)
.build()
很典型的构建者模式,可以配置 OkHttp
、Gson
、RxJava
等等,最后通过build()
做构建操作,跟一下 build()
代码:
# Retrofit.class
public Retrofit build() {
// 1. CallAdapter 工厂集合
List<CallAdapter.Factory> callAdapterFactories = new ArrayList<>(this.callAdapterFactories);
callAdapterFactories.addAll(platform.defaultCallAdapterFactories(callbackExecutor));
// 2. Converter 工厂集合
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);
}
什么是动态代理?
动态代理是可以在运行期动态创建某个 interface
的实例,我们通过 Proxy.newProxyInstance
产生的代理类,当调用接口的任何方法时,都会被InvocationHandler#invoke
方法拦截,同时,在这个方法中可以拿到所传入的参数等,依照参数值再做相应的处理。
每次 CRUD 都会手动做一次上报操作,这显然是模版代码,如何解决?下面来看动态代理:
interface ToDo {
fun buyHouse()
}
/**
* 中介
*/
class Middlemen(private val any: Any) : InvocationHandler {
override fun invoke(proxy: Any?, method: Method?, args: Array<out Any>?): Any? {
return method?.invoke(any, *(args ?: arrayOfNulls<Any>(0)))
}
}
/**
* 买家
*/
class Buyer : ToDo {
override fun buyHouse() {
print("中介,请帮我买房子")
}
}
// 最后利用代理对象实现购房的动作:
fun main(args: Array<String>) {
val toDo: ToDo = Buyer()
val dynamicMid = Proxy.newProxyInstance(
toDo.javaClass.classLoader, toDo.javaClass.interfaces,
Middlemen(toDo)
) as ToDo
dynamicMid.buyHouse()
}
动态代理最主要的部分在于代理对象实现 InvocationHandler
,并重写 invoke
方法。 当代理对象代理了委托者的要求,不管要求有多少,当代理执行时,都会走进 invoke()
方法中。这是重点,圈起来后面要考。
动态代理获取 ApiService
# Retrofit.class
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 (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);
}
});
}
invoke() 这是一个代理方法,调用
ApiService中
的任一方法并执行,其中参数method
和args
代表ApiService
对应的方法和参数。返回值中有一个isDefaultMethod
,这里如果是 Java8 的默认方法直接执行,毕竟我们只需要代理ApiService
中方法即可。经过反复筛选最后重任落在了loadServiceMethod
,这也是Retrofit
中最核心的一个方法,下面我们来跟一下:
loadServiceMethod(method)
它主要是将网络请求方法中的信息进行初步的处理,我们在创建 api service 具体接口时,会加上注解( @GET
,@POST
,@PUT
...),参数( @Path
、@Query
... )等,该方法就是对接口中的注解、参数等进行解析,解析接口后又生成了一个 RequestFactory
请求工厂对象,并且利用这个 RequestFactory
对象创建了一个 CallAdapter
。
CallAdapter
: 适配器,我们默认定义API Service
方法的返回值为Call
类型,但是有时候会自定义返回类型,例如和RxJava
相结合,返回Observable
或者Single
类型的时候应该怎么处理?CallAdapter
的作用就是帮助开发者去适配这些返回类型,你定义了什么类型的数据,就可以通过CallAdapter#adapt
进行返回。
# Retrofit.class
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;
}
创建 ServiceMethod
对象是通过其静态方法 parseAnnotations
实现的,再跟一下这个方法:
# HttpServiceMethod.class
static <ResponseT, ReturnT> HttpServiceMethod<ResponseT, ReturnT> parseAnnotations(
Retrofit retrofit, Method method, RequestFactory requestFactory) {
boolean isKotlinSuspendFunction = requestFactory.isKotlinSuspendFunction;
boolean continuationWantsResponse = false;
boolean continuationBodyNullable = false;
Annotation[] annotations = method.getAnnotations();
Type adapterType;
//1.获取adapterType,默认为method返回值类型
if (isKotlinSuspendFunction) {
Type[] parameterTypes = method.getGenericParameterTypes();
Type responseType =
Utils.getParameterLowerBound(
0, (ParameterizedType) parameterTypes[parameterTypes.length - 1]);
if (getRawType(responseType) == Response.class && responseType instanceof ParameterizedType) {
// Unwrap the actual body type from Response<T>.
responseType = Utils.getParameterUpperBound(0, (ParameterizedType) responseType);
continuationWantsResponse = true;
} else {
}
adapterType = new Utils.ParameterizedTypeImpl(null, Call.class, responseType);
annotations = SkipCallbackExecutorImpl.ensurePresent(annotations);
} else {
adapterType = method.getGenericReturnType();
}
//2.创建CallAdapter
CallAdapter<ResponseT, ReturnT> callAdapter =
createCallAdapter(retrofit, method, adapterType, annotations);
Type responseType = callAdapter.responseType();
//3.创建responseConverter
Converter<ResponseBody, ResponseT> responseConverter =
createResponseConverter(retrofit, method, responseType);
okhttp3.Call.Factory callFactory = retrofit.callFactory;
//4.创建HttpServiceMethod类型具体实例
if (!isKotlinSuspendFunction) {
return new HttpServiceMethod.CallAdapted<>(requestFactory, callFactory, responseConverter, callAdapter);
}
//兼容kotlin suspend方法
else if (continuationWantsResponse) {
//noinspection unchecked Kotlin compiler guarantees ReturnT to be Object.
return (HttpServiceMethod<ResponseT, ReturnT>)
new HttpServiceMethod.SuspendForResponse<>(
requestFactory,
callFactory,
responseConverter,
(CallAdapter<ResponseT, Call<ResponseT>>) callAdapter);
} else {
//noinspection unchecked Kotlin compiler guarantees ReturnT to be Object.
return (HttpServiceMethod<ResponseT, ReturnT>)
new HttpServiceMethod.SuspendForBody<>(
requestFactory,
callFactory,
responseConverter,
(CallAdapter<ResponseT, Call<ResponseT>>) callAdapter,
continuationBodyNullable);
}
}
# ServiceMethod.class
static <T> ServiceMethod<T> parseAnnotations(Retrofit retrofit, Method method) {
// ⚠️
RequestFactory requestFactory =
RequestFactory.parseAnnotations(retrofit, method);
Type returnType = method.getGenericReturnType();
...
// ⚠️
return HttpServiceMethod.parseAnnotations(retrofit,
method, requestFactory);
}
# HttpServiceMethod.class
@Override
final @Nullable ReturnT invoke(Object[] args) {
Call<ResponseT> call = new OkHttpCall<>(requestFactory, args, callFactory, responseConverter);
return adapt(call, args);
}
OkHttpCall
其实是对 OkHttp
中的 realCall
进行了一层包装,realCall
是如何创建的?
先创建
OkHttpClient
和Request
对象,以Request
来创建一个RealCall
对象,利用它执行异步enqueue
或者同步execute
操作将请求发送出去,并监听请求失败或者成功的反馈Callback
。
val okHttpClient = OkHttpClient()
val request: Request = Request.Builder()
.url("https://cn.bing.com/")
.build()
okHttpClient.newCall(request).enqueue(object : Callback{
override fun onFailure(call: Call, e: IOException) {
}
override fun onResponse(call: Call, response: Response) {
}
})
这里有三个主要的类需要说明一下:OkHttpClient
、Request
以及 RealCall
。
-
OkHttpClient
: 相当于配置中⼼,可用于发送 HTTP 请求并读取其响应。它的配置有很多,例如 connectTimeout:建⽴连接(TCP 或 TLS)的超时时间,readTimeout :发起请求到读到响应数据的超时时间,Dispatcher:调度器,⽤于调度后台发起的⽹络请求,等等。还有其他配置可查看源码。 -
Request
: 一个主要设置网络请求 Url、请求方法(GET、POST......)、请求头、请求 body 的请求类。 -
RealCall
:RealCall
是由newCall(Request)
方法返回,是OkHttp
执行请求最核心的一个类之一,用作连接OkHttp
的应用程序层和网络层,也就是将OkHttpClient
和Request
结合起来,发起异步和同步请求。
loadServiceMethod(method).invoke(args);
, loadServiceMethod(method)
返回的是 HttpServiceMethod
, invoke()
创建了一个 OkHttpCall
实例,它内部其实就是对 OkHttp
的一系列操作。
我们再来看看 OkHttpCall
中网络请求的细节:
@Override
public void enqueue(final Callback<T> callback) {
Objects.requireNonNull(callback, "callback == null");
okhttp3.Call call;
synchronized (this) {
...
if (call == null && failure == null) {
try {
call = rawCall = createRawCall();
}
}
...
call.enqueue(
new okhttp3.Callback() {
@Override
public void onResponse(okhttp3.Call call, okhttp3.Response rawResponse) {
Response<T> response;
try {
//解析请求返回值
response = parseResponse(rawResponse);
}
...
try {
callback.onResponse(OkHttpCall.this, response);
}
@Override
public void onFailure(okhttp3.Call call, IOException e) {
callFailure(e);
}
private void callFailure(Throwable e) {
try {
callback.onFailure(OkHttpCall.this, e);
}
}
});
}
分析上面的源码:
1,内部首先调用了一个 createRawCall()
方法,创建 rawCall
,这个 rawCall
其实指代的就是 Okhttp3
的 call,也就是 OkHttp
进行网络请求调用的一个调度器;
2,创建好 OkHttp 的 call 后,就开始调用 enqueue
进行异步请求,发现在异步请求内响应的回调属于 okhttp3.Callback
,所返回来的结果,也都是 okhttp3.Response
,到这里就可以大概知道了,retrofit
的网络请求其实还是由 OkHttp
来实现。
3,okhttp3.Response
这个响应不方便开发者直接使用,所以 retrofit
在收到结果后,又对响应结果进行新一轮的解析 response = parseResponse(rawResponse)
,以 Response
对象的形式返回给开发者。
另外还有最后一行: adapt(call, args)
; 它的内部其实是由一个适配器CallAdapted
来调用,如下:
protected ReturnT adapt(Call<ResponseT> call, Object[] args) {
return callAdapter.adapt(call);
}
adapt
是一个抽象方法,里面传入了上面刚创建的 OkHttpCall
作为参数。adapt 即 adapter,适配器的意思,那我们可以猜测一下,传入 OkHttpCall
,是不是就是为了对 OkHttpCall
再进行适配的工作?但,OkHttpCall
已经可以进行网络请求了,为什么还需要使用 CallAdapted
进行再次的适配呢? ⁉️这个问题还是先记下,抛到后面解答。
从上面分析知道,Retrofit
之所以调用简便,是因为内部对 @GET
,@POST
,@Path
等注解以及请求参数进行了解析,让开发者只需要关心自己新增的请求方法是不是符合规范。而上述动态代理最主要做的事情就是创建了 CallAdapted
,CallAdapted
其实只是一个适配器,主要需要了解的是它适配的是什么? 是否还记得我们在上面抛出了一个关于 CallAdapted
的问题:那就先来看看他是如何创建的?⁉️
abstract class HttpServiceMethod<ResponseT, ReturnT> extends ServiceMethod<ReturnT> {
static <ResponseT, ReturnT> HttpServiceMethod<ResponseT, ReturnT> parseAnnotations(
Retrofit retrofit, Method method, RequestFactory requestFactory) {
Type adapterType;
if (isKotlinSuspendFunction) {
Type[] parameterTypes = method.getGenericParameterTypes();
Type responseType =
Utils.getParameterLowerBound(
0, (ParameterizedType) parameterTypes[parameterTypes.length - 1]);
if (getRawType(responseType) == Response.class && responseType instanceof ParameterizedType) {
// Unwrap the actual body type from Response<T>.
responseType = Utils.getParameterUpperBound(0, (ParameterizedType) responseType);
continuationWantsResponse = true;
}
adapterType = new Utils.ParameterizedTypeImpl(null, Call.class, responseType);
annotations = SkipCallbackExecutorImpl.ensurePresent(annotations);
} else {
adapterType = method.getGenericReturnType();
}
CallAdapter<ResponseT, ReturnT> callAdapter =
createCallAdapter(retrofit, method, adapterType, annotations);
...
Converter<ResponseBody, ResponseT> responseConverter =
createResponseConverter(retrofit, method, responseType);
okhttp3.Call.Factory callFactory = retrofit.callFactory;
if (!isKotlinSuspendFunction) {
return new CallAdapted<>(requestFactory, callFactory, responseConverter, callAdapter);
}
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;
}
}
CallAdapter
是根据 returnType
和 annotations
的类型,从 callAdapterFactories
工厂中进行查找,从而返回所对应的网络请求适配器,这里 returnType 指的是网络请求接口里方法的返回值类型,如 Call
、Observable
等。annotations
则指代的是注解类型,如 @GET
、@POST
等。
这里也提到了一个适配器工厂 callAdapterFactories
,不同的 CallAdapter
就是从这个工厂中查询出来的,有查找那就必定有添加,那适配器 CallAdapter
是怎么添加到工厂类中的? ⁉️callAdapterFactories
这个变量是属于 Retrofit
类,跟踪发现是由 Retrofit
构造函数传入,也就是 Retrofit
初始化时进行了赋值。
Retrofit
的初始化是由一种建造者模式来创建,在 Retrofit
的 build()
方法中,找到了适配器工厂对其适配器的添加:
public Retrofit build() {
...
Executor callbackExecutor = this.callbackExecutor;
if (callbackExecutor == null) {
callbackExecutor = platform.defaultCallbackExecutor();
}
List<CallAdapter.Factory> callAdapterFactories = new ArrayList<>(this.callAdapterFactories);
//添加适配器callAdapter
callAdapterFactories.addAll(platform.defaultCallAdapterFactories(callbackExecutor));
...
return new Retrofit(
callFactory,
baseUrl,
unmodifiableList(converterFactories),
unmodifiableList(callAdapterFactories),
callbackExecutor,
validateEagerly);
}
platform.defaultCallAdapterFactories
指的是 Android 平台的一个默认的适配器工厂,当我们不使用自定义适配器工厂时,则添加的就是这默认的工厂。这里提到了自定义适配器工厂,其实我们在使用 Retrofit
的时候,有时候会和RxJava
结合,例如在创建 Retrofit
时,也会 addCallAdapterFactory
,将RxJava2CallAdapterFactory
添加到 callAdapterFactories
中。
mRetrofit = new Retrofit.Builder()
.client(okHttpClient)
.baseUrl(baseUrl)
.addCallAdapterFactory(RxJava2CallAdapterFactory.create())
.build();
添加调用适配器工厂的目的就是支持 Call
以外的服务方法返回类型,如支持Observable
,Single
返回类型等。 在 callAdapterFactories
集合器添加一个默认适配器工厂时,也附带传进去了一个参数 callbackExecutor
,callbackExecutor
是 Java8 或者 Android 平台的一个默认线程调度器,它的作用涉及到一个线程切换的问题,也就是后续需要分析的 Retrofit
是如何将子线程切换到主线程?⁉️
在上面源码创建 CallAdapter
时,有一个对象也同时被创建,那就是Converter
。
Converter<ResponseBody, ResponseT> responseConverter = createResponseConverter(retrofit, method, responseType);
Converter
数据装换器。它的添加和获取方式和 CallAdapter
类似。在Retrofit
初始化时添加到 List<Converter.Factory>
工厂集合,根据类型 Type
取出不同的 Converter
,对返回的网络响应,做出数据的转换,例如转换成实体类。基本逻辑与 CallAdapter
类似。
mRetrofit = new Retrofit.Builder()
.client(mOkHttpClient)
.baseUrl(baseUrl)
.addConverterFactory(GsonConverterFactory.create()
.build();
总结一下上面讲的:
1,一开始动态代理中调用 loadServiceMethod
方法,解析接口方法中的注解,参数,头部信息等;
2,依据接口方法的返回类型,从适配器工厂集合里进行查询,生成相应的适配器 CallAdapter
,区分是 RxJava
的 Observable
、Single
还是 Call
或者其他类型,(适配器工厂集合的数据是由构建 Retrofit
时 addCallAdapterFactory()
添加,如无自定义,则添加 Android 平台默认适配器)。以相同的方式取出数据转换器 Converter
;
3,利用上面生成的 CallAdapter
,调用 invoke
方法,创建 OkHttpCall
对象,即针对请求信息,利用 OkHttp
进行异步或者同步网络请求,并且对响应结果进行实体类转换;
4,创建好 OkHttpCall
后,又利用上面查询到的适配器 CallAdapter
调用adapt
,返回 RxJava
的 Observable
、Single
或者 Call
对象。
Retrofit
是如何将子线程切换到主线程?
在添加默认适配器工厂 defaultCallAdapterFactories
时,将 callbackExecutor
作为了一个参数,那么它的具体实现也就是在这个默认适配器工厂中。 我们来看下 callbackExecutor
在里面做了些啥。
static final class ExecutorCallbackCall<T> implements Call<T> {
final Executor callbackExecutor;
final Call<T> delegate;
...
@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(
() -> {
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(() -> callback.onFailure(ExecutorCallbackCall.this, t));
}
});
}
在上述代码里了解到,callbackExecutor
即 Executor
,一个线程调度器。在Call 的 enqueue
实现里执行了一个异步网络请求 delegate.enqueue
,在请求的响应 onResponse
、onFailure
中 Executor
也同样执行了一个线程,这里就有个疑问,为什么要在一个异步请求里又调用一个线程?我们知道callbackExecutor
是一个线程调度器,那他内部到底实现的是什么?
默认 callbackExecutor
的创建在 Retrofit
的初始化中,callbackExecutor = platform.defaultCallbackExecutor()
;
static final class Android extends Platform {
@Override
public Executor defaultCallbackExecutor() {
return new MainThreadExecutor();
}
static final class MainThreadExecutor implements Executor {
private final Handler handler = new Handler(Looper.getMainLooper());
@Override
public void execute(Runnable r) {
handler.post(r);
}
}
}
}
platform
是一个 Android 平台,defaultCallbackExecutor
内部其实调用的是 new MainThreadExecutor() ,很清楚的看到, handler.post(r) 内部使用Handler将响应抛到了主线程。这就是 Retrofit
将子线程切换到主线程的核心所在。
Retrofit 为什么要使用动态代理?
动态代理的优势是什么?不用暴露真实的委托者,根据不同的委托创建不同的代理,通过代理去做事情。
那 Retrofit 弥补了OkHttp 的哪些缺点呢?
OkHttp 在使用的时候,请求参数的配置是不是很繁琐,尤其当有一些表单提交时,又臭又长,而 Retrofit 就是弥补了这个缺点,利用 @GET、@POST、@Path、@Body 等注解以及一些参数很简便的就构造出了请求。
当 Retrofit 创建了不同的接口,动态代理就发挥出了作用。每当不同接口方法执行时,动态代理都会拦截该请求,对接口中的注解,参数进行解析,构建出不同的 Request,最后则交给 OkHttp 去真正执行。
Retrofit 结合动态代理,不用关心真正的接口方法,对符合规范的接口进行统一化的管理,以统一的方式解析注解和参数,拼接成 request。
总结:
Retrofit
其实是 OkHttp
的封装类,内部网络请求靠的还是 OkHttp
,那 Retrofit
封装后改变了什么?
-
接口请求更加简便,标注注解 @GET、@POST、@Path、@Body 等就形成一个网络请求;
-
默认帮助开发者解析 responseBody,另外还可以自定义解析策略;
-
Retrofit 帮助开发者进行线程切换;
-
Retrofit 带给开发者更多的权限,可自定义适配网络请求。