Retrofit 是square公司开发的一款对OKHttp进行了进一步封装的网络框架,现在也是android网络请求中非常火的一个网络请求框架,最近在准备面试的时候也刚好复习到了这一块,然后花了几天时间简单地看了下Retrofit2.0源码,分享一下。若有不对的地方,欢迎指正。
Retrofit2.0原理
Retrofit2.0用了动态代理技术,通过解析注解生成Http请求,把请求交给OkHttp,然后通过我们设置的ConverterFactory进行serialization和deserialization,最后通过CallAdapter把结果进行进一步适配,实现了对Rxjava,Guava和java8的支持。
Retrofit2.0所使用的动态代理
在官方给的samples中,我们首先得创建一个retrofit对象,然后通过该对象创建一个我们要代理的实例,即:
public static final String API_URL = "https://api.github.com";
Retrofit retrofit = new Retrofit.Builder()
.baseUrl(API_URL)
.addConverterFactory(GsonConverterFactory.create())
.build();
// Create an instance of our GitHub API interface.
GitHub github = retrofit.create(GitHub.class);
这个Github就是我们网络请求的接口:
public interface GitHub {
@GET("/repos/{owner}/{repo}/contributors")
Call<List<Contributor>> contributors(
@Path("owner") String owner,
@Path("repo") String repo);
}
在这里,retrofit使用了builder模式,我们可以在此设置请求的URL,ConverterFactory(此处用了Gson解析,当然你也可以自定义数据解析器,只要你的解析器继承 Converter.Factory就行),或者设置回调时的适配器。在retrofit中提供了三种CallAdapterFactory
:GuavaCallAdapterFactory
,Java8CallAdapterFactory
和RxJavaCallAdapterFactory
。比如要把response封装成rxjava的Observeble,然后对其进行流式操作:
Retrofit.Builder.addCallAdapterFactory(newRxJavaCallAdapterFactory().create());
然后我们点进去retrofit.create(GitHub.class)会发现:
@SuppressWarnings("unchecked") // Single-interface proxy creation guarded by parameter safety.
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, Object... args)
throws Throwable {
if (method.getDeclaringClass() == Object.class) {
return method.invoke(this, args);
}
//对java8兼容
if (platform.isDefaultMethod(method)) {
return platform.invokeDefaultMethod(method, service, proxy, args);
}
//看缓存里面有没有这个method,要是有,就返回,要是没有,就生成一个,然后加入缓存
ServiceMethod serviceMethod = loadServiceMethod(method);
//生成一个OkHttpCall对象
OkHttpCall okHttpCall = new OkHttpCall<>(serviceMethod, args);
//调用OkHttp,然后根据okHttpCall返回rejava的Observe对象或者返回Call
return serviceMethod.callAdapter.adapt(okHttpCall);
}
});
}
这里的Platform
其实是检测retrofit所运行的平台,是java8还是android还是ios。这里主要是在builder的时候,如果没有设置适配器,那么retrofit就会通过运行时的不同平台,然后选择不同的CallAdapterFactory
。从上面的代码可以看出,create
方法返回了一个动态代理对象,通过Github
接口生成代理类,并将代理类的实现交给 InvocationHandler
作为具体的实现。这里使用动态代理的好处是简化复杂的网络请求和解析、封装ServiceMethod。
主要的网络请求类
OkHttpCall
:这个类主要实现了Call<T>
接口,主要的作用就是发送一个HTTP请求,retrofit默认的也是这个类,我们也可以根据不同的情况实现自己的Call类,这种设计很插件化。在OkHttpCall内部提供了异步和同步两种方法来发送请求,分别是同步的OkHttpCall.execute()
和异步的OkHttpCall.enqueue()
,在异步操作中:
@Override public void enqueue(final Callback<T> callback) {
if (callback == null) throw new NullPointerException("callback == null");
okhttp3.Call call;
Throwable failure;
synchronized (this) {
if (executed) throw new IllegalStateException("Already executed.");
executed = true;
call = rawCall;
failure = creationFailure;
if (call == null && failure == null) {
try {
call = rawCall = createRawCall();
} catch (Throwable t) {
failure = creationFailure = t;
}
}
}
if (failure != null) {
callback.onFailure(this, failure);
return;
}
if (canceled) {
call.cancel();
}
call.enqueue(new okhttp3.Callback() {
@Override public void onResponse(okhttp3.Call call, okhttp3.Response rawResponse)
throws IOException {
Response<T> response;
try {
response = parseResponse(rawResponse);
} catch (Throwable e) {
callFailure(e);
return;
}
callSuccess(response);
}
@Override public void onFailure(okhttp3.Call call, IOException e) {
try {
callback.onFailure(OkHttpCall.this, e);
} catch (Throwable t) {
t.printStackTrace();
}
}
private void callFailure(Throwable e) {
try {
callback.onFailure(OkHttpCall.this, e);
} catch (Throwable t) {
t.printStackTrace();
}
}
private void callSuccess(Response<T> response) {
try {
callback.onResponse(OkHttpCall.this, response);
} catch (Throwable t) {
t.printStackTrace();
}
}
});
}
这里的call就是okhttp3.Call
,在这里通过用call.enqueue(...)
把请求交给okhttp的队列中,然后再通过异步回调切换到主线程,这里线程切换主要是通过前面的retrofit在build()的时候,Platform
检测到运行环境在android的时候,就会跳到Android:
static class Android extends Platform {
@Override public Executor defaultCallbackExecutor() {
return new MainThreadExecutor();
}
@Override CallAdapter.Factory defaultCallAdapterFactory(Executor callbackExecutor) {
return new ExecutorCallAdapterFactory(callbackExecutor);
}
static class MainThreadExecutor implements Executor {
private final Handler handler = new Handler(Looper.getMainLooper());
@Override public void execute(Runnable r) {
handler.post(r);
}
}
}
看到上面的MainThreadExecutor
就能看出来,在execute()的时候,retrofit获取了主线程的handler,然后在UI线程执行网络请求回调后的数据显示等操作。
网络请求流程总结
- 首先,在
Retrofit
build的时候,我们假设设置了newRxJavaCallAdapterFactory().create()
,还加上了addConverterFactory(GsonConverterFactory.create())
。 - 在当我们调用
github.contributors("square", "retrofit");
的时候,其实是调用了动态代理的InvocationHandler
,在InvocationHandler
中,我们看到有一个ServiceMethod
,这里用了缓存,缓存了之前调用过的网络请求的方法,loadServiceMethod(method)
方法检测缓存里面有没有调用过这个method,要是有,就返回,要是没有,就生成一个(此时会解析contributors()
,解析我们在contributors()
上面加的Annotation等,以便后面封装成request),然后加入缓存。 - 然后生成一个
OkHttpCall
,serviceMethod.callAdapter.adapt(OkHttpCall)
,此时的callAdapter
就是RxJavaCallAdapterFactory
。 - 在
InvocationHandler
中的最后一行代码中,return serviceMethod.callAdapter.adapt(okHttpCall);
此时调用了RxJavaCallAdapterFactory.adapt()
方法,返回我们想要的对象,比如android里面默认的Call<>,或者我们这里设置了RxJavaCallAdapterFactory,所以返回的是Observable<>。 - 当我们调用了rxjava的subscribe方法的时候(或者调用Call的enqueue方法) ,做了下面几件事:
- 首先执行
okHttpCall
的execute()方法把网络请求交给okHttp,当完成请求后,会对网络请求的结果进行解析,在解析时,会调用serviceMethod.toResponse(catchingBody)
,这里会通过我们之前设置的GsonConverterFactory
来解析返回的数据(比如通过GSON解析什么的),然后再生成一个Response
类. - 再回调到
ExecutorCallAdapterFactory
(这个CallAdapterFactory
是之前在retrofit的build()方法中通过Platform
检测环境,然后设置的一个CallAdapterFactory
),这个CallAdapterFactory
的adapt()中生成了一个ExecutorCallbackCall
对象,在这个ExecutorCallbackCall
的enqueue(CallBack)
中,会调用MainThreadExecutor
的execute()
方法,此时就是之前说的利用handler切换到主线程。
- 最后因为我们使用了rxjava,所以在这里会对返回的call对象进行了进一步封装,生成了我们需要的
Observable<Response>
,最后我们就可以在主线程中进行开心的rx的流式操作了!
- 首先执行