-
适配器模式
上一篇文章我们已经分析了Retrofit解析注解封装进ServiceMethod的流程,读者在这里要记住,一个ServiceMethod就代表一个interface接口里的方法。相信部分读者已经忘了Retrofit的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, @Nullable 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<Object, Object> serviceMethod = (ServiceMethod<Object, Object>) loadServiceMethod(method); OkHttpCall<Object> okHttpCall = new OkHttpCall<>(serviceMethod, args); return serviceMethod.callAdapter.adapt(okHttpCall); } }); }
ServiceMethod初始化完毕之后,接下来需要创建Call对象。
OkHttpCall<Object> okHttpCall = new OkHttpCall<>(serviceMethod, args);
看到没有?这部分默认使用的是OkHttpCall,并且是写死的,用户无法自行扩展。如果你不想默认使用okhttp,那只能修改这部分代码,重新打包retrofit了。
构造完okhttpCall对象,接下来该做什么呢?
有了接口代理对象之后,我们就需要把http请求发送出去了。发送可以使用同步方式与异步方式。同步方式我们好理解,http请求发送出去后程序需要阻塞等待http响应的返回,这样我们自然会自己开辟子线程来执行同步请求等待结果并处理;但异步形式呢?既然是异步发送,http响应的到达一般是通过回调的形式通知的。而异步发送请求在Retrofit里面本身又是运行在子线程的,如果不经过特殊处理,回调自然也是运行在子线程。
这么说,难道还要用户自行从子线程切换到主线程去处理回调?这也太麻烦了吧?
想太多!!!Retrofit这么神奇的库,当然早就处理好了这种情况。还记得我们在上一篇文章里面提到的MainThreadExecutor吗?聪明的你肯定已经想起来了。是的,它就是用来执行切换操作的神器。
既然知道有这个神器存在,接下来该怎么使用呢?默认的okhttpCall对象和MainThreadExecutor是没有任何挂钩的,okhttpCall的异步请求的实现也没有去做任何的切换操作,而我们又需要执行异步操作完毕后可以自行切换到主线程去回调,很显然,okhttpCall不能满足我们的使用场景,该怎么办呢?
这时候就需要适配器模式来发挥作用了,把不能满足使用情形的Call适配成符合场景的另外一个Call。这个系列的文章开头就已经大致介绍了CallAdapter的作用,但还没有实际看过这个接口内部的构造,我们现在跟进去看看。
public interface CallAdapter<R, T> { //返回要将http响应转换到的Java对象类型,如Call<Repo>,则返回类型是Repo Type responseType(); T adapt(Call<R> call); abstract class Factory { public abstract @Nullable CallAdapter<?, ?> get(Type returnType, Annotation[] annotations, Retrofit retrofit); protected static Type getParameterUpperBound(int index, ParameterizedType type) { return Utils.getParameterUpperBound(index, type); } protected static Class<?> getRawType(Type type) { return Utils.getRawType(type); } } }
CallAdapter里面最重要的一个方法是adapt,它的作用是把传递进来的Call对象适配成符合程序需求的其他类型的对象。
还记得上一篇文章中,在建造者模式这一节中我们分析了默认的适配器工厂的赋值吗?是的,它的默认值是ExecutorCallAdapterFactory(忘记的读者请自行异步上一篇文章回顾哦)。我们再重新温习一下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<Object, Call<?>>() { @Override public Type responseType() { return responseType; } @Override public Call<Object> adapt(Call<Object> call) { return new ExecutorCallbackCall<>(callbackExecutor, call); } }; } }
ExecutorCallAdapterFactory的get方法是获取CallAdapter的入口,可以看到,get内部返回了一个匿名的callAdapter对象,实现了responseType和adapt两个方法。
因此,Retrofit.create的serviceMethod.callAdapter.adapt(okHttpCall)最终调用的是由ExecutorCallAdapterFactory获取到的CallAdapter的adapt方法。okhttpCall最终也被适配成ExecutorCallbackCall返回。这就是Retrofit需要的Call对象。
这里我们也关注两个点:
1. 构造ExecutorCallbackCall对象时,传入的callbackExecutor是我们在上一篇文章分析过的MainThreadExecutor;
2. 构造ExecutorCallbackCall对象时,传入的call是OkhttpCall;
至于适配的其他平台,如RxJava\Java8\Guava等,我们不做展开,这里主要是梳理分析Retrofit的主要流程源码。若有较多读者有需要,可以反馈给我,后续开专栏补上。
-
装饰者模式
创建完代理对象后,接下来就是使用代理对象来发送http请求了。发送分为同步和异步两种,对应Call接口里的execute和enqueue。我们首先分析execute流程,然后再分析enqueue流程,循序渐进。
经过Retrofit.create方法创建的Call对象实际上是ExecutorCallbackCall对象,因此,同步、异步请求真正调用的是ExecutorCallbackCall的execute和enqueue方法。
execute
@Override public Response<T> execute() throws IOException { return delegate.execute(); }
execute内部的实现非常简单,仅仅是调用了delegate的execute方法。那么delegate是什么呢?顾名思义,它是被ExecutorCallbackCall委托来执行execute操作的。我们再来看看ExecutorCallbackCall对于delegate的赋值。
static final class ExecutorCallbackCall<T> implements Call<T> { final Executor callbackExecutor; final Call<T> delegate; ExecutorCallbackCall(Executor callbackExecutor, Call<T> delegate) { this.callbackExecutor = callbackExecutor; this.delegate = delegate; } ... }
有没有似曾相识的感觉?是的,我们在上文的分析中,曾经划重点指出了ExecutorCallbackCall在构造时的默认赋值,其中,callbackExecutor的默认赋值是MainThreadExecutor,delegate的默认赋值是OkhttpCall。也就是说,实际的发起同步请求的操作还是由OkhttpCall来发起的。
将某个值通过依赖注入的形式在类内部持有其引用,之后再在类对应的方法里调用该引用的方法(前后可增加一些操作),这种模式就是装饰者模式。
好了,现在定位到OkhttpCall的execute方法。
@Override public Response<T> execute() throws IOException { okhttp3.Call call; synchronized (this) { if (executed) throw new IllegalStateException("Already executed."); executed = true; call = rawCall; if (call == null) { try { call = rawCall = createRawCall(); } catch (IOException | RuntimeException | Error e) { creationFailure = e; throw e; } } } if (canceled) { call.cancel(); } return parseResponse(call.execute()); }
Retrofit的一个请求对象只能执行一次,因此execute一开始会进行判断。通过判断之后,会对call对象赋值rawCall。rawCall一开始显然是null,所以会执行到createRawCall()来创建call对象。
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; }
Retrofit在创建ServiceMethod对象时,已经将请求的注解信息都封装进去了,这里直接调用ServiceMethod.toRequest就可以把一个接口方法转换成Request请求对象了。我们来看看ServiceMethod是如何将一个接口方法转换成Request对象的。
Request toRequest(@Nullable Object... args) throws IOException { RequestBuilder requestBuilder = new RequestBuilder(httpMethod, baseUrl, relativeUrl, headers, contentType, hasBody, isFormEncoded, isMultipart); ParameterHandler<Object>[] handlers = (ParameterHandler<Object>[]) parameterHandlers; int argumentCount = args != null ? args.length : 0; for (int p = 0; p < argumentCount; p++) { handlers[p].apply(requestBuilder, args[p]); } return requestBuilder.build(); }
toRequest内部使用了RequestBuilder来封装解析的注解信息,并对每个参数注解调用其处理器的apply方法进行转换解析,最后调用RequestBuilder.build方法构造出一个Request对象。RequestBuilder就是一个Request建造器,其内部源码不复杂,读者也请自行翻阅,这里不做展开,我们的解读目标依然是Retrofit主线。
创建出了Request对象之后,Request对象如何传输到网络上,还需要Call来实现。接下来createRawCall会执行serviceMethod.callFactory.newCall(request)。我们在上一篇文章中已经提到过,serviceMethod.callFactory实际上就是OkhttpClient,因此实际构造Call的操作是交由Okhttp来完成的(Okhtpp实际创建的是RealCall对象),本系列文章主要是探索Retrofit的源码实现,Okhttp部分的源码我们就不做深入分析了,读者有兴趣可以自行去阅读。
到此,Call对象就已经创建出来了,其本质是Okhttp的RealCall对象,这也直接说明了Retrofit本身并不负责网络请求,而是将该功能交由okhttp来执行,很好地解除了依赖。
Call对象创建完毕,剩下的就是以下步骤了。
parseResponse(call.execute())
call.execute实际就是调用Okhttp.RealCall.execute了。那么RealCall里面的execute到底做了什么呢?这里我们小小地涉足一下,浅尝辄止。
Okhtpp.RealCall @Override public Response execute() throws IOException { synchronized (this) { if (executed) throw new IllegalStateException("Already Executed"); executed = true; } try { client.dispatcher().executed(this); Response result = getResponseWithInterceptorChain(false); if (result == null) throw new IOException("Canceled"); return result; } finally { client.dispatcher().finished(this); } }
相信细心的读者已经发现了。
Response result = getResponseWithInterceptorChain(false);
RealCall是通过上述getResponseWithInterceptorChain方法获取到了http响应的。Okhttp发起网络请求和解析网络响应的核心正是使用了责任链模式实现的,这部分源码太多,这里不做展开,读者可自行搜索相关文章查看。
好了,现在让我们回到Retrofit。
call.execute执行完毕以后,程序会获取到Okhttp.Response类型的http响应。而Okhttp.Response在Retrofit中是无法被识别的,因此需要将其转换成REtrofit可以识别的对象,parseResponse()就是起这个作用的。
Response<T> parseResponse(okhttp3.Response rawResponse) throws IOException { ResponseBody rawBody = rawResponse.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) { rawBody.close(); return Response.success(null, rawResponse); } 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; } }
parseResponse解析出rawBody之后,调用了ServiceMethod.toResponse方法,将rawBody转换成对应的JavaBean对象。
R toResponse(ResponseBody body) throws IOException { return responseConverter.convert(body); }
可以看到,ServiceMethod的toResponse方法很简单,就是调用事先已经注册好的responseConverter来将rawBody对象convert成我们事先定义好的JavaBean。那么responseConverter的值各位读者还记得吗?对了,它就是我们一开始的时候注册进去的GsonConverterFactory.GsonResponseBodyConverter。
经过以上流程,execute方法就执行完毕了。
enqueue
分析完同步请求execute的流程,现在我们来分析异步请求enqueue。
call.enqueue调用的还是ExecutorCallbackCall的enqueue方法。
ExecutorCallbackCall @Override public void enqueue(final Callback<T> callback) { delegate.enqueue(new Callback<T>() { @Override public void onResponse(Call<T> call, final Response<T> response) { callbackExecutor.execute(new Runnable() { @Override public void run() { if (delegate.isCanceled()) { callback.onFailure(ExecutorCallbackCall.this, new IOException("Canceled")); } else { callback.onResponse(ExecutorCallbackCall.this, response); } } }); } @Override public void onFailure(Call<T> call, final Throwable t) { callbackExecutor.execute(new Runnable() { @Override public void run() { callback.onFailure(ExecutorCallbackCall.this, t); } }); } }); }
这里的enqueue依然是交由OkhttpCall的enqueue方法来执行。我们来看看OkhttpCall的enqueue方法。
OkhttpCall @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 { 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); }
与execute方法的执行流程相似,首先还是会调用createRawCall()创建一个Okhttp.RealCall对象,再调用该对象的enqueue方法发起真正的http异步请求。
OkhttpCall.enqueue方法的参数callBack是从ExecutorCallbackCall的enqueue方法传入的。当请求成功获取到响应时,OkhttpCall会执行和execute同样的解析操作:调用parseResponse,在parseResponse里面调用ServiceMethod.toResponse方法,将Okhttp.Response转换成JavaBean,最终再经由Retrofit.Response.success进行封装返回Retrofit.Response对象。
无论成功或者失败,OkhttpCall都会通过callBack将结果进行回调。这里的回调执行的是ExecutorCallbackCall传递的callBack。
我们再来回顾一下ExecutorCallbackCall的回调吧。
new Callback<T>() { @Override public void onResponse(Call<T> call, final Response<T> response) { callbackExecutor.execute(new Runnable() { @Override public void run() { if (delegate.isCanceled()) { callback.onFailure(ExecutorCallbackCall.this, new IOException("Canceled")); } else { callback.onResponse(ExecutorCallbackCall.this, response); } } }); } @Override public void onFailure(Call<T> call, final Throwable t) { callbackExecutor.execute(new Runnable() { @Override public void run() { callback.onFailure(ExecutorCallbackCall.this, t); } }); } }
可以看到,在onResponse和onFailure里面,真正执行回调的是callbackExecutor!!!
callbackExecutor.execute(...)
各位,各位,这里的callbackExecutor对象还记得吗???
它就是我们反复强调的MainThreadExecutor了!!!
从这里开始,异步请求的回调就顺利地从子线程切换到了主线程。
跋山涉水,一番探索之后,我们终于把Retrofit的源码捋顺了。希望这两篇文章对各位读者理解Retrofit的设计有所帮助,谢谢!
Retrofit源码解析(二)
最后编辑于 :
©著作权归作者所有,转载或内容合作请联系作者
- 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
- 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
- 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
推荐阅读更多精彩内容
- 前言 使用Retrofit已经一段时间了,这货挺好用的,还很特别,特别是使用接口来定义请求方式,这用法让我对它的源...
- 贵州茅台生态农业销售有限责任公司(以下简称“茅台生态农业公司”)是经贵州省国资委批准、中国贵州茅台酒厂(集团)...