金三银四速速临近,准备分析一下用了一段时日的Retrofit,本文分析Retrofit2.1.0版本,2.2.0最近新发布,据说支持RxJava2
(°∀°)ノ
Ps:对所用的框架分析也是面试重要一环哦,知其然而知其所然是考察程序员潜力的重要一环(๑•̀ㅂ•́)و✧
Retrofit源码分析(二)
介绍
Retrofit:大名鼎鼎的Square公司旗下的开源框架,是OkHttp的封装版,完成了高度的解耦,非常的好用,尤其配合是RESTful风格的服务端。
基本用法
//定义一个全局单例(推荐)
Retrofit retrofit = new Retrofit.Builder()
.baseUrl("yourAPI/")
.client(new OkHttpClient())
.addConverterFactory(GsonConverterFactory.create())
.build();
//定义一个接口,里面存放方法
public interface API{
//注意这个Call不是OkHttp3的Call
@GET("users/search/{keyword}")
Call> searchUserList(@Path("keyword") String keyword);
}
API api= retrofit.create(API.class);
api.searchUserList("张三").省略
入口
在我们构建了一个Retrofit实例后,调用了create方法,调用API接口中定义的方法后直接可以发起网络请求,这是怎么实现的呢?那么,请听下回分解。让我们进去瞧瞧。
public T create(final Class service) {
//判断了传入的service必须为interface,且不能实现其他接口
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 {
//如果调用的方法是Object的
if (method.getDeclaringClass() == Object.class) {
return method.invoke(this, args);
}
//默认进不来这个if
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);
}
});
}
可以看到这里运用了Java的动态代理技术,关于动态代理的详细介绍,这篇写的很详细。
我们先来看执行细节的第一句
第一句
ServiceMethod serviceMethod = loadServiceMethod(method);
看起来像是把我们在API中定义的方法searchUserList
封装成了一个对象
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;
}
果然,这里还运用了建造者模式和缓存来设计,API中的同一个方法只会构建一次,值得一提的是ServiceMethod的原注释
Adapts an invocation of an interface method into an HTTP call
意思是一个接口方法对应一个Http请求,及接下来让我们来看看ServiceMethod的构造器,都带上了注释,看官暂时不明白也没关系,后面会一一讲解清楚。反正这博文也没人看(;¬_¬)
ServiceMethod(Builder builder) {
//负责创建请求
this.callFactory = builder.retrofit.callFactory();
//负责将请求适配成API中的Call或者RxJava的Observable
this.callAdapter = builder.callAdapter;
//请求的baseUrl
this.baseUrl = builder.retrofit.baseUrl();
//响应体的解析器,上文设置了Gson来解析成
this.responseConverter = builder.responseConverter;
//请求的方式,POST,GET。。。
this.httpMethod = builder.httpMethod;
//请求的相对路径,API中定义的路径
this.relativeUrl = builder.relativeUrl;
//请求报文头
this.headers = builder.headers;
//请求报文类型
this.contentType = builder.contentType;
//是否带有请求体
this.hasBody = builder.hasBody;
//是否表单请求
this.isFormEncoded = builder.isFormEncoded;
//是否是Multipart请求,一般用于传输文件
this.isMultipart = builder.isMultipart;
//数组,记录请求中方法的参数,@Path,@Query等等
this.parameterHandlers = builder.parameterHandlers;
}
我们前文在API中定义的一个方法searchUserList
的生辰八字(包括方法的注解信息)就被封装到了ServiceMethod中了。
第二句
OkHttpCall okHttpCall = new OkHttpCall<>(serviceMethod, args);
如果说第一句是乘客准备好了身份证,那么第二句代码就是购票上车的具体操作。
我们可以看到OkHttpCall
的内部是实现了retrofit2
包下的Call
,主要是一个同步方法execute
,一个异步方法enqueue
,
@Override
public void enqueue(final Callback 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
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 response;
try {
//将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();
}
}
private void callSuccess(Response response) {
try {
callback.onResponse(OkHttpCall.this, response);
} catch (Throwable t) {
t.printStackTrace();
}
}
});
}
@Override
public Response execute() throws IOException {
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
call = rawCall = createRawCall();
} catch (IOException | RuntimeException e) {
creationFailure = e;
throw e;
}
}
}
if (canceled) {
call.cancel();
}
//将Response转换
return parseResponse(call.execute());
}
细心地读者可能都发现了,在这2个方法中都调用了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;
}
这个方法完成了如下几步操作
1.将serviceMethod
(乘客身份信息)+args
(乘客选择的列车座位)生成了一个okhttp3.Request
(列车票)
2.serviceMethod.callFactory.newCall(request)
上车
3.将call返回,通知上级乘客已经就位
toRequest(args)
无非就是将参数构建成一个okhttp3请求,略过~~这里注意,将request
转换成okhttp3.Call
是通过callFactory
来完成的,callFactory
是okhttp3.Call
下的Factory
接口,默认也就是自家的OkHttpClient
,也可以通过new Retrofit.Builder().client(yourClient)
来改变。
还有一个是
Response 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();
int code = rawResponse.code();
if (code < 200 || code >= 300) {
//Http错误码的情况
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) {
//204-205时服务端不返回实体内容
return Response.success(null, rawResponse);
}
ExceptionCatchingRequestBody catchingBody = new ExceptionCatchingRequestBody(rawBody);
try {
//将从okhttp3来的response通过转换再解析成数据实体T
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);
}
接口的初始化这里
new Retrofit.Builder()
.addConverterFactory(GsonConverterFactory.create())
.build();
也就是说看官不喜欢Gson也可以选用Jackson以及自定义,在项目中一般都要自定义,这里先挖个坑,(`・ω・´)
那么我们花了不少的篇幅来讲解乘客购票上车这个动作,毕竟上车了不是还会有验票吗 手动滑稽
第三句
return serviceMethod.callAdapter.adapt(okHttpCall);
接下来我们来看动态代理的最后一句,也就是司机开车,哦不,发起请求
这里我们又见到了老熟人serviceMethod
,这里也是通过callAdapter
这个接口来发起请求的。
//我们也可以添加对RxJava的支持,安利一波
Retrofit retrofit = new Retrofit.Builder()
.addCallAdapterFactory(RxJavaCallAdapterFactory.create())
.build();
由于在Retrofit中支持的adapterFactories
是一个List类型,并且默认会在List中添加一个默认的ExecutorCallAdapterFactory
,这里我们先分析它的源码
final class ExecutorCallAdapterFactory extends CallAdapter.Factory {
final Executor callbackExecutor;
ExecutorCallAdapterFactory(Executor callbackExecutor) {
//传入一个回调 调度器
this.callbackExecutor = callbackExecutor;
}
@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>() {
@Override public Type responseType() {
return responseType;
}
@Override public Call adapt(Call call) {
//返回调度器的回调结果
return new ExecutorCallbackCall<>(callbackExecutor, call);
}
};
}
static final class ExecutorCallbackCall implements Call {
final Executor callbackExecutor;
final Call delegate;
ExecutorCallbackCall(Executor callbackExecutor, Call delegate) {
this.callbackExecutor = callbackExecutor;
this.delegate = delegate;
}
@Override public void enqueue(final Callback callback) {
if (callback == null) throw new NullPointerException("callback == null");
delegate.enqueue(new Callback() {
@Override public void onResponse(Call call, final Response response) {
callbackExecutor.execute(new Runnable() {
@Override public void run() {
if (delegate.isCanceled()) {
// Emulate OkHttp's behavior of throwing/delivering an IOException on cancellation.
callback.onFailure(ExecutorCallbackCall.this, new IOException("Canceled"));
} else {
callback.onResponse(ExecutorCallbackCall.this, response);
}
}
});
}
@Override public void onFailure(Call call, final Throwable t) {
callbackExecutor.execute(new Runnable() {
@Override public void run() {
callback.onFailure(ExecutorCallbackCall.this, t);
}
});
}
});
}
@Override public boolean isExecuted() {
return delegate.isExecuted();
}
@Override public Response execute() throws IOException {
return delegate.execute();
}
@Override public void cancel() {
delegate.cancel();
}
@Override public boolean isCanceled() {
return delegate.isCanceled();
}
@SuppressWarnings("CloneDoesntCallSuperClone") // Performing deep clone.
@Override public Call clone() {
return new ExecutorCallbackCall<>(callbackExecutor, delegate.clone());
}
@Override public Request request() {
return delegate.request();
}
}
}
短短100行代码看官可能看的有点找不到北
在
Retrofit.Builder
中我们执行最后的
build()
时,Retrofit已经为我们准备了默认的调度器,也就是Handler,也就是说
callbackExecutor.execute(Runnable)
这个方法就相当于Handler post一个任务到主线程,那么剩下的也就是纸老虎了,细心的同学可能注意到了,
ExecutorCallbackCall
其实就是一个装饰者模式。那么我们就简单的分析完了Retrofit的一部分源码。
总结
笔者总结了一张精简的本文阉割版,如有错误,敬请指正。
对RxJavaAdapter的分析和其他的补充就放到下一篇了(。・ω・。)
Retrofit源码分析(二)
“你们是世上的盐。盐若失了味,怎能叫它再咸呢?以后无用,不过丢在外面,被人践踏了。你们是世上的光。城造在山上是不能隐藏的。人点灯,不放在斗底下,是放在灯台上,就照亮一家的人。你们的光也当这样照在人前,叫他们看见你们的好行为,便将荣耀归给你们在天上的父。” (马太福音 5:13-16 和合本)