First POST build by Jekyll.github:https://github.com/nishibaiyang
Retrofit解析
Retrofit 概览
Retrofit 是一个 RESTful 的 HTTP 网络请求框架的封装。注意这里并没有说它是网络请求框架,主要原因在于网络请求的工作并不是 Retrofit 来完成的。Retrofit 2.0 开始内置 OkHttp,前者专注于接口的封装,后者专注于网络请求的高效,我们的应用程序通过 Retrofit 请求网络,实际上是使用 Retrofit 接口层封装请求参数、Header、Url 等信息,之后由 OkHttp 完成后续的请求操作,在服务端返回数据之后,OkHttp 将原始的结果交给 Retrofit,后者根据用户的需求对结果进行解析的过程。
Retrofit 使用
省略
Retrofit 原理
Retrofit使用接口+注解的形式来定义一个网络请求,在通过OkHttp来执行网络请求。现在有很多开源使用了注解Annotation,如Dagger2、ButterKnife等,以及Retrofit。所不同的是他们利用注解干的事却不一样。Dagger2、ButterKnife他们在编译期间就处理注解生成代码,提供依赖注入。Retrofit则是则运行期间处理注解,通过动态代理的方式来提供AOP能力。
是谁实际上完成了接口请求的处理?
前面讲了这么久,我们始终只看到了我们自己定义的接口,比如:
public interface GitHubService {
@GET("users/{user}/repos")
Call<List<Repo>> listRepos(@Path("user") String user);
}
其实它是 Retrofit 创建的一个代理对象了,这里涉及点儿 Java 的动态代理的知识,直接来看代码:
Github github = retrofit.create(Github.class);
Call<List<Contributor>> call = github.getContributors("square", "retrofit");
它使用了动态代理的方式来创建一个网络请求任务,create方法返回了一个动态代理对象github。
Java动态代理就是Java开发给了开发人员一种可能:当你要调用某个类的方法前,插入你想要执行的代码比如你要执行某个操作前,你必须要判断这个用户是否登录,或者你在付款前,你需要判断这个人的账户中存在这么多钱。这么简单的一句话,我相信可以把一个不懂技术的人也讲明白Java动态代理是什么东西了。那么问题来了,我们的接口是Github,但是并未提供Github的实现类,动态代理对象到底代理的是哪个具体类的方法啊?
public <T> T create(final Class<T> service) {
Utils.validateServiceInterface(service);
if (validateEagerly) {
eagerlyValidateMethods(service);
}
//这里返回一个 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, Object... args)
throws Throwable {
// If the method is a method from Object then defer to normal invocation.
if (method.getDeclaringClass() == Object.class) {
return method.invoke(this, args);
}
//DefaultMethod 是 Java 8 的概念,是定义在 interface 当中的有实现的方法
if (platform.isDefaultMethod(method)) {
return platform.invokeDefaultMethod(method, service, proxy, args);
}
//每一个接口最终实例化成一个 ServiceMethod,并且会缓存
ServiceMethod serviceMethod = loadServiceMethod(method);
//由此可见 Retrofit 与 OkHttp 完全耦合,不可分割
OkHttpCall okHttpCall = new OkHttpCall<>(serviceMethod, args);
//下面这一句当中会发起请求,并解析服务端返回的结果
return serviceMethod.callAdapter.adapt(okHttpCall);
}
});
}
简单的说,在我们调用 GitHubService.listRepos 时,实际上调用的是这里的 InvocationHandler.invoke 方法~~
可以看到实际上是构造了一个okhttpcall对象,这么可以猜测网络请求调用的okhttpcall. execute.
完整的一次请求
前面我们已经看到 Retrofit 为我们构造了一个 OkHttpCall ,实际上每一个 OkHttpCall 都对应于一个请求,它主要完成最基础的网络请求,而我们在接口的返回中看到的 Call 默认情况下就是 OkHttpCall 了,如果我们添加了自定义的 callAdapter,那么它就会将 OkHttp 适配成我们需要的返回值,并返回给我们。
我们来看下OkHttpCall.execute
@Override public Response<T> execute() throws IOException {
//这个 call 是真正的 OkHttp 的 call,本质上 OkHttpCall 只是对它做了一层封装
okhttp3.Call call;
synchronized (this) {
//处理重复执行的逻辑
if (executed) throw new IllegalStateException("Already executed.");
executed = true;
if (creationFailure != null) {
if (creationFailure instanceof IOException) {
throw (IOException) creationFailure;
} else {
throw (RuntimeException) creationFailure;
}
}
call = rawCall;
if (call == null) {
try {
call = rawCall = createRawCall();
} catch (IOException | RuntimeException e) {
creationFailure = e;
throw e;
}
}
}
if (canceled) {
call.cancel();
}
//发起请求,并解析结果
return parseResponse(call.execute());
}
parseResponse 主要完成了由 okhttp3.Response 向 retrofit.Response 的转换,同时也处理了对原始返回的解析:
Response<T> parseResponse(okhttp3.Response rawResponse) throws IOException {
ResponseBody rawBody = rawResponse.body();
//略掉一些代码
try {
//在这里完成了原始 Response 的解析,T 就是我们想要的结果,比如 GitHubService.listRepos 的 List<Repo>
T body = serviceMethod.toResponse(catchingBody);
return Response.success(body, rawResponse);
} catch (RuntimeException e) {
// If the underlying source threw an exception, propagate that rather than indicating it was
// a runtime exception.
catchingBody.throwIfCaught();
throw e;
}
}
在Retrofit的默认实现中,CallBack回调运行在主线程上,而不用管retrofit是否是在主线程中调用。
1、 因为Retrofit底层调用的是OkHttp,而OkHttp的回调并非运行在主线程,所以Retrofit对它进行了封装,使用主线程Handler进行分发,使得CallBack回调运行在主线程上
2、 也就说不管在哪个线程中调用retrofit,我们都可以在Retrofit的回调中直接操作UI,这个特性是不是很屌!
结果适配
前面提到serviceMethod.callAdapter.adapt(okHttpCall);实际上这个okhttpcall传进去没有进行任何处理。
final class DefaultCallAdapterFactory extends CallAdapter.Factory {
static final CallAdapter.Factory INSTANCE = new DefaultCallAdapterFactory();
@Override
public CallAdapter<?> get(Type returnType, Annotation[] annotations, Retrofit retrofit) {
... 毫不留情的省略一些代码 ...
return new CallAdapter<Call<?>>() {
... 省略一些代码 ...
@Override public <R> Call<R> adapt(Call<R> call) {
//看这里,直接把传入的 call 返回了
return call;
}
};
}
}
如果我想要接入 RxJava,让接口的返回结果改为 Observable,那就需要实现一个adapter队这个call进行处理了。Retrofit 的开发者们早就想到了这个问题,并且为我们提供了相应的 Adapter:RxJavaCallAdapterFactory
public final class RxJavaCallAdapterFactory extends CallAdapter.Factory {
... 请叫我省略君,为了省地方,一个都不放过! ...
@Override
public CallAdapter<?> get(Type returnType, Annotation[] annotations, Retrofit retrofit) {
//注意下面的代码主要是判断 returnType 是否为 RxJava 支持的类型
Class<?> rawType = getRawType(returnType);
String canonicalName = rawType.getCanonicalName();
boolean isSingle = "rx.Single".equals(canonicalName);
boolean isCompletable = "rx.Completable".equals(canonicalName);
if (rawType != Observable.class && !isSingle && !isCompletable) {
return null;
}
... 这里省略掉的代码主要是根据返回类型获取合适的 Adapter ...
return callAdapter;
}
... 我又来了,继续略去一些代码 ...
static final class SimpleCallAdapter implements CallAdapter<Observable<?>> {
private final Type responseType;
private final Scheduler scheduler;
SimpleCallAdapter(Type responseType, Scheduler scheduler) {
this.responseType = responseType;
this.scheduler = scheduler;
}
@Override public Type responseType() {
return responseType;
}
@Override public <R> Observable<R> adapt(Call<R> call) {
//在这里创建需作为返回值的 Observable 实例,并持有 call 实例
//可以想象得到,在 Observable.subscribe 触发时, call.execute 将会被调用
Observable<R> observable = Observable.create(new CallOnSubscribe<>(call))
.lift(OperatorMapResponseToBodyOrError.<R>instance());
if (scheduler != null) {
return observable.subscribeOn(scheduler);
}
return observable;
}
}
... 略去一些代码 ...
}
RxJavaCallAdapterFactory 提供了不止一种 Adapter,但原理大同小异,有兴趣的读者可以自行参阅其源码。
最后还是回到最还是的作为总结:Retrofit 接口层封装请求参数、Header、Url 等信息,之后由 OkHttp 完成后续的请求操作,在服务端返回数据之后,OkHttp 将原始的结果交给 Retrofit,后者根据用户的需求对结果进行解析的过程。
参考:https://segmentfault.com/a/1190000005638577 https://blog.csdn.net/lyric_315/article/details/52710464