简介
官网
Type-safe HTTP client for Android and Java by Square, Inc
Square公司为Android/Java开发的类型安全HTTP客户端。
- Square公司:开源了很多组件库,做Android开发肯定是绕不过的。比如说现在通用的okhttp。
- 类型安全:Java是类型安全的语言,但如果你的代码里面充斥着Object对象,那必定是不安全的。
- Android HTTP客户端:封装了网络请求,Android开发网络库。
在使用Retrofit时候,我们还需要引入okHttp库。为啥有了okHttp库,我们还要用Retrofit呢?
简单网络请求示例
下面是三组示例代码,实现网络请求。 完整代码在这里
OkHttp请求
val request = Request.Builder()
.url("https://api.github.com/users/$userName").get().build()
val okHttpClient = OkHttpClient()
var call = okHttpClient.newCall(request)
call.enqueue(object : okhttp3.Callback {
override fun onFailure(call: okhttp3.Call, e: IOException) {}
override fun onResponse(call: okhttp3.Call, response: okhttp3.Response) {
if (response.code == 200) {
var responseString = response.body?.string();
var user = Gson().fromJson(responseString, UserBean::class.java)
Handler(Looper.getMainLooper()).post { tv.text = "OkHttp: \n ${user.url}" }
}
}
})
Retrofit
//定义一个接口类
public interface IService {
//通过注解定义我们的网络请求方法。返回 retrofit2.call
@GET("users/{user}")
Call<UserBean> userInfo(@Path("user") String user);
//返回 RxJava Single
@GET("users/{user}")
Single<UserBean> userInfoRx(@Path("user") String user);
}
构建一个服务实例
//
private var service = Retrofit.Builder()
.baseUrl("https://api.github.com/")
.addConverterFactory(GsonConverterFactory.create())
.addCallAdapterFactory(RxJava2CallAdapterFactory.create())
.build().create(IService::class.java)
Retrofit
service.userInfo(userName).enqueue(object : Callback<UserBean> {
override fun onFailure(call: Call<UserBean>, t: Throwable) {}
override fun onResponse(call: Call<UserBean>, response: Response<UserBean>) {
tv.text = "retrofit: \n ${response.body()?.url}"
}
})
service.userInfoRx(userName)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(Consumer { tv.text = "retrofitAndRxJava: \n ${it.url }" })
使用流程
流程图
对象在流程中传递。箭头是执行的操作。
粗点的对标下。
| OkHttp | Retrofit | |
|---|---|---|
| 构建request | Request(params) | Method(params) |
| 构建call | okHttp3.call | retrofit2.Call |
| 执行call | oKhttp3.Response | retrofit2.response |
| 转换业务对象 | 手动 | 配置自动化 |
| 线程切换 | 手动 | 配置自动化 |
| 结束 | UI | UI |
Retrofit相对于OkHttp
request构建
OkHttp构建一个Request,需要手动配置Url/get/Post/RequestBody 等等。
Retrofit不需要我们构建Request,取而代之的是,定义一个method接口方法,利用method注解,提前定义好了部分参数,比如get/post/url等等。写程序,从某种意义上来说,我们一直在封装函数,调用函数。这个函数有输入和输出,而需要不用关心它的细节。
Retrofit正是通过这种方式,帮我们屏蔽了构建Request的细节,你不用关心这是Post还是get。只需要关心入参和出参。当然在我们实际应用OkHttp过程中,也会将OkHttp请求的细节封装在函数中。
Retrofit使用注解的高明之处在哪里?我们要去思考这个问题
- 业务对象转换
网络请求返回的是一个JSON字符串。
OkHttp有一个显式转换的过程,而Retrofit内部实现了自动转换。我们猜测与addConverterFactory有关。
- 线程切换
OkHttp的callback在工作线程执行,刷新UI需要切换到主线程,而Retrofit内部实现了自动切换线程。
- 配置与请求分离
接口即为配置,将OkHttp请求中一串配置参数剥离开来,同时可以配置数据解析等额外功能。
Retrofit做的封装我们都可以做,但是通用性上就大打折扣。试想我们基于业务封装了一套函数。能复制给别人,能复制给别的App么,不能!假设封装一套要1人日,那如果有1万套呢?Retrofit提高了多少生产力?突然觉得Retrofit,开源精神很伟大,不是么。
Retrofit内部原理
Retrofit帮我们实现了OkHttp的部分过程,内部也必然是按照OkHttp的流程在走的。
源码流程图
对应第二简单示例。
流程图注意点
retrofit2.OkHttpCall是分割点,整体来看分成了两部分:
- 通过Retrofit/HttpServiceMethod,解析Method注解,构建一个retrofit2.OkHttpCall
OkHttpCall包含了构建OkHttp3.Request和 ResponseConvert的必要参数。
- OkHttpCall并不直接生成OkHttp3.Call,而是在OkHttpCall.equeue时生成。
OkHttpCall内部完成了OkHttp3.Call的构建/equeue/convert。ExecutorCallback实现了线程切换。 红色标记:红色部分即为Okhttp的使用流程。
过程源码解读
上面的流程图,我们省略了Retrofit初始化,这里其实是做了一系列自定义配置(baseUrl,factory,callbackExecutor...)。
1.构建retrofit2.OkHttpCall
Retrofit构造IService接口实例
分两步,第一步:构造Retrofit实例,一个配置or构造工厂
private var retrofit:Retrofit = Retrofit.Builder()
.baseUrl("https://api.github.com/")
.addConverterFactory(GsonConverterFactory.create())
.addCallAdapterFactory(RxJava2CallAdapterFactory.create())
.build()
public final class Retrofit {
//loadServiceMethod会用到,简单的ServiceMethod对象缓存
private final Map<Method, ServiceMethod<?>> serviceMethodCache =
new ConcurrentHashMap<>();
//通常我们的子域名是固定的几个,
//这个baseUrl就很细节了,不用我们在定义IService时,写大量的重复域名前缀
final HttpUrl baseUrl;
//入参okHttp3.Request构建okHttp3.Call的工厂,OkHttpClient就是一个Call.Factory
final okhttp3.Call.Factory callFactory;
//converter,不仅response可以转,request也可以转。
//不仅有GsonConverterFactory,
//还有ProtoConverterFactory,SimpleXmlConverterFactory等等
final List<Converter.Factory> converterFactories;
//不仅有Platform中定义的DefaultCallAdapterFactory,还有RxJava2CallAdapterFactory等等
final List<CallAdapter.Factory> callAdapterFactories;
//Platform中定义为这个MainThreadExecutor。
//其实就是封装Handler(Looper.getMainLooper())
//在ExecutorCallbackCall用到了,切换主线程用的
final @Nullable Executor callbackExecutor;
//如果为true,则在创建IService动态代理时,把IService的所有Method转换成ServiceMethod
//否则每个Method只有在第一次使用是转换成ServiceMethod。
final boolean validateEagerly;
//常用的Builder设计模式,构建Retrofit
public static final class Builder {
public Builder() {this(Platform.get()); }
public Builder baseUrl(URL baseUrl)
public Builder callFactory(okhttp3.Call.Factory factory)
public Builder addConverterFactory(Converter.Factory factory)
public Builder addCallAdapterFactory(CallAdapter.Factory factory)
public Builder callbackExecutor(Executor executor)
public Builder validateEagerly(boolean validateEagerly)
public Retrofit build()
}
}
Retrofit的create方法,帮我们构建了一个ISerivce接口的动态代理实例。
调用接口方法实际都会走到loadServiceMethod(method).invoke(xxx)
private var service:IService = retrofit.create(IService::class.java)
//核心代码
class Retrofit{
public <T> T create(final Class<T> service) {
return (T) Proxy.newProxyInstance(
service.getClassLoader(), new Class<?>[] { service },
new InvocationHandler() {
//当我们调用IService Proxy对象方法时,都会走到这个invoke中
@Override public @Nullable Object invoke(Object proxy, Method method,
@Nullable Object[] args) throws Throwable {
return loadServiceMethod(method).invoke(args != null ? args : emptyArgs);
}
});
}
//省略了缓存的代码
ServiceMethod<?> loadServiceMethod(Method method) {
return ServiceMethod.parseAnnotations(this, method);
}
ServiceMethod<?> loadServiceMethod(Method method)
当我们调用IService.userInfo方法时,会进入InvocationHandler.invoke方法中,再进入ServiceMethod.parseAnnotations,得到一个CallAdapted
abstract class ServiceMethod<T> {
static <T> ServiceMethod<T> parseAnnotations(Retrofit retrofit, Method method) {
//还得去HttpServiceMethod里面,最后返回的是一个CallAdapted(继承了HttpServiceMethod)
return HttpServiceMethod.parseAnnotations(retrofit, method, requestFactory);
}
//InvocationHandler.invoke中用到了ServiceMethod.invoke
abstract @Nullable T invoke(Object[] args);
}
代码有点长,但是逻辑清楚的。
abstract class HttpServiceMethod<ResponseT, ReturnT>
extends ServiceMethod<ReturnT> {
/**
* 识别方法注解,构造可复用的服务方法(使用Http),因为使用了反射,最好一次构造,多次复用。
* 这里我们省略了Kotlin相关代码,省略了构造方法
*/
static <ResponseT, ReturnT> HttpServiceMethod<ResponseT, ReturnT> parseAnnotations(
Retrofit retrofit, Method method, RequestFactory requestFactory) {
Annotation[] annotations = method.getAnnotations();
//1.CallAdapter作用在于将原始的响应Call<T>转换成你想要的类型。
//例如当我们添加RxJava2CallAdapterFactory,同时定义了RxJava的Single<T> ,retrofit就会帮我们将Call<T>转换成Single<T>,
//默认返回DefaultCallAdapterFactory$CallAdapter内部实例
CallAdapter<ResponseT, ReturnT> callAdapter =
retrofit.callAdapter(method.getGenericReturnType(), annotations);
//2.从retrofit构建responseConverter,实际返回的GsonResponseBodyConverter。
Type responseType = callAdapter.responseType();
Converter<ResponseBody, ResponseT> responseConverter =
retrofit.responseBodyConverter(responseType, annotations);
//3.脑容量不够大没关系,记住这些都是跟Retrofit配置那些Factory有关就好。先走主流程。
//callFactory,默认就是我们的OKhttpClient
return new CallAdapted<>(
requestFactory, retrofit.callFactory, responseConverter, callAdapter);
}
CallAdapted.invoke
invoke 做了两件事情 new OkHttpCall和 adapt
- 1.new OkHttpCall很好理解了,你要构建okHttp3.call/解析数据就得需要这些啊,需要啥传啥。
- 2.adapt实际是在DefaultCallAdapterFactory.$CallAdapter中将OkHttpCall套了一层ExecutorCallbackCall,方便用Executor切换到主线程。
@Override final @Nullable ReturnT invoke(Object[] args) {
Call<ResponseT> call = new OkHttpCall<>(requestFactory, args, callFactory, responseConverter);
return adapt(call, args);
}
到上面的流程为止,我们构建了一个retrofit2.OkHttpCall。它具备了以下能力的必要配置
- 构建okhttp3.Call,
- 转换okhttp3.Response,
- 回调可以切换到主线程
2.OkHttpCall.equeue执行过程
//T是我们返回的业务数据类
//OkHttpCall的代码很简单,该配置的东西都HttpServiceMethod中弄好了。
final class OkHttpCall<T> implements Call<T> {
private @Nullable okhttp3.Call rawCall;
//HttpServiceMethod在构建OkHttpCall时传进来的。
OkHttpCall(RequestFactory requestFactory, Object[] args,
okhttp3.Call.Factory callFactory, Converter<ResponseBody, T> responseConverter);
@Override public void enqueue(final Callback<T> callback) {
//构建okhttp3.Call
okhttp3.Call call= rawCall = callFactory.newCall(requestFactory.create(args));
//加入请求队列
call.enqueue(new okhttp3.Callback() {
@Override public void onResponse(okhttp3.Call call, okhttp3.Response rawResponse) {
//原始response转换成泛型Response
Response<T> response = parseResponse(rawResponse);
callback.onResponse(OkHttpCall.this, response);
}
});
}
Response<T> parseResponse(okhttp3.Response rawResponse) throws IOException {
ResponseBody rawBody = rawResponse.body();
rawResponse = rawResponse.newBuilder()
.body(new NoContentResponseBody(rawBody.contentType(), rawBody.contentLength()))
.build();
ExceptionCatchingResponseBody catchingBody = new ExceptionCatchingResponseBody(rawBody);
//此时的responseConverter为GsonResponseBodyConverter
T body = responseConverter.convert(catchingBody);
return Response.success(body, rawResponse);
}
}
ExecutorCallbackCall
如果是DefaultCallAdapterFactory$CallAdapter adapt出来的是ExecutorCallbackCall内部的callbackExecutor做线程切换。如果是Android,参考MainThreadExecutor
ExecutorCallbackCall -> OkHttpCall -> okHttp3.Call
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) {
delegate.enqueue(new Callback<T>() {
@Override public void onResponse(Call<T> call, final Response<T> response) {
callbackExecutor.execute(new Runnable() {
@Override public void run() {
callback.onResponse(ExecutorCallbackCall.this, response);
}
});
}
});
}
}
RxJava2CallAdapter
还记得RxJava2CallAdapterFactory,此时OkHttpCall会被封装进 CallExecuteObservable。当发生订阅时,会调用call.execute().
final class RxJava2CallAdapter<R> implements CallAdapter<R, Object> {
@Override public Object adapt(Call<R> call) {
Observable<Response<R>> responseObservable = new CallExecuteObservable<>(call);
Observable<?> observable = new BodyObservable<>(responseObservable);
if (isSingle) {
return observable.singleOrError();
}
}
}
final class CallExecuteObservable<T> extends Observable<Response<T>> {
private final Call<T> originalCall;
CallExecuteObservable(Call<T> originalCall) {
this.originalCall = originalCall;
}
@Override protected void subscribeActual(Observer<? super Response<T>> observer) {
// Since Call is a one-shot type, clone it for each new observer.
Call<T> call = originalCall.clone();
Response<T> response = call.execute();
observer.onNext(response);
}
...
}
总结下来,我们发现主流程的核心类,就两个 HttpServiceMethod,OkHttpCall
- HttpServiceMethod:构建OkHttpCall,传入所有后续请求需要的所有参数
- OkHttpCall:构建和执行okHttp3.call,用您想要的方式将execute封装,完善其链路
例如:线程切换,reponse数据对象转换,RxJava响应式变换。
到这里主流程就分析完成了。
Factory视角
读完主流程之后,我有种万物皆可配的感觉。几乎每一步都可以做配置。
我们需要与RxJava结合,那就配一个RxJava2CallAdapterFactory
我们的返回数据可能是XML,那就配一个SimpleXmlConverterFactory
默认的OkHttpClient不满意了,可以新写一个OkHttpClient加各种Timeout限制/拦截器等等
request构建call,没有给我们配置的空间,但是也预留了代码,待开发
callbackExecutor,也提供了线程切换的能力。