Android开源项目原理系列
[搞定开源] 第一篇 okhttp 3.10原理
[搞定开源] 第二篇 okio 1.14原理
demo
interface Api {
@GET("v2/movie/top250")
fun getTopList(): Call<HttpResponse>
}
api用的是豆瓣一个接口。
val retrofit = Retrofit.Builder()
.baseUrl("http://api.douban.com")
.addConverterFactory(GsonConverterFactory.create(Gson()))
.build()
val api = retrofit.create(Api::class.java)
val call = api.getTopList()
call.enqueue(object : Callback<HttpResponse> {
override fun onResponse(call: Call<HttpResponse>, response: Response<HttpResponse>) {
println("response:${response.body()}")
}
override fun onFailure(call: Call<HttpResponse>, t: Throwable) {
}
})
网络请求工作实际由OkHttp负责,Retrofit是对网络请求接口的封装,简单的几行就可以发送一次网络请求。
归纳
面向对象的网络请求流程可以归纳为:
- 封装网络请求参数为对象;
- 发送网络请求,同步或者异步,得到服务器响应结果;
- 解析结果为对象。
Retrofit的工作是第1、3点,OKHttp负责第2点。具体化Retrofit的工作流程为下面5点,接下来正文也是通过这5点详述:
- 创建Retrofit实例;
- 通过Java接口描述请求api,创建请求实例;
- 同步或者异步发送网络请求;
- 数据转换器解析数据;
- 切换线程
理解Retrofit源码主干并不困难,我也不打算深入到每个细节。Retrofit是设计模式的宝库,是我本文最想分析总结的。
- Retrofit外观模式;
- Retrofit、ServiceMethod使用构建者模式创建;
- 动态代理实例化请求实例;
- 工厂模式创建CallAdapter适配器;

1、创建Retrofit
创建Retrofit实例用的是构建者设计模式,主要关注有哪些参数,默认参数是什么,哪些参数是必须的。
- platform:当前平台,预设了Java8和Android,不同平台的初始参数有区别
- baseUrl:必填
- callFactory:默认是OkHttpClient
- converterFactories:将响应结果进行转换
- callAdapterFactories:对请求返回类型进行转换
- callbackExecutor:异步回调执行
整个库围绕的是核心类Retrofit,只需要了解这个入口类就可以玩起来,内部复杂的逻辑不需要考虑,这是外观模式的体现。
2、网络请求的描述
每个网络请求api在Retrofit中是定义在接口里,我们知道需要调用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();
@Override public Object invoke(Object proxy, Method method, @Nullable Object[] args)
throws Throwable {
//...
ServiceMethod<Object, Object> serviceMethod =
(ServiceMethod<Object, Object>) loadServiceMethod(method);
OkHttpCall<Object> okHttpCall = new OkHttpCall<>(serviceMethod, args);
return serviceMethod.adapt(okHttpCall);
}
});
}
答案是通过动态代理模式,运行时生成接口的代理类。当调用api里的方法时,自动调用动态代理的invoke。一般的动态代理会有具体的实现类,代理类在调用实现类前后加上诸如日志的操作,这里就不需要了。
invoke核心是最后三行代码:
- api里每个请求方法和网络参数,包装为ServiceMethod类。ServiceMethod不是直接创建,而是通过一个名叫serviceMethodCache的Map进行缓存,保持单例。真要创建时,ServiceMethod使用了Builder设计模式,它包含了网络请求的详细参数,具体创建逻辑不一一道来;
- ServiceMethod和请求参数包装为retrofit2.OkHttpCall;
- 返回是接口retrofit2.Call,具体的类型需要通过CallAdapter转换,比如返回RxJava的Observable。
CallAdapter
CallAdapter是Retrofit混杂着最多设计模式的一个接口,它的作用是将请求返回类型retrofit2.Call<R>转为合适的类型T。顾名思义,首先它是一个适配器。
回看上面demo定义的一个api,默认返回Call<HttpResponse>,使用RxJava就返回一个Observable。如果我们有需要,完全可以自行开发新的CallAdapter,返回MyCall<MyResponse>类型。
@GET("v2/movie/top250")
fun getTopList(): Call<HttpResponse>
看一眼源码里的retrofit-adapters文件夹,Retrofit支持转换的库有:
- guava
- java8
- rxjava
- rxjava2
- scala
CallAdapter的创建需要用到CallAdapter.Factory,工厂模式,默认工厂类是ExecutorCallAdapterFactory:
@Override
public CallAdapter<?, ?> get(Type returnType, Annotation[] annotations, Retrofit retrofit) {
if (getRawType(returnType) != Call.class) {
return null;
}
final Type responseType = Utils.getCallResponseType(returnType);
return new CallAdapter<Object, Call<?>>() {
@Override public Type responseType() {
return responseType;
}
@Override public Call<Object> adapt(Call<Object> call) {
return new ExecutorCallbackCall<>(callbackExecutor, call);
}
};
}
工厂get函数返回CallAdapter的匿名实现类,将Object转为ExecutorCallbackCall,这也是demo里call的类型。ExecutorCallbackCall的代码不太多,大部分函数都委托给delegate处理,唯独处理了异步请求的enqueue函数。
static final class ExecutorCallbackCall<T> implements Call<T> {
final Executor callbackExecutor;
final Call<T> delegate;
ExecutorCallbackCall(Executor callbackExecutor, Call<T> delegate) {
this.callbackExecutor = callbackExecutor;
this.delegate = delegate;
}
@Override public void enqueue(final Callback<T> callback) {
checkNotNull(callback, "callback == null");
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()) {
// 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(new Runnable() {
@Override public void run() {
callback.onFailure(ExecutorCallbackCall.this, t);
}
});
}
});
}
}
delegate实质就是OkHttpCall,这里应用了装饰器模式,OkHttpCall原来的enqueue函数回调是在子线程执行,ExecutorCallbackCall装饰了enqueue函数,将回调交由回调执行器处理,在Android平台也即是MainThreadExecutor,最终回调执行在主线程。(线程切换后文第5点)
3 发送请求
获取retrofit2.Call对象后,就可以发送网络请求了,分同步和异步两个方法。毕竟Retrofit的定位是对OkHttp的封装,收集的网络请求信息转换为okhttp3.Call交给OkHttp处理,最终得到OkHttp返回的Response,中间过程看OkHttp相关文章。
异步请求和同步请求类似,唯一不同的是执行网络请求后,回调需要在指定的线程执行。
还有最后一步数据转换调用了parseResponse函数,将OkHttp的Response转为Retrofit的Response,实质调用了我们定义的Converter。
4 数据转换Converter
数据转换的作用是将结果ResponseBody转为合适的类型,默认的转换器是BuiltInConverters,几乎什么都无干。我常用的是GsonConverter,具体的解析过程不看了,
5 线程切换
异步得到服务器响应后,需要将回调运行在合适的线程。根据平台的不同,提供不同的处理。
static class MainThreadExecutor implements Executor {
private final Handler handler = new Handler(Looper.getMainLooper());
@Override public void execute(Runnable r) {
handler.post(r);
}
}
Android平台默认的CallbackExecutor是MainThreadExecutor,通过Handler切换线程到UI主线程。
用RxJava2CallAdapterFactory就更加方便了,自行设置。
网友评论