时间:2021-05-20
之前在Retrofit源码初探一文中我们提出了三个问题:
其中第一个问题前几篇文章已经做了解答,今天我们探究下第二个问题。
之前也分析过,具体生成这个请求对象的是这句代码:
代码很简单,那我们就来探究下这个OkHttpCall能干什么:
final class OkHttpCall<T> implements Call<T> {可以看到其实主要实现了一个接口,所以我们看下这个接口都有哪些方法:
public interface Call<T> extends Cloneable { Response<T> execute() throws IOException; void enqueue(Callback<T> callback); boolean isExecuted(); void cancel(); boolean isCanceled(); Call<T> clone(); /** The original HTTP request. */ Request request();}看到这几个方法有没有很熟悉,没错,几乎和Okhttp的Call方法一模一样,我们看下okhttp的call接口:
public interface Call extends Cloneable { Request request(); Response execute() throws IOException; void enqueue(Callback var1); void cancel(); boolean isExecuted(); boolean isCanceled(); Call clone(); public interface Factory { Call newCall(Request var1); }}从这里我们猜测,Retrofit的OkHttpCall其实就是对OkHttp的call的一种包装,下面我们详细探究下每种方法,看是如何分别调用OkHttp的call中的方法的,有没有做什么特殊处理。
之前有提过看源码之前要带着问题去看,那么对于这个OkHttpCall我们想知道什么?之前提到过这是对OkHttp的okhttp3.Call的一个封装,那么每个方法必然会调用到okhttp3.Call对应的方法,所以我们提出两个问题:
这两个问题在每个主要方法中都能得到答案。
request()方法
@Override public synchronized Request request() { okhttp3.Call call = rawCall; if (call != null) { return call.request(); } if (creationFailure != null) { if (creationFailure instanceof IOException) { throw new RuntimeException("Unable to create request.", creationFailure); } else if (creationFailure instanceof RuntimeException) { throw (RuntimeException) creationFailure; } else { throw (Error) creationFailure; } } try { return (rawCall = createRawCall()).request(); } catch (RuntimeException | Error e) { throwIfFatal(e); // Do not assign a fatal error to creationFailure. creationFailure = e; throw e; } catch (IOException e) { creationFailure = e; throw new RuntimeException("Unable to create request.", e); } }可以看到,大致逻辑就是如果okhttp3.Call已经被实例化了直接调用它的request()方法,如果没有的话,会调用createRawCall()方法先实例化,然后再调用request方法。
所以想要解答okhttp3.Call是怎么生成的,就来看看这个createRawCall()方法:
private okhttp3.Call createRawCall() throws IOException { okhttp3.Call call = serviceMethod.toCall(args); if (call == null) { throw new NullPointerException("Call.Factory returned null."); } return call; }可以看到核心方法还是ServiceMethod中的toCall方法来生成的,这里提供了参数而已,继续跟进去:
okhttp3.Call toCall(@Nullable 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 callFactory.newCall(requestBuilder.build()); }这个方法最终其实就是调用okhttp3.Call中的这个方法:
public interface Factory { Call newCall(Request var1); }至于怎么根据Request生成Call是OkHttp干的,在ServiceMethod中的toCall方法,我们要做的就是用已有信息生成一个OkHttp的Request来,如何生成这个Request?这里利用了一个RequesetBuilder。
第一:处理方法级别的注解的信息
利用httpMethod,baseUrl,relativeUrl等直接new了一个RequestBuilder出来,这些信息都是从方法级别的注解中解析出来的。
第二:处理参数级别的注解信息
之前在生成ServiceMethod对象时,利用参数级别的注解生成了一个ParameterHandler数组,每个Handler都有一个apply方法,将参数信息设置到一个RequestBuilder中,这个apply方法就是在这里调用的。
经过上面两部,一个包含了http请求完整信息的RequesetBuilder就生成了,最后build下生成一个Request传到newCall方法中,则一个okhttp3.Call对象就生成了。
整个request()方法分析完了,做的事很简单,有okhttp3.Call对象就直接调用它的request()方法,没有就生成一个再调用,但大家注意到没有,他的代码设计安排很奇怪。如果是我来写这个方法,我可能会这样写:
public synchronized Request request1() { okhttp3.Call call = rawCall; if (call != null) { return call.request(); }else{ try { return (rawCall = createRawCall()).request(); } catch (RuntimeException | Error e) { throwIfFatal(e); // Do not assign a fatal error to creationFailure. throw e; } catch (IOException e) { throw new RuntimeException("Unable to create request.", e); } } }可以看到,和我自己的代码相比,原代码多了一个记录createRawCall()的异常的成员变量,这是处于效率考虑。由于我们的okhtt3.Call对象是延迟加载的,就是说在调用request方法时,其他的方法中有可能已经调用过createRawCall()方法,并由于某种原因失败了,我们将这个失败的异常记录下来,在调用createRawCall()方法之前做一次判断,如果已有异常就不需要调用createRawCall()方法了,提高了效率。
enque()
整个enque()方法的核心必然是调用okhttp3.Call的enque方法,我们重点关注调用之前有做什么,调用之后做了什么:
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); } @Override public void onFailure(okhttp3.Call call, IOException e) { callFailure(e); }在调用之前其实没做什么,和request()方法差不多,做了下提前判断而已,所以这里可以直接看代码,核心就是调用了parseResponse()方法将返回值转成了Retrofit的Response对象,然后调用了callSuccess()而已,所以我们跟进去:
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(); 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; } }这里逻辑很简单,根据不同的http状态码返回对应的Response对象,这里有一点,当状态码正常时,这里会利用一个converter将Body对象转成自己想要的,比如转成json等,具体处理是在serviceMethod.toResponse()中进行的。
日常偷懒环节
好了,关键时刻来了,分析了这两个方法后,OkHttpCall中的主要方法应该都讲到了,剩下的一些方法基本和上面两个差不多,大家对着来就行了!
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持。
声明:本页内容来源网络,仅供用户参考;我单位不保证亦不表示资料全面及准确无误,也不保证亦不表示这些资料为最新信息,如因任何原因,本网内容或者用户因倚赖本网内容造成任何损失或损害,我单位将不会负任何法律责任。如涉及版权问题,请提交至online#300.cn邮箱联系删除。
本文较为详细的分析了jQuery源码解读之removeClass()方法。分享给大家供大家参考。具体分析如下:removeClass()方法和addClass(
本文实例讲述了php源码分析之DZX1.5加密解密函数authcode用法。分享给大家供大家参考。具体分析如下:
分析vue是如何实现数据改变更新视图的.前记三个月前看了vue源码来分析如何做到响应式数据的,文章名字叫vue源码之响应式数据,最后分析到,数据变化后会调用Wa
本文实例讲述了php源码分析之DZX1.5字符串截断函数cutstr用法。分享给大家供大家参考。具体分析如下:
本文较为详细的分析了jQuery源码解读之addClass()方法。分享给大家供大家参考。具体分析如下:给jQuery原型对象扩展addClass功能,jQue