最近研究Retrofit2+RxJava实现网络请求及数据集处理,一部分的知识点就在Retrofit2,为了更好的理解代码,本人决定分析下Retrofit2源码,既然是分析源码就得带着目的去分析,那么说说本文要解决的问题吧,先看代码来说明
Retrofit retrofit = new Retrofit.Builder()
.client(new OkHttpClient())
.baseUrl(API_URL)
.addCallAdapterFactory(RxJavaCallAdapterFactory.createWithScheduler(Schedulers.io()))
.addConverterFactory(GsonConverterFactory.create())
.build();
ZhuanLanApi api = retrofit.create(ZhuanLanApi.class);
Call<ZhuanLanAuthor> call = api.getAuthor("qinchao");
call.enqueue(new Callback<ZhuanLanAuthor>() {
@Override
public void onResponse(Call<ZhuanLanAuthor> call, Response<ZhuanLanAuthor> response) {
System.out.println(response.body().getName());
}
@Override
public void onFailure(Call<ZhuanLanAuthor> call, Throwable t) {
}
});
以上代码就是单独使用Retrofit2的基本方式,在写该代码时,我有几个疑惑,先列举吧。
疑惑1: call.enqueue是怎么发起网络请求的,和Okhttp3发起的网络请求为什么这么相似
疑惑2: 创建retrofit对象时client函数传的OkHttpClient对象有何作用
疑惑3: addCallAdapterFactory函数传参有起到什么作用
疑惑4: addConverterFactory函数传参起到什么作用
下面就来解决这些疑惑
1、Retrofit2和OkHttp3的关系
实际上Retrofit2是用来解决Okhttp3用法复杂的问题的,先看看用OkHttp3实现一个异步网络请求的代码
String url = "https://www.baidu.com/";
OkHttpClient okHttpClient = new OkHttpClient();
MediaType JSON = MediaType.parse("application/json;charset=utf-8");
RequestBody body = RequestBody.create(JSON, "{'name':'yjing','age':'12'}");
Request request = new Request.Builder()
.post(body)
.url(url)
.build();
Call call = okHttpClient.newCall(request);
call.enqueue(new Callback() {
@Override
public void onFailure(Call call, IOException e) {
e.printStackTrace();
}
@Override
public void onResponse(Call call, Response response) throws IOException {
System.out.println("我是异步线程,线程Id为:" + Thread.currentThread().getId());
}
});
注意,这里只是举个例子,传的参数是没有意义的,从代码可以看出OkHttp3使用太复杂,每次调用都得配置手动配置参数,而Retrofit2就是对OkHttp3进行了封装,使得能够通过调用一个Java方法来发起网络请求,网络请求实际上还是由OkHttp3完成的,Retrofit2只是将Java方法反射成为对应的网络请求。
由OkHttp3的使用代码可知,使用OkHttp3发起网路请求使通过Call对象来完成的,而创建Call实例对象,需要OkHttpClient对象和Request对象(这里需要注意)。
2、Retrofit2框架结构说明
在对Retrofit2源码进行解析之前,先说说其框架结构
这里的接口和类并不多
Converter:
负责转换网络请求返回数据为对应格式数据的接口,其中GsonConverterFactory共厂类生产的GsonRequestBodyConverter类就是该接口的实现
CallAdapter:
负者决定retrofit.create()返回参数的接口,DefaultCallAdapterFactory以及RxJavaCallAdapterFactory连个工厂类生产的类都是该接口的实现
ServiceMethod:
这个类比较核心,几乎保存了一个API请求所有需要的数据,OkHttpCall需要从ServiceMethod中获得一个Request对象,然后得到Response后,还需要传入ServiceMethod用对应的Converter对象转将Response数据换成数据格式
Retrofit:
生成Retrofit对象,并初始化上面的几个接口实现类或ServiceMethod类对象
3、Retrofit类介绍
上面有代码说明Retrofit对象的初始化操作,这里为了方便说明再贴一次
Retrofit retrofit = new Retrofit.Builder()
.client(new OkHttpClient())
.baseUrl(API_URL)
.addCallAdapterFactory(RxJavaCallAdapterFactory.createWithScheduler(Schedulers.io()))
.addConverterFactory(GsonConverterFactory.create())
.build();
ZhuanLanApi api = retrofit.create(ZhuanLanApi.class);
通过build的一系列方法,完成了在Retrofit对象中实例化Convertor对应工厂类、CallAdapter对应工厂类以及OkHttpClient对象的初始化。
然后调用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, 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);
}
if (platform.isDefaultMethod(method)) {
return platform.invokeDefaultMethod(method, service, proxy, args);
}
ServiceMethod serviceMethod = loadServiceMethod(method);
OkHttpCall okHttpCall = new OkHttpCall<>(serviceMethod, args);
return serviceMethod.callAdapter.adapt(okHttpCall);
}
});
}
这里的create方法中使用了动态代理,作用是当调用对应service方法时,会执行到动态代理代码中来,并且传入service对应方法反射对象以及方法参数。而动态代理方法核心是最后三行代码,其中loadServiceMethod方法会实例化一个ServiceMethod对象,并实例化ServiceMethod对象中的一下几个关键变量
下面先在三小节说说ServiceMethod方法的关键点
3、ServiceMethod类的关键点
ServiceMethod中有callAdapter变量、responseConverter变量、toRequest方法、toResponse方法这几个关键点。
callAdapter变量
根据是否有调用addCallAdapterFactory方法来实例化,例如如果调用了addCallAdapterFactory(RxJavaCallAdapterFactory.createWithScheduler(Schedulers.io()))则根据该工厂类创建SimpleCallAdapter(这里只是列举了该工厂类能生产的其中一种)对象
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<R> observable = Observable.create(new CallOnSubscribe<>(call)) //
.lift(OperatorMapResponseToBodyOrError.<R>instance());
if (scheduler != null) {
return observable.subscribeOn(scheduler);
}
return observable;
}
}
如果没有调用addCallAdapterFactory方法则使用一下默认工厂类生成的CallAdapter对象。
final class DefaultCallAdapterFactory extends CallAdapter.Factory {
static final CallAdapter.Factory INSTANCE = new DefaultCallAdapterFactory();
@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<Call<?>>() {
@Override public Type responseType() {
return responseType;
}
@Override public <R> Call<R> adapt(Call<R> call) {
return call;
}
};
}
}
这里一定熬注意两种CallAdapter对象的adapt方法的差异,不同的CallAdapterFactory生产的CallAdapter类,其adapt方法的返回参数不一样,DefaultCallAdapterFactory 对应的返回参数是Call,而SimpleCallAdapter 对应的返回参数是Observable。
responseConverter变量
该变量负责根据对应规则对Response数据进行格式转化,在Retrofit类的nextResponseBodyConverter方法中可以看到,当有调用addConverterFactory(GsonConverterFactory.create())使用GsonConverterFactory或其它的工厂类生产的转换器,从代码中可以看出当添加多个工厂类时,使用第一个工厂类生产的转换器。
for (int i = start, count = converterFactories.size(); i < count; i++) {
Converter<ResponseBody, ?> converter =
converterFactories.get(i).responseBodyConverter(type, annotations, this);
if (converter != null) {
//noinspection unchecked
return (Converter<ResponseBody, T>) converter;
}
}
然后我们来看看GsonConverterFactory工厂类生产的对象
final class GsonResponseBodyConverter<T> implements Converter<ResponseBody, 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 IOException {
JsonReader jsonReader = gson.newJsonReader(value.charStream());
try {
return adapter.read(jsonReader);
} finally {
value.close();
}
}
}
GsonResponseBodyConverter的convert方法会将ResponseBody 值转化为对应Java方法的返回参数T类型,如下面的方法则转化为ZhuanLanAuthor类型(解决疑惑4)。
Call<ZhuanLanAuthor> getAuthor(@Path("user") String user);
toResponse
T toResponse(ResponseBody body) throws IOException {
return responseConverter.convert(body);
}
这个方法将body数据转化为对应参数数据。
toRequest
/** Builds an HTTP request from method arguments. */
Request toRequest(Object... args) throws IOException {
RequestBuilder requestBuilder = new RequestBuilder(httpMethod, baseUrl, relativeUrl, headers,
contentType, hasBody, isFormEncoded, isMultipart);
@SuppressWarnings("unchecked") // It is an error to invoke a method with the wrong arg types.
ParameterHandler<Object>[] handlers = (ParameterHandler<Object>[]) parameterHandlers;
int argumentCount = args != null ? args.length : 0;
if (argumentCount != handlers.length) {
throw new IllegalArgumentException("Argument count (" + argumentCount
+ ") doesn't match expected count (" + handlers.length + ")");
}
for (int p = 0; p < argumentCount; p++) {
handlers[p].apply(requestBuilder, args[p]);
}
return requestBuilder.build();
}
这个方法,提供OkHttp3发起的网络请求需要的Request对象。
4、再谈Retrofit类的create方法
大致介绍完Retrofit类和ServiceMethod后,接着看Retrofit的create方法,前面说了其最后三行是关键代码
ServiceMethod serviceMethod = loadServiceMethod(method);
OkHttpCall okHttpCall = new OkHttpCall<>(serviceMethod, args);
return serviceMethod.callAdapter.adapt(okHttpCall);
其中第一行代码讲了,是实例化ServiceMehod对象,第二行是实例化OkHttpCall对象,该OkHttpCall是Call接口的实现,最后调用了callAdapter.adapt方法。前面备注了要注意的就是这个adapt方法,不同的CallAdapter其对应的adapter方法不一样,如前面所说DefaultCallAdapterFactory 工厂类生产的CallAdapter adapt方法对应的返回参数是Call,而SimpleCallAdapter 对应adapt方法的返回参数是Observable,这就为之后RxJava结合Retrofit2使用留下了铺垫(解决疑惑3)。
我们看到当不添加.addCallAdapterFactory(RxJavaCallAdapterFactory.createWithScheduler(Schedulers.io()))方法时,默认的CallAdater adapt方法直接来什么参数返回什么,也就是返回OKHttpCall对象。
到这里应该知道在本文最开始在addCallAdapterFactory之后还使用了
Call<ZhuanLanAuthor> call = api.getAuthor("qinchao");
这种方式来返回Call在运行时,肯定是会报错的,应为实际上api.getAuthor方法返回的是Observable变量,这地方只是为了方便后面讲解,才那么写的。
5、call.enqueue发起网络请求
这里的call.enqueue放弃网络请求的时候,实际上是OKHttpCall对象发起的网络请求,看起enqueue的部分代码
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);
}
}
其中createRawCall中通过获取ServiceMethod对象toRequest方法生成call对象,儿toRequest方法实际上就是获取的我们传入的.client(new OkHttpClient()) OkHttpClient对象(解决疑惑1、疑惑2)。
private okhttp3.Call createRawCall() throws IOException {
Request request = serviceMethod.toRequest(args);
okhttp3.Call call = serviceMethod.callFactory.newCall(request);
if (call == null) {
throw new NullPointerException("Call.Factory returned null.");
}
return call;
}
然后通过call对象调用call.enqueue方法发起网络请求,并执行回调。
到这里对Retrofit2的源码分析就算完成了,在分析的过程中也算是解决了文章开始提出的四个疑问。
6、参考文献
1、Retrofit2 源码解析
http://www.jianshu.com/p/c1a3a881a144
2、OkHttp3的基本用法
http://www.jianshu.com/p/1873287eed87
7、Demo地址
Android Demo:https://github.com/Yoryky/AndroidDemo.git