一.概述
我想作为一个Android开发如果没有听过Retrofit网络请求框架,那么他是与时代脱节的,你随便在gayhub
上找一个开源的Android项目都能发现Retrofit的身影。在介绍Retrofit之前,大家还是需要知道okhttp的基本使用方式的。
okhttp使用很简单,主要分为同步请求和异步请求:
OkHttpClient client = new OkHttpClient();
Request request = new Request.Builder().url(url).build();
- 同步请求,即等待请求结果返回,然后对结果进行解析,主要流程:
Response response = client.newCall(request).execute();
然后对返回的结果Request进行解析。
- 异步请求,即回调式请求,结果返回后进行回调,不会阻塞当前线程的执行。主要流程:
Call call = client.newCall(request);
call.enqueue(new Callback() {
@Override
public void onResponse(Response response) throws IOException {
//get result from response.
}
@Override
public void onFailure(Request arg0, IOException arg1) {
}
});
可以看到,两种操作方式,都是对返回的Response进行包括io操作、结果转换等解析操作。你可以把okhttp就当成类似HttpURLConnection
一样的Api,他们的功能是一样的都是负责网络请求。如果想对Okhttp进一步了解的可以参考:Android OkHttp完全解析 是时候来了解OkHttp了。
二.Retrofit的使用
既然都会使用okhttp了,那么使用Retrofit就很容易了。网上教程一大堆,这边主要罗列基本的3个操作:
- 1.创建接口类,其中每个方法都是一个对应的网络请求。
public interface API {
@POST("/load")
Call<Rep> load();
@Post("/test")
Call<Rep> down();
}
- 2.创建Retrofit对象,方式和Okhttp一样是通过Builder模式进行配置生成对象,其中baseUrl表示表示完整的url前半段,和API中的注解合并成最终的url。
Retrofit retrofit = new Retrofit.Builder()
.baseUrl("http://com.seu.test/")
.build();
- 3.创建异步请求
Call<Rep> call = retrofit.create(API.class).load();
call.enquque(new Callback<Rep> {
@Override
public void onResponse(Call<Rep> call, Response<Rep> response) {
//get result from response
}
@Override
public void onFailure(Call<Rep> call, Throwable t) {
}
});
是不是看去和Okhttp的异步请求一模一样。你猜对了,Retrofit就是对Okhttp的网络请求的封装。如果你现在还不了解Retrofit的用途,那我还能怎么办呢。Retrofit2.0使用详解,我也只能帮到这了。
三.Retrofit源码分析
注解Annotation
通过查看源码可以知道在retrofit2.http目录下面都是Java注解类,其中包括熟悉的GET
、POST
等。本文结合GET
进行入门式介绍。
Java中包括4种元注解(用于自定义注解的注解):
- Documented,如果定义了该注解,则通过javadoc后,该注解的信息可以在docs中显示(对于开发来说,没啥用,不行就在自定义注解中直接加上就行)。
- Inherited 定义该注解是否可以在子类中被继承。一般该注解的使用也不会很多,至今没用过继承的注解。
- Retention 定义了该注解的存活周期。这个是我见过用处比较大的注解了,但是在我解释了他的用处以后,你也会觉得
so easy
。Retention包括三部分:
Rentention 的三个enum属性 |
---|
SOURCE: 说明该自定义注解,只存在与XX.java中,在编译后的XX.class都是找不到的。比如@Override,可以通过jd-gui来查看XX.class文件,确实找不到该注解,该注解是在虚拟机层面进行解析的,实际自定义注解中大概也不会用到该类别。 |
CLASS : 存在于XX.java和XX.class文件中,但是在运行时Java虚拟机会忽略该注释,只有在编译阶段会读取到该注解,在运行时通过反射是获取不到该注解的,可以查看Butter Knife ,他就是编译时注解。 |
RUNTIME: 和class属性一样,都存在于源文件以及编译生成的class文件中,不同的是在运行时可以得到该注解,即可以运行时可以通过反射得到。 |
- Target表示该注解可以用在哪个地方,比如:用于属性字段、用于方法声明、用于包声明、用于接口声明等等,详细的可以参考ElementType。
在了解了Annotation的元注解以后,我们来解释GET
:
//还不是小儿科,直接忽略
@Documented
//说明该注解是使用在方法上的,且只能用在方法上
@Target(METHOD)
//说明该注解是运行时注解,可以通过反射得到
@Retention(RUNTIME)
public @interface GET {
//需要有一个属性
String value() default "";
}
怎么样,一个自定义的注解就可以完成了,妈妈再也不用担心我看不懂注解咯。其实所有的自定义注解都是照猫画虎,没啥困难的。还想深入全名学习注解的可以参考:Java注解(Annotation),保证药到病除。到现在为止,retrofit2.http下面所有的类基本都可以知道它们的真实含义了吧。
动态代理
在将ServiceMethod之前还是需要稍微介绍下Java的动态代理技术:动态代理是jdk封装好的比较好用的类代理生成方式,Proxy.newProxyInstance有三个参数:
- ClassLoader,这个比较通用,直接用类本身的Classloader就可以。
- Class<?>[] interfaces,表示该生成代理类要实现的接口功能。
- InvocationHandler h,接口功能的真正实现是h。
Proxy.newProxyInstance是通过反射的方式生成代理类,通过源码分析可以看到,Class对象是通过ProxyGenerator
生成的字节码,不信的话你可以通过ProxyGenerator
生成的byte[]保存到xx.class文件中,看看是不是把所有的功能都让h来完成的,可以参考:JDK动态代理的实现及原理,保证让你对动态代理有比较深刻的理解。
看到了没有,说话算话,真的很简单的介绍了动态代理。
开刀Retrofit.java
Retrofit是通过Builder模式生成的对象,所以直接对Retrofit.Builder类进行解析,这里只罗列来了属性和build(),属性设置方法略(没啥特别的,都是一堆类似set方法):
public static final class Builder {
//Retrofit运行的平台:具体实现类比较简单,其中包括了Android平台、IOS平台、还有java8平台。这里你就直接理解为Android平台吧。
private Platform platform;
//该接口直接实现类为OkHttpClient,相信我。
private okhttp3.Call.Factory callFactory;
private HttpUrl baseUrl;
private List<Converter.Factory> converterFactories = new ArrayList<>();
private List<CallAdapter.Factory> adapterFactories = new ArrayList<>();
//网络请求返回后是不在主线程的,那么是不是需要有一个主线程的executor来执行ui操作,猜对了就是他。
private Executor callbackExecutor;
//我暂时理解为懒加载的意思,如果为true则把所有的methond都进行反射,如果为false则用到哪个method再去反射。
private boolean validateEagerly;
}
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();
}
// 这里会默认添加一个适配器,这才能让API中的load方法返回Call<Rep>
List<CallAdapter.Factory> adapterFactories = new ArrayList<>(this.adapterFactories);
//即加入ExecutorCallAdapterFactory
adapterFactories.add(platform.defaultCallAdapterFactory(callbackExecutor));
// 没有给默认解析器,所以要自己定义一个解析器
List<Converter.Factory> converterFactories = new ArrayList<>(this.converterFactories);
return new Retrofit(callFactory, baseUrl, converterFactories, adapterFactories,
callbackExecutor, validateEagerly);
}
adapterFactories:这个东西举个栗子吧,由于adapterFactories中会默认添加ExecutorCallAdapterFactory,所以我们看到API中的方法返回Call是默认可以解析的。如果接触过RxJava的同学应该知道,为了让API中的方法能够返回Observable,则需要添加一个自定义Retrofit Adapters,而RxJavaCallAdapterFactory就是能够让API返回Observable的具体实现。所以你想自定义API中函数的返回类型,就需要添加自定义Adapter,而常见的Adapter在GayHub上都给有给出。
converterFactories:还是很好理解的,一般的网络请求后返回的body都是String形式的,那么怎么把String形式转换成具体的entity对象呢?就是他来实现的。你可以用Gson、jackson来解析返回的数据,只要你配置了真确的converter就可以啦。retrofit-converters,而默认中converterFactories为空的没有具体的实现,所以在初始化Retrofit的时候需要添加例如:addConverterFactory(GsonConverterFactory.create())的converter。
这种可插拔式的框架,可以灵活的适配项目之前已经使用的一些框架。如果retrofit只支持fastjson解析,而之前项目都用Gson解析的,那你会不会把之前的Gson都用fastjson替换(不然有洁癖的我是无法容忍的)?这就是retrofit可插拔的优点。
现在创建API的实例,通过Retrofit.create():
public <T> T create(final Class<T> service) {
//确保API是一个接口,且该接口没有继承其他接口
Utils.validateServiceInterface(service);
//看看是不是懒加载,如果true则会把API里面的所有method都进行一次性缓存。
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);
}
//android平台返回的是false,所以不管它
if (platform.isDefaultMethod(method)) {
return platform.invokeDefaultMethod(method, service, proxy, args);
}
//API中每个方法都会生成对应的一个ServiceMethod,解析该方法对应的Annotation信息。
ServiceMethod serviceMethod = loadServiceMethod(method);
//他就是okhttp3.Call的一个包装类,真正的网络请求在这里面进行,且是通过okhttp3.Call进行的。
OkHttpCall okHttpCall = new OkHttpCall<>(serviceMethod, args);
//返回该API中method的返回类型,例如Call<Rep>
return serviceMethod.callAdapter.adapt(okHttpCall);
}
});
}
adapt()默认返回的是ExecutorCallbackCall,他就是Call子类,所以你也可以直接把它理解为API函数的返回类型,例如load的返回值Call<Rep>,没有任何问题。
ServiceMethod咋一眼看去不知道什么鬼东西,其实他的存在就是:把API中的每个method方法解析成一个ServiceMethod对象,解析的内容就是该method对应的注解信息,把这些信息存储在对应的ServiceMethod对象中进行缓存等待网络请求使用。
ServiceMethod loadServiceMethod(Method method) {
ServiceMethod result;
synchronized (serviceMethodCache) {
result = serviceMethodCache.get(method);
if (result == null) {
result = new ServiceMethod.Builder(this, method).build();
//缓存,不多说,总不可能在多次调用API方法的时候,每次都使用反射进行解析吧。
serviceMethodCache.put(method, result);
}
}
return result;
}
下面就要开始解析ServiceMethod.Builder()方法,按照惯例只写属性和builder方法。这样,一个method中所有信息都被保存在ServiceMethod当中,后续的网络请求只需要获取ServiceMethod当中的数据即可。
static final class Builder<T> {
//需要介绍吗,就是上面loadServiceMethod传入的this
final Retrofit retrofit;
//动过动态代理得到的method,后续要详细的解析该method
final Method method;
//作用于该method的所有注解,比如API方法中的GET
final Annotation[] methodAnnotations;
//method方法中参数对应的注解,一个参数可以对应多个注解
final Annotation[][] parameterAnnotationsArray;
//method中参数对应的真实类型列表
final Type[] parameterTypes;
//method返回类型,就是你看到的Call<Rep>。
Type responseType;
//这一堆boolean自行脑补吧
boolean gotField;
boolean gotPart;
boolean gotBody;
boolean gotPath;
boolean gotQuery;
boolean gotUrl;
String httpMethod;
boolean hasBody;
boolean isFormEncoded;
boolean isMultipart;
String relativeUrl;
Headers headers;
MediaType contentType;
Set<String> relativeUrlParamNames;
ParameterHandler<?>[] parameterHandlers;
Converter<ResponseBody, T> responseConverter;
CallAdapter<?> callAdapter;
public ServiceMethod build() {
//获取CallAdapter,可以通过该函数知道,最终会调用retrofit.callAdapter(returnType, annotations);
callAdapter = createCallAdapter();
responseType = callAdapter.responseType();
//最终调用retrofit.responseBodyConverter(responseType, annotations);
responseConverter = createResponseConverter();
//猜都不用猜,肯定是解析retrofit2.http下面的注解,解析完之后上面的一堆boolean值差不多都赋值好咯。
for (Annotation annotation : methodAnnotations) {
parseMethodAnnotation(annotation);
}
int parameterCount = parameterAnnotationsArray.length;
parameterHandlers = new ParameterHandler<?>[parameterCount];
for (int p = 0; p < parameterCount; p++) {
Type parameterType = parameterTypes[p];
if (Utils.hasUnresolvableType(parameterType)) {
throw parameterError(p, "Parameter type must not include a type variable or wildcard: %s",
parameterType);
}
//解析对应参数和该参数的注解列表。
Annotation[] parameterAnnotations = parameterAnnotationsArray[p];
parameterHandlers[p] = parseParameter(p, parameterType, parameterAnnotations);
}
return new ServiceMethod<>(this);
}
}
通过对上述对Method的解析,得到了一一对应的ServiceMethod对象,最后通过serviceMethod.callAdapter.adapt(okHttpCall)
返回一个API中函数的返回类型。由于系统默认了使用ExecutorCallAdapterFactory
,可以对比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;
}
//这里的returnType是API中函数的返回类型,比如Call<Rep>
//responseType则是Rep,就是returnType对应的泛型类型。
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) {
//看看,直接把call返回了,这就是为什么可以处理API中函数是Call返回类型的原因。
return call;
}
};
}
}
好了,到这里Call<Rep> call = retrofit.create(API.class).load()
我们就得到了Call,接下去就是enqueue了,这个过程有没有很像Okhttp的enqueue,知识点有没有!前面我们已经知道了这里对应的Call就是OkhttpCall(不清楚的直接在动态代理里面找),所以要enqueue就去找OkhttpCall:
@Override public void enqueue(final Callback<T> callback) {
//这是啥东西,哈哈,就是要去真真网络请求的Call,不是retrofit的call。
//如果还是混淆了这两个call,那么只能再多看看Okhttp是怎么网络请求的。
okhttp3.Call call;
Throwable failure;
//禁止同一个call入列两次
synchronized (this) {
if (executed) throw new IllegalStateException("Already executed.");
executed = true;
call = rawCall;
failure = creationFailure;
if (call == null && failure == null) {
try {
//从该函数的命名就可以看到他的意思,创建真实的Call,即Okhttp的Call
call = rawCall = createRawCall();
} catch (Throwable t) {
failure = creationFailure = t;
}
}
}
if (failure != null) {
callback.onFailure(this, failure);
return;
}
if (canceled) {
call.cancel();
}
//真真的网络请求来了,这一块网络请求就是Okhttp真真的网络请求,看到这你要还不懂的话,怪我咯。
call.enqueue(new okhttp3.Callback() {
@Override public void onResponse(okhttp3.Call call, okhttp3.Response rawResponse)
throws IOException {
Response<T> response;
try {
//通过真实返回的rawResponse,解析得到retrofit的response
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();
}
}
//把成功的结果传递给回调,其中response中包好了解析好的对象。
private void callSuccess(Response<T> response) {
try {
callback.onResponse(OkHttpCall.this, response);
} catch (Throwable t) {
t.printStackTrace();
}
}
});
}
//把okhttp的返回rawResponse转换成retrofit的Response,并把解析内容放入到Response当中。
Response<T> parseResponse(okhttp3.Response rawResponse) throws IOException {
ResponseBody rawBody = rawResponse.body();
//该rawResponse只解析返回内容的头部,可以了解NoContentResponseBody读取body抛异常了
rawResponse = rawResponse.newBuilder()
.body(new NoContentResponseBody(rawBody.contentType(), rawBody.contentLength()))
.build();
int code = rawResponse.code();
if (code < 200 || code >= 300) {
try {
// Buffer the entire body to avoid future I/O.
ResponseBody bufferedBody = Utils.buffer(rawBody);
return Response.error(bufferedBody, rawResponse);
} finally {
rawBody.close();
}
}
if (code == 204 || code == 205) {
return Response.success(null, rawResponse);
}
//异常捕获response?不了解具体的用处。。。
ExceptionCatchingRequestBody catchingBody = new ExceptionCatchingRequestBody(rawBody);
try {
//这个地方厉害了,就是通过convert来转换成具体的对象,比如用GsonConverterFactory.create()来把String转换成具体对象。自行看代码,其实就一句话。
T body = serviceMethod.toResponse(catchingBody);
return Response.success(body, rawResponse);
} catch (RuntimeException e) {
catchingBody.throwIfCaught();
throw e;
}
}
到这里,我们就把解析好的T body
返回给回调接口Callback里面去了,现在我们就可以在onResponse里面通过Response.body()获取到解析以后的真正对象咯。
好了基本上能说的都说了,应该能够差不多了解了Retrofit的使用流程了。
Utils
Retrofit中有一个非常重要的工具类Utils,里面包含了所有的关于Type
的反射方法,比如通过Call<Rep>得到Rep的Type等等。
Type
是和泛型相关的接口,具体分为4种类型:
- ParameterizedType:具体的泛型类型,比如ArrayList<String>中,具体泛型就是String。
- TypeVariable:泛型变量,在泛型类里面使用该泛型变量,这时候该变量就是TypeVariable。
- GenericArrayType:泛型数组,String[],懂?
- WildcardType:通配符泛型,比如ArrayList<? extends Number>。
不想讲了,自己参考:Java中的Type详解
四.总结
本文通过Okhttp的网络请求为主线,然后把Retrofit是如何包装Okhttp的过程进行了分析,到这里你应该有能力去修改Retrofit去适应你自己app里面的业务功能了吧。
最后再把参考的几篇文章进行罗列下,欢迎提出问题,我说鸡蛋你说要:
网友评论