携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第1天,点击查看活动详情
写惯了 Flutter ,Android 知识还得在熟悉熟悉,就从最简单的三方库开始复习吧,以问问题的方式来回忆 Android,闲话不多说,开始吧!
问题一:Retrofit 是如何组织 Http 协议的
首先,Http 协议是网络传输的协议,包含了请求和响应两部分,详细信息可以看这里 👉HTTP协议超级详解,在Retrofit 用于请求的信息是通过注解标记的,比如:
@GET("users/{user}/repos")
fun listRepos(
@Path("user") user: String?,
@HeaderMap headers: Map<String, String>
): Call<List<Repo>>
上面代码反应的信息如下:
Http
请求的方法是GET
,所以用不到body
请求体- 路径是
users/{user}/repos
,user
的参数是listRepos
方法@Path
注解的 user - 自定义的请求头,通过
@HeaderMap
注解的 headers 参数设置
这些是我们常用到的信息,那么注解是如何解析的呢?答案是 Method
对象。
我们写的用于请求网络的 Retrofit 接口,仅仅是一个普通的接口,并不具备方法调用的功能,真正的 listRepos
调用是通过动态代理实现的。
动态代理类的创建就是:
val service = retrofit.create(XXX::class.java)
create 方法如下:
实际返回的代理类是通过标准的动态代理模版创建的,也就是 Proxy.newProxyInstance
,这个方法的最后一个参数就是方法处理器,也就是匿名 InvocationHandler
。
我们调用 listRepos
时,就会调用到匿名类的 invoke 方法中,参数如下:
proxy
就是代理对象method
就是listRepos
方法args
就是listRepos
方法的参数,我们这里就是user
和headers
会先加载方法,然后调用方法。 从性能考虑,Retrofit 会将已经调用过的方法缓存下来,从而减少一些不必要的解析。加载就是看方法有没有解析过。
我们接着看上面的解析方法:
解析主要是两个箭头处的代码,以第一处代码为例:
对于请求方法来说,就是硬编码解析。箭头指向的参数的 annotation
,就是通过 method.getAnnotation
获得的。那么解析出来的值给谁了呢?那就是 okhttp3
的 Request
类,其他的注解解析相似。
总结起来就是:动态代理拿到方法的 Method
对象,通过 Method
获取所有的注解,然后将解析出来的值一次赋值给构造出来的 Request
类。
问题二:Retrofit 是如何实现子线程请求网络,主线程刷新 UI 的。
val call: Call<List<Repo>> = service.listRepos("octocat")
call.enqueue(object : Callback<List<Repo>> { // 子线程请求网络
override fun onResponse(call: Call<List<Repo>>, response: Response<List<Repo>>) {
// 主线程更新 UI
}
override fun onFailure(call: Call<List<Repo>>, t: Throwable) {
// 主线程更新 UI
}
})
call
是我们接口返回的,是 retrofit2
包中的一个接口类,所以需要找到实现。上面介绍了实现是动态代理类,最终会走到 HttpServiceMethod
的 invoke
方法中。
@Override final @Nullable ReturnT invoke(Object[] args) {
Call<ResponseT> call = new OkHttpCall<>(requestFactory, args, callFactory, responseConverter);
return adapt(call, args); 第一处
}
HttpServiceMethod
有三个子类,不同的子类,adapt
有不同的逻辑,一般来说,HttpServiceMethod
的子类是 CallAdapted
。从名字就知道这玩意是一个适配器模式,而且适配的对象就是参数的call。
CallAdapted
的 adapt 的关键就两个:callAdapter
是谁,call
是谁。call
好说,就是 OkHttpCall
, callAdapter
是谁呢? callAdapter
是在构造方法赋值的,所以就在初始化的地方,经过追踪 callAdapter
就是 HttpServiceMethod
的 createCallAdapter
返回值。
private static <ResponseT, ReturnT> CallAdapter<ResponseT, ReturnT> createCallAdapter(
Retrofit retrofit, Method method, Type returnType, Annotation[] annotations) {
try {
return (CallAdapter<ResponseT, ReturnT>) retrofit.callAdapter(returnType, annotations);
} catch (RuntimeException e) {
throw methodError(method, e, "Unable to create call adapter for %s", returnType);
}
}
我们看直接调用了 retrofit
的 callAdapter
,那就追到 retrofit
去看看。
public CallAdapter<?, ?> callAdapter(Type returnType, Annotation[] annotations) {
return nextCallAdapter(null, returnType, annotations);
}
public CallAdapter<?, ?> nextCallAdapter(@Nullable CallAdapter.Factory skipPast, Type returnType,
Annotation[] annotations) {
///...
for (int i = start, count = callAdapterFactories.size(); i < count; i++) {
CallAdapter<?, ?> adapter = callAdapterFactories.get(i).get(returnType, annotations, this);
if (adapter != null) {
return adapter;
}
}
///...
}
我们看是直接从 callAdapterFactories
集合中获取的,而 callAdapterFactories
集合我们并没有赋值,肯定就是我们构造 retrofit
的时候,框架帮我们赋的值。果然,在 Retrofit build()
中有这么一段代码:
public Retrofit build() {
List<CallAdapter.Factory> callAdapterFactories = new ArrayList<>(this.callAdapterFactories);
callAdapterFactories.addAll(platform.defaultCallAdapterFactories(callbackExecutor));
}
就是添加了平台默认的 CallAdapter
,即 DefaultCallAdapterFactory
get 出来的。
所以看到这里就找到了:
CallAdapted
的 adapt 的两个关键:callAdapter
是 DefaultCallAdapterFactory
get 出来的。call
是 OkHttpCall
。
所以我们继续回到我们的主流程, CallAdapted
的 adapt
方法。
static final class CallAdapted<ResponseT, ReturnT> extends HttpServiceMethod<ResponseT, ReturnT> {
@Override protected ReturnT adapt(Call<ResponseT> call, Object[] args) {
return callAdapter.adapt(call);
}
}
callAdapter 是 DefaultCallAdapterFactory
get 出来的东西。
@Override public @Nullable CallAdapter<?, ?> get(
Type returnType, Annotation[] annotations, Retrofit retrofit) {
/// 代码省略
final Executor executor = Utils.isAnnotationPresent(annotations, SkipCallbackExecutor.class)
? null
: callbackExecutor;
return new CallAdapter<Object, Call<?>>() {
@Override public Type responseType() {
return responseType;
}
@Override public Call<Object> adapt(Call<Object> call) {
return executor == null
? call
: new ExecutorCallbackCall<>(executor, call); //最终的返回值
}
};
}
我们写的:
val call: Call<List<Repo>> = service.listRepos("octocat")
最终的返回值就是 ExecutorCallbackCall<>(executor, call)
,call 依旧是 OkHttpCall
, executor 是谁呢?
DefaultCallAdapterFactory(@Nullable Executor callbackExecutor) {
this.callbackExecutor = callbackExecutor;
}
是 DefaultCallAdapterFactory 构造方法设置的,也就是 Android 平台的主线程执行器。
static final class Android extends Platform {
@Override public Executor defaultCallbackExecutor() {
return new MainThreadExecutor();
}
static class MainThreadExecutor implements Executor {
private final Handler handler = new Handler(Looper.getMainLooper());
@Override public void execute(Runnable r) {
handler.post(r);
}
}
}
主线的实现方式就是 Handler 和 主线程的 Looper,大家就熟悉了。 adapt 的返回值就是主线程, 既然知道了主线程,我们再去找子线程。
val call: Call<List<Repo>> = service.listRepos("octocat")
我们知道了 ExecutorCallbackCall<>(executor, call)
是 service 方法返回的对象类型,并且 executor 是主线程执行器,参数call 是 OkHttpCall。 所以我们看返回类型的 enqueue
逻辑。
public void enqueue(final Callback<T> callback) {
this.delegate.enqueue(new Callback<T>() {
public void onResponse(Call<T> call, Response<T> response) {
ExecutorCallbackCall.this.callbackExecutor.execute(() -> {
if (ExecutorCallbackCall.this.delegate.isCanceled()) {
callback.onFailure(ExecutorCallbackCall.this, new IOException("Canceled"));
} else {
callback.onResponse(ExecutorCallbackCall.this, response);
}
});
}
public void onFailure(Call<T> call, Throwable t) {
ExecutorCallbackCall.this.callbackExecutor.execute(() -> {
callback.onFailure(ExecutorCallbackCall.this, t);
});
}
});
}
这一下子就明白了吧! delegate 是 call,也就是 OkHttpCall, callbackExecutor 是主线程执行器。 OkHttpCall 的 enqueue 是通过线程池实现的子线程请求网络。 这就把两者关联起来了。
总结一下:**Retrofit 使用适配器模式,将 OkHttpCall 和 主线程执行器进行了一次适配,连接了任务的两端。真正发送网络的子线程是 Okhttp 的线程池创建的,回调主线程是通过 Handler + 主线程 Lopper 实现的。 **