First, the whole idea
From using the method, first and foremost how to use, followed by the function we use internally is how to achieve, to achieve what skills the program, what paradigm. The full text is basically a source of analysis and Guide Retrofit, Retrofit very suggest that you download the source code after, followed by paper, go over the source code.
The figure knowledge summary of PDF content GitHub subsequent updates, like the impact of gold and three silver four small partners can look and see, welcome Star
( pulled left GitHub link, you need access to relevant content such as interviews can find their own )
https://github.com/xiangjiana/Android-MS
(VX:mm14525201314)
Second, the basic example
2.1 Creating objects Retrofit
Retrofit retrofit = new Retrofit.Builder()
.baseUrl("https://api.github.com/")
.addConverterFactory(GsonConverterFactory.create())
.build();
2.2 API defined API and get examples
public interface GitHubService {
@GET("users/{user}/repos")
Call<List<Repo>> listRepos(@Path("user") String user);
}
GitHubService github = retrofit.create(GitHubService.class);
Look at the definition, very simple, and there is nothing special, except for two notes: @GET
and @Path
. Their usefulness again later analysis, we then look to create API instance: retrofit.create(GitHubService.class)
. This creates an instance of the API, you can call the API method sends an HTTP request to the network, too convenient. But the create
method is how to create API instance of it?
public <T> T create(final Class<T> service) {
// 省略非关键代码
return (T) Proxy.newProxyInstance(service.getClassLoader(),
new Class<?>[] { service },
new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object ... args)
throws Throwable {
// 先省略实现
}
});
}
API instance is created using a dynamic agent technology.
简而言之,就是动态生成接口的实现类(当然生成实现类有缓存机制),并创建其 实例(称之为代理),代理把对接口的调用转发给 InvocationHandler
实例, 而在 InvocationHandler
的实现中,除了执行真正的逻辑(例如再次转发给真 正的实现类对象),我们还可以进行一些有用的操作,例如统计执行时间、进行初 始化和清理、对接口调用进行检查等。 为什么要用动态代理?因为对接口的所有方法的调用都会集中转发到 InvocationHandler#invoke
函数中,我们可以集中进行处理,更方便了。你可 能会想,我也可以手写这样的代理类,把所有接口的调用都转发到 InvocationHandler#invoke
呀,当然可以,但是可靠地自动生成岂不更方便?
2.3 调用 API 方法
获取到 API 实例之后,调用方法和普通的代码没有任何区别:
Call<List<Repo>> call = github.listRepos("square");
List<Repo> repos = call.execute().body();
这两行代码就发出了 HTTP 请求,并把返回的数据转化为了 List<Repo>
,太方 便了!
现在我们来看看调用 listRepos
是怎么发出 HTTP 请求的。上面 Retrofit#create 方法返回时省略的代码如下:
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, Object.. . args)
throws Throwable {
// If the method is a method from Object then defer to n ormal invocation.
if (method.getDeclaringClass() == Object.class) {
return method.invoke(this, args);
}
if (platform.isDefaultMethod(method)) {
return platform.invokeDefaultMethod(method, service, p roxy, args);
}
ServiceMethod serviceMethod = loadServiceMethod(method);
OkHttpCall okHttpCall = new OkHttpCall<>(serviceMethod, args);
return serviceMethod.callAdapter.adapt(okHttpCall);
}
});
如果调用的是 Object
的方法,例如 equals
, toString
,那就直接调用。 如果是 default
方法(Java 8 引入),就调用 default
方法。这些我们都先不管,因 为我们在安卓平台调用 listRepos
,肯定不是这两种情况,那这次调用真正干活 的就是这三行代码了(好好记住这三行代码,因为接下来很长的篇幅都是在讲它们 :) ):
ServiceMethod serviceMethod = loadServiceMethod(method);
OkHttpCall okHttpCall = new OkHttpCall<>(serviceMethod, args);
return serviceMethod.callAdapter.adapt(okHttpCall);
在继续分析这三行代码之前,先看一个流程图
这三行代码基本就是对应于流程图中轴上部了, ServiceMethod
, build OkHttpCall
, CallAdapter adapt
。
2.4 ServiceMethod
ServiceMethod<T>
类的作用正如其 JavaDoc
所言:
Adapts an invocation of an interface method into an HTTP call. 把对接口方法 的调用转为一次 HTTP 调用。
一个 ServiceMethod
对象对应于一个 API interface
的一个方 法, loadServiceMethod(method)
方法负责加载 ServiceMethod
:
ServiceMethod loadServiceMethod(Method method) {
ServiceMethod result;
synchronized (serviceMethodCache) {
result = serviceMethodCache.get(method);
if (result == null) {
result = new ServiceMethod.Builder(this, method).build();
serviceMethodCache.put(method, result);
}
}
return result;
}
这里实现了缓存逻辑,同一个 API 的同一个方法,只会创建一次。这里由于我们每 次获取 API 实例都是传入的 class
对象,而 class 对象是进程内单例的,所 以获取到它的同一个方法 Method
实例也是单例的,所以这里的缓存是有效的。
我们再看看 ServiceMethod
的构造函数:
ServiceMethod(Builder<T> builder) {
this.callFactory = builder.retrofit.callFactory();
this.callAdapter = builder.callAdapter;
this.baseUrl = builder.retrofit.baseUrl();
this.responseConverter = builder.responseConverter;
this.httpMethod = builder.httpMethod;
this.relativeUrl = builder.relativeUrl;
this.headers = builder.headers;
this.contentType = builder.contentType;
this.hasBody = builder.hasBody;
this.isFormEncoded = builder.isFormEncoded;
this.isMultipart = builder.isMultipart;
this.parameterHandlers = builder.parameterHandlers;
}
成员很多,但这里我们重点关注四个成 员: callFactory
, callAdapter
, responseConverter
和 parameterHandlers
。
callFactory
负责创建 HTTP 请求,HTTP 请求被抽象为了okhttp3.Call
类,它表示一个已经准备好,可以随时执行的 HTTP 请求;callAdapter
把retrofit2.Call<T>
转为 T (注意和okhttp3.Call
区分开来,retrofit2.Call<T>
表示的是对一个 Retrofit 方法的调用),这个过程会发送一个 HTTP 请求,拿到服务器返回的数据(通 过okhttp3.Call
实现),并把数据转换为声明的 T 类型对象(通过Converter<F, T>
实现);responseConverter
是Converter<ResponseBody, T>
类型,负责把服 务器返回的数据(JSON、XML、二进制或者其他格式,由ResponseBody
封装)转化为 T 类型的对象;parameterHandlers
则负责解析 API 定义时每个方法的参数,并在构造 HTTP 请求时设置参数;
它们的使用稍后再分析,这里先看看它们的创建(代码比较分散,就不贴太多代码 了,大多是结论):
2.4.1 callFactory
this.callFactory = builder.retrofit.callFactory()
,所以 callFactory
实际上由 Retrofit 类提供,而我们在构造 Retrofit 对象 时,可以指定 callFactory
,如果不指定,将默认设置为一个 okhttp3.OkHttpClient
。
2.4.2 callAdapter
private CallAdapter<?> createCallAdapter() {
// 省略检查性代码
Annotation[] annotations = method.getAnnotations();
try {
return retrofit.callAdapter(returnType, annotations);
} catch (RuntimeException e) {
// Wide exception range because factories are user code.
throw methodError(e, "Unable to create call adapter for %s", returnType);
}
}
可以看到, callAdapter
还是由 Retrofit 类提供。在 Retrofit 类内部, 将遍历一个 CallAdapter.Factory
列表,让工厂们提供,如果最终没有工厂能 (根据 returnType
和 annotations
)提供需要的 CallAdapter
,那将抛出 异常。而这个工厂列表我们可以在构造 Retrofit
对象时进行添加。
2.4.3, responseConverter
private Converter<ResponseBody, T> createResponseConverter() {
Annotation[] annotations = method.getAnnotations();
try {
return retrofit.responseBodyConverter(responseType, annotati ons);
} catch (RuntimeException e) {
// Wide exception range because factories are user code.
throw methodError(e, "Unable to create converter for %s", re sponseType);
}
}
同样, responseConverter
还是由 Retrofit 类提供,而在其内部,逻辑和创 建 callAdapter
基本一致,通过遍历 Converter.Factory
列表,看看有没有 工厂能够提供需要的 responseBodyConverter
。工厂列表同样可以在构造 Retrofit 对象时进行添加。
2.4.4 parameterHandlers
每个参数都会有一个 ParameterHandler
,由 ServiceMethod#parseParameter
方法负责创建,其主要内容就是解析每个参数 使用的注解类型(诸如 Path , Query , Field
等),对每种类型进行单独的 处理。构造 HTTP 请求时,我们传递的参数都是字符串,那 Retrofit 是如何把我们 传递的各种参数都转化为 String 的呢?还是由 Retrofit 类提供 converter!
Converter.Factory
除了提供上一小节提到的 responseBodyConverter
,还提 供 requestBodyConverter
和 stringConverter
,API 方法中除了 @Body
和 @Part
类型的参数,都利用 stringConverter
进行转换,而 @Body
和 @Part
类型的参数则利用 requestBodyConverter
进行转换。
这三种 converter
都是通过“询问”工厂列表进行提供,而工厂列表我们可以在构造 Retrofit
对象时进行添加。
2.4.5 工厂让各个模块得以高度解耦
上面提到了三种工厂: okhttp3.Call.Factory
, CallAdapter.Factory
和 Converter.Factory
,分别负责提供不同的模块,至于怎么提供、提供何种模 块,统统交给工厂,Retrofit 完全不掺和,它只负责提供用于决策的信息,例如参 数/返回值类型、注解等。
这不正是我们苦苦追求的高内聚低耦合效果吗?解耦的第一步就是面向接口编程, 模块之间、类之间通过接口进行依赖,创建怎样的实例,则交给工厂负责,工厂同 样也是接口,添加(Retrofit doc
中使用 install 安装一词,非常贴切)怎样的工 厂,则在最初构造 Retrofit
对象时决定,各个模块之间完全解耦,每个模块只 专注于自己的职责,全都是套路,值得反复玩味、学习与模仿。
除了上面重点分析的这四个成员, ServiceMethod
中还包含了 API 方法的 url 解 析等逻辑,包含了众多关于泛型和反射相关的代码,有类似需求的时候,也非常值 得学习模仿
2.5 OkHttpCall
终于把 ServiceMethod
看了个大概,接下来我们看看 OkHttpCall
。 OkHttpCall
实现了 retrofit2.Call
,我们通常会使用它的 execute()
和 enqueue(Callback<T> callback)
接口。前者用于同步执行 HTTP 请求,后者 用于异步执行。
2.5.1,先看 execute()
@Override
public Response<T> execute() throws IOException {
okhttp3.Call call;
synchronized (this) {
// 省略部分检查代码
call = rawCall;
if (call == null) {
try {
call = rawCall = createRawCall();
} catch (IOException | RuntimeException e) {
creationFailure = e;
throw e;
}
}
}
return parseResponse(call.execute());
......
}
主要包括三步:
- 创建
okhttp3.Call
,包括构造参数;- 执行网络请求;
- 解析网络请求返回的数据;
createRawCall()
函数中,我们调用了 serviceMethod.toRequest(args)
来创建 okhttp3.Request
,而在后者中,我们之前准备好的 parameterHandlers
就派上了用场。
然后我们再调用 serviceMethod.callFactory.newCall(request)
来创建 okhttp3.Call
,这里之前准备好的 callFactory
同样也派上了用场,由于工 厂在构造 Retrofit 对象时可以指定,所以我们也可以指定其他的工厂(例如使 用过时的 HttpURLConnection
的工厂),来使用其它的底层 HttpClient
实现。
我们调用 okhttp3.Call#execute()
来执行网络请求,这个方法是阻塞的,执行 完毕之后将返回收到的响应数据。收到响应数据之后,我们进行了状态码的检查, 通过检查之后我们调用了 serviceMethod.toResponse(catchingBody)
来把响 应数据转化为了我们需要的数据类型对象。在 toResponse
函数中,我们之前准 备好的 responseConverter
也派上了用场。
好了,之前准备好的东西都派上了用场,还好没有白费 :)
2.5.2 再看 enqueue(Callback<T> callback)
这里的异步交给了 okhttp3.Call#enqueue(Callback responseCallback)
来 实现,并在它的 callback
中调用 parseResponse
解析响应数据,并转发给传入 的 callback
。
2.6 CallAdapter
终于到了最后一步了, CallAdapter<T>#adapt(Call<R> call)
函数负责把 retrofit2.Call<R>
转为 T 。这里 T 当然可以就是 retrofit2.Call<R>
,这时我们直接返回参数就可以了,实际上这正是 DefaultCallAdapterFactory
创建的 CallAdapter
的行为。至于其他类型的 工厂返回的 CallAdapter
的行为,这里暂且不表,后面再单独分析。
至此,一次对 API 方法的调用是如何构造并发起网络请求、以及解析返回数据,这 整个过程大致是分析完毕了。对整个流程的概览非常重要,结合 stay 画的流程图, 应该能够比较轻松地看清整个流程了。
虽然我们还没分析完,不过也相当于到了万里长征的遵义,终于可以舒一口气了 :)
三、retrofit-adapters 模块
retrofit 模块内置了 DefaultCallAdapterFactory
和 ExecutorCallAdapterFactory
,它们都适用于 API 方法得到的类型为 retrofit2.Call
的情形,前者生产的 adapter 啥也不做,直接把参数返回,后 者生产的 adapter 则会在异步调用时在指定的 Executor 上执行回调。
retrofit-adapters
的各个子模块则实现了更多的工 厂: GuavaCallAdapterFactory
, Java8CallAdapterFactory
和 RxJavaCallAdapterFactory
。这里我主要分析 RxJavaCallAdapterFactory
,下面的内容就需要一些 RxJava
的知识了,不过 我想使用 Retrofit 的你,肯定也在使用RxJava
:)
RxJavaCallAdapterFactory#get
方法中对返回值的类型进行了检查,只支持 rx.Single
, rx.Completable
和 rx.Observable
,这里我主要关注对 rx.Observable
的支持。
RxJavaCallAdapterFactory#getCallAdapter
方法中对返回值的泛型类型进行 了进一步检查,例如我们声明的返回值类型为 Observable<List<Repo>>
,泛型 类型就是 List<Repo>
,这里对 retrofit2.Response
和retrofit2.adapter.rxjava.Result
进行了特殊处理,有单独的 adapter 负责 进行转换,其他所有类型都由 SimpleCallAdapter
负责转换。
那我们就来看看 SimpleCallAdapter#adapt
:
@Override
public <R> Observable<R> adapt(Call<R> call) {
Observable<R> observable = Observable.create(new CallOnSubscri be<>(call))
.lift(OperatorMapResponseToBodyOrError.<R>instance()); if (scheduler != null) {
return observable.subscribeOn(scheduler);
}
return observable;
}
这里创建了一个 Observable
,它的逻辑由 CallOnSubscribe
类实现,同时使 用了一个 OperatorMapResponseToBodyOrError
操作符,用来把 retrofit2.Response
转为我们声明的类型,或者错误异常类型。
我们接着看 CallOnSubscribe#call
:
@Override
public void call(final Subscriber<? super Response<T>> subscribe r) {
// Since Call is a one-shot type, clone it for each new subscr iber.
Call<T> call = originalCall.clone();
// Wrap the call in a helper which handles both unsubscription and backpressure.
RequestArbiter<T> requestArbiter = new RequestArbiter<>(call, subscriber);
subscriber.add(requestArbiter);
subscriber.setProducer(requestArbiter);
}
代码很简短,只干了三件事:
- clone 了原来的 call,因为
okhttp3.Call
是只能用一次的,所以每次都是 新 clone 一个进行网络请求;- 创建了一个叫做
RequestArbiter
的producer
,别被它的名字吓懵了,它就 是个producer
;- 把这个
producer
设置给subscriber
;
简言之,大部分情况下 Subscriber
都是被动接受 Observable push
过来的数据, 但要是 Observable
发得太快,Subscriber
处理不过来,那就有问题了,所以就有 了一种 Subscriber
主动 pull
的机制,而这种机制就是通过 Producer
实现的。给 Subscriber
设置 Producer
之后(通过 Subscriber#setProducer
方法), Subscriber
就会通过 Producer 向上游根据自己的能力请求数据(通过 Producer#request
方法),而 Producer
收到请求之后(通常都是 Observable
管理 Producer
,所以“相当于”就是 Observable
收到了请求),再根据请求的量给 Subscriber 发数据。
那我们就看看 RequestArbiter#request
:
@Override
public void request(long n) {
if (n < 0) throw new IllegalArgumentException("n < 0: " + n);
if (n == 0) return; // Nothing to do when requesting 0.
if (!compareAndSet(false, true)) return; // Request was alread y triggered.
try {
Response<T> response = call.execute();
if (!subscriber.isUnsubscribed()) {
subscriber.onNext(response);
}
} catch (Throwable t) {
Exceptions.throwIfFatal(t);
if (!subscriber.isUnsubscribed()) {
subscriber.onError(t);
}
return;
}
if (!subscriber.isUnsubscribed()) {
subscriber.onCompleted();
}
}
producer
相关的逻辑非常简单,这里就不在赘述。实际干活的逻辑就是执行 call.execute()
,并把返回值发送给下游。
而 OperatorMapResponseToBodyOrError#call
也相当简短:
@Override
public Subscriber<? super Response<T>> call(final Subscriber<? s uper T> child) {
return new Subscriber<Response<T>>(child) {
@Override
public void onNext(Response<T> response) {
if (response.isSuccessful()) {
child.onNext(response.body());
} else {
child.onError(new HttpException(response));
}
}
@Override
public void onCompleted() {
child.onCompleted();
}
@Override
public void onError(Throwable e) {
child.onError(e);
}
};
}
关键就是调用了 response.body()
并发送给下游。这里, body()
返回的就是 我们声明的泛型类型了,至于 Retrofit
是怎么把服务器返回的数据转为我们声明的 类型的,这就是 responseConverter
的事了,还记得吗?
最后看一张返回 Observable
时的调用栈:
执行路径就是:
Observable.subscribe
,触发 API 调用的执行;CallOnSubscribe#call
,clone call
,创建并设置producer
;RequestArbiter#request
,subscriber
被设置了producer
之后最终调用 request,在 request 中发起请求,把结果发给下游;OperatorMapResponseToBodyOrError$1#onNext
, Toresponse
the body sent downstream;- We finally went to
subscribe
an incoming callback inside;
Four, retrofit-converters module
retrofit module built BuiltInConverters
, only processing ResponseBody
, RequestBody
and conversion of type String (switch is not actually required). Retrofit- converters in the sub-module is provided JSON
, XML
, ProtoBuf
and other types of data conversion, but there are many ways to choose conversion. My main concern here GsonConverterFactory
.
The code is very simple:
@Override
public Converter<ResponseBody, ?> responseBodyConverter(Type typ e, Annotation[] annotations, Retrofit retrofit) {
TypeAdapter<?> adapter = gson.getAdapter(TypeToken.get(type));
return new GsonResponseBodyConverter<>(gson, adapter);
}
final class GsonResponseBodyConverter<T> implements Converter<Re sponseBody, T> {
private final Gson gson;
private final TypeAdapter<T> adapter;
GsonResponseBodyConverter(Gson gson, TypeAdapter<T> adapter) {
this.gson = gson;
this.adapter = adapter;
}
@Override public T convert(ResponseBody value) throws IOExcept ion {
JsonReader jsonReader = gson.newJsonReader(value.charStream( ));
try {
return adapter.read(jsonReader); } finally {
value.close();
}
}
}
The target type, use Gson#getAdapter
to obtain the corresponding Adapter, conversion using Gson
the API can.
The figure knowledge summary of PDF content GitHub subsequent updates, like the impact of gold and three silver four small partners can look and see, welcome Star
( pulled left GitHub link, you need access to relevant content such as interviews can find their own )
https://github.com/xiangjiana/Android-MS
(VX:mm14525201314)