Retrofit是一款开源网络框架,由Square公司推出的,目前最新版本是Retrofit2,源码地址 https://github.com/square/retrofit
Retrofit的推出,主要是为了对Square公司推出的另一款网络请求框架OKHttp做封装。(Retrofit并不直接发起网络请求,网络请求仍然是由OkHttp发起的。)
Retrofit2+OkHttp3的使用,已经成为Android最主流的网络请求框架,此外还可以再配合RxJava、Dagger2等,使用起来非常强大。
本文简要分析Retrofit2的源码,及记录自己的一些理解。
Retrofit2源码量很少,所以阅读起来还是稍容易的,只要对动态代理,Java注解等有一定基础。
看下它的源码类:
上面就是Retrofit2的所有源码类了,其中“http”包下所有的都是注解类,占了一大半,挑一两个看看即可。重点看下Retrofit处理网络请求流程相关的类。
还是先从如何使用说起:
如果不对Retrofit2做封装,以发起登录请求为例,
假设登录请求的地址为http://192.168.1.1/user/login,参数包括账号和密码,那么得这么使用:
1.首先创建网络请求接口
创建一个Interface,按Retrofit习惯,后缀为Service。这里就叫LoginService吧:
//LoginService.java
public interface LoginService {
//登录
@POST("user/login")
Call<UserBean> login(@Field("account") String account, @Field("pwd") String pwd);
//注册
@POST("user/register")
Call<UserBean> register(@Field("account") String account, @Field("pwd") String pwd);
}
这里@POST注解表示这是post请求,@Field注解表示这个参数将会放到请求body里。
UserBean是我们自定义的表示用户信息的Java bean类,包括用户名,ID等信息,这里就不贴代码了。登录成功后返回的数据将会解析为UserBean类型。
2.创建Retrofit对象
Retrofit retrofit = new Retrofit.Builder()
//设置请求通用地址,必选
.baseUrl("http://192.168.1.1/")
//添加解析器,可选。如果不添加,则结果只能解析为ResponseBody类型,需自己解析为UserBean。
.addConverterFactory(GsonConverterFactory.create())
//添加Retrofit对Rxjava的支持,可选
.addCallAdapterFactory(RxJavaCallAdapterFactory.create())
//配置处理请求的client,必选
.client(getOkHttpInstance())
.build();
3.得到Call对象(Call代表网络请求)
Call<UserBean> call = retrofit.create(LoginService.class).login("Jack", "123456");
4.发起网络请求,处理响应结果
call.enqueue(new Callback<UserBean>() {
@Override
public void onResponse(Call<UserBean> call, Response<UserBean> response) {
//响应成功
UserBean user = response.body();
}
@Override
public void onFailure(Call<UserBean> call, Throwable t) {
//响应失败
}
});
1.创建网络请求接口
Retrofit2使用流程的第1步不细讲了,是准备工作。
2.创建Retrofit对象
创建Retrofit对象,用到了建造者模式,baseUrl必须传,另外可以按需添加Json解析器,添加RxJava的适配器等。
public Retrofit build() {
if (baseUrl == null) {
throw new IllegalStateException("Base URL required.");
}
okhttp3.Call.Factory callFactory = this.callFactory;
if (callFactory == null) {
callFactory = new OkHttpClient();
}
Executor callbackExecutor = this.callbackExecutor;
if (callbackExecutor == null) {
callbackExecutor = platform.defaultCallbackExecutor();
}
// Make a defensive copy of the adapters and add the default Call adapter.
List<CallAdapter.Factory> adapterFactories = new ArrayList<>(this.adapterFactories);
adapterFactories.add(platform.defaultCallAdapterFactory(callbackExecutor));
// Make a defensive copy of the converters.
List<Converter.Factory> converterFactories = new ArrayList<>(this.converterFactories);
return new Retrofit(callFactory, baseUrl, converterFactories, adapterFactories,
callbackExecutor, validateEagerly);
}
3.得到Call对象(Call代表网络请求)
重点是这第三步。看了使用过程我们可能会有疑问,LoginService只是个Interface,一般都是要有这个Interface的实现类,才能创建对象的。但是使用Retrofit2,我们不用自己实现,它帮我们实现了LoginService对象。而且用这个LoginService对象调用了登录方法,得到了Call对象。
Retrofit2是怎么实例化LoginService的呢?
我们从Retrofit.java的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);
}
});
}
从上面的代码可看到,用到了动态代理来处理请求。
对于代理,可以理解为创建一个中间者去访问目标,这个中间者就称为代理。这样做的好处就是,我们可以在代理访问目标前后,插入我们自定义的处理,比如用来插入日志打印,判断拦截等等。
如果是静态代理的方式,需要我们为每一个目标对象创建一个代理对象。
而动态代理的方式,则可以用同一个代理去处理不同的目标对象。也就是要代理的目标对象是动态确定的。
对于动态代理,这里就不过多分析了。要知道的是,Proxy.newProxyInstance()方法,传入一个Interface,返回的就是该Interface的实例对象。这就是LoginService对象的创建由来。
而Proxy.newProxyInstance()方法还传入了一个new InvocationHandler()对象,这个对象是用来干嘛的呢? 它的作用就是,当我们调用目标对象的方法的时候,都会通过这个 InvocationHandler()的invoke()方法来调用。
因此当我们调用LoginService的login()方法的时候,实际上是通过这个InvocationHandler的invoke()方法来处理的。
这个invoke()方法,一般都会走到最后几句:
ServiceMethod serviceMethod = loadServiceMethod(method);
OkHttpCall okHttpCall = new OkHttpCall<>(serviceMethod, args);
return serviceMethod.callAdapter.adapt(okHttpCall);
分为三行,第一行:
Retrofit 的 loadServiceMethod()方法:
ServiceMethod serviceMethod = loadServiceMethod(method);
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;
}
这里传入的method,在本例中就是login()这个Method。
可见ServiceMethod对象,同样是使用建造者模式创建的。ServiceMethod类用于描述我们创建的Interface里的方法的各个属性,注解属性,json转换器等。
看下这个简化后的 ServiceMethod.Builder#build()方法:
public ServiceMethod build() {
callAdapter = createCallAdapter();
responseType = callAdapter.responseType();
XXX省略中间一些处理
responseConverter = createResponseConverter();
for (Annotation annotation : methodAnnotations) {
parseMethodAnnotation(annotation);
}
XXX省略中间一些处理
return new ServiceMethod<>(this);
}
这里的parseMethodAnnotation(annotation)就是解析方法用到的各个注解的过程,这里就不看了。
而createCallAdapter()方法会遍历adapterFactories列表,通过逐个判断其方法的返回值,注解等,找个符合目标的那个adapter,这里也不细看了。
adapterFactories都会包含哪些adapterFactory呢?
可以点这里回去看看Retrofit的构建方法中,adapterFactories默认会添加defaultCallAdapterFactory对象。另外如果我们创建Retrofit对象的时候,如果传入了别的adapterFactory比如RxJavaCallAdapterFactory以支持Rxjava,那么也会添加到adapterFactories里。
另外,ServiceMethod的build()方法里还会确定一个responseConverter,和得到adapter的过程类似,会遍历converterFactories,找到合适的Converter。因为我们创建Retrofit对象的时候,添加了GsonConverterFactory,所以这里最终确定的responseConverter肯定是GsonResponseBodyConverter。这里就不细看了。
第二行,得到OkHttpCall对象。
OkHttpCall okHttpCall = new OkHttpCall<>(serviceMethod, args);
注意这里的OkHttpCall类是Retrofit的类,不是OkHttp的。
第三行,对网络请求okHttpCall 进行处理。
return serviceMethod.callAdapter.adapt(okHttpCall);
这里serviceMethod 的 callAdapter,上面已经说过,是ServiceMethod建造时,build()方法里调用createCallAdapter()生成的。
看到这,可以点这里返回到动态代理相关的invoke()方法理一下思路。
接着看,得到adapter之后,会调用adapter.adapt(okHttpCall)。
这里假设我们用的是默认的adapter,没有使用RxJava,则看下DefaultCallAdapterFactory 类:
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;
}
};
}
}
到这里,再理一下那三行代码的作用:
就是这三行:
ServiceMethod serviceMethod = loadServiceMethod(method);
OkHttpCall okHttpCall = new OkHttpCall<>(serviceMethod, args);
return serviceMethod.callAdapter.adapt(okHttpCall);
简而言之,其作用就是
解析所调用的请求方法(本例中是login方法)的注解等属性,找到能处理这个请求的adapter,json解析器等,并且得到OkHttpCall 对象。
4.发起网络请求,处理响应结果
这一步是通过Call.enqueue()方法把call加入请求队列的,看下enqueue()方法:
@Override public void enqueue(final Callback<T> callback) {
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 {
//创建OkHttp的call对象
call = rawCall = createRawCall();
} catch (Throwable t) {
failure = creationFailure = t;
}
}
}
xxx省略一些检查
call.enqueue(new okhttp3.Callback() {
@Override public void onResponse(okhttp3.Call call, okhttp3.Response rawResponse)
throws IOException {
Response<T> response;
try {
//解析成目标Bean类
response = parseResponse(rawResponse);
} catch (Throwable e) {
callFailure(e);
return;
}
callSuccess(response);
}
xxx省略callSuccess()等方法的定义
});
}
从OkHttpCall的enqueue(final Callback callback)方法可见,其内部是调用了OkHttp的Call类的enqueue()来处理请求的。这也就是本文开头说的Retrofit不会自己处理网络请求,而是交给OkHttp来处理。而OkHttp返回请求结果后,会由Retrofit的json解析器解析成目标Bean类,然后传入Retrofit的callback里,交给用户去处理。
看下Retrofit是如何解析响应数据为Bean的:
Response<T> parseResponse(okhttp3.Response rawResponse) throws IOException {
ResponseBody rawBody = rawResponse.body();
// Remove the body's source (the only stateful object) so we can pass the response along.
rawResponse = rawResponse.newBuilder()
.body(new NoContentResponseBody(rawBody.contentType(), rawBody.contentLength()))
.build();
xxx省略对rawResponse.code()的检查
ExceptionCatchingRequestBody catchingBody = new ExceptionCatchingRequestBody(rawBody);
try {
//关键解析在这里
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;
}
}
ServiceMethod的toResponse()方法:
T toResponse(ResponseBody body) throws IOException {
return responseConverter.convert(body);
}
之前说到,responseConverter是在Retrofit的loadServiceMethod(Method method)里,建造者模式生成ServiceMethod对象的时候,遍历converterFactories得到的。可以点这里回顾一下。
本例中这里得到的responseConverter肯定是GsonResponseBodyConverter。
下面是GsonResponseBodyConverter的convert()方法:
@Override public T convert(ResponseBody value) throws IOException {
JsonReader jsonReader = gson.newJsonReader(value.charStream());
try {
return adapter.read(jsonReader);
} finally {
value.close();
}
}
通过convert()方法,就把ResponseBody转换成目标Bean类型了。
这也是Retrofit的一个方便之处,一般我们处理网络请求数据的时候,解析得到的json字符串为为Java Bean这一步,通常由我们手动完成。
而使用Retrofit的话,在定义请求接口的时候需指定Java Bean的类型。Retrofit会将这步结果解析的步骤自动完成,最终回调里传入的是解析好的Java Bean了。