android网络框架retrofit源码解析四
来源:互联网 发布:局域网是什么端口 编辑:程序博客网 时间:2024/05/23 21:23
在阅读retrofit源码的过程中主要参考了该博客:http://www.2cto.com/kf/201405/305248.html
在前面的文章中,在使用动态代理invoke方法来调用我们的方法的时候。
无论是哪种请求,最后都是通过invokeRequest方法来执行http的request
/**
* Execute an HTTP request.
*
* @return HTTP response object of specified {@codetype} or {@code null}.
* @throws RetrofitError if any error occurs duringthe HTTP request.
*/
private Object invokeRequest(RequestInterceptorrequestInterceptor, RestMethodInfo methodInfo,
Object[] args) {
String url = null;
try {
methodInfo.init(); // Ensure all relevant methodinformation has been loaded.//标注1
String serverUrl = server.getUrl();//标注2
RequestBuilder requestBuilder = new RequestBuilder(serverUrl, methodInfo,converter);
requestBuilder.setArguments(args);
requestInterceptor.intercept(requestBuilder);
Request request =requestBuilder.build();
url = request.getUrl();
if (!methodInfo.isSynchronous) {
// If we are executing asynchronously thenupdate the current thread with a useful name.
Thread.currentThread().setName(THREAD_PREFIX+ url.substring(serverUrl.length()));
}
if (logLevel.log()) {
// Log the request data.
request = logAndReplaceRequest("HTTP", request);
}
Object profilerObject = null;
if (profiler !=null) {
profilerObject =profiler.beforeCall();
}
long start = System.nanoTime();
Response response =clientProvider.get().execute(request);//标注3
long elapsedTime =TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - start);
int statusCode = response.getStatus();//标注4
if (profiler !=null) {
RequestInformation requestInfo =getRequestInfo(serverUrl, methodInfo, request);
//noinspectionunchecked
profiler.afterCall(requestInfo,elapsedTime, statusCode, profilerObject);
}
if (logLevel.log()) {
// Log the response data.
response = logAndReplaceResponse(url,response, elapsedTime);
}
Type type = methodInfo.responseObjectType;
if (statusCode >= 200 && statusCode< 300) {// 2XX ==successful request
// Caller requested the raw Response objectdirectly.
if (type.equals(Response.class)) {
if (!methodInfo.isStreaming) {
// Read the entire stream and replace withone backed by a byte[].
response =Utils.readBodyToBytesIfNecessary(response);
}
if (methodInfo.isSynchronous) {
return response;
}
return new ResponseWrapper(response, response);
}
TypedInput body = response.getBody();
if (body ==null) {
if (methodInfo.isSynchronous) {
return null;
}
return new ResponseWrapper(response, null);
}
ExceptionCatchingTypedInput wrapped =new ExceptionCatchingTypedInput(body);
try {
Object convert =converter.fromBody(wrapped, type);
if (methodInfo.isSynchronous) {
return convert;
}
return new ResponseWrapper(response, convert);
} catch (ConversionException e) {
// If the underlying input stream threw anexception, propagate that rather than
// indicating that it was a conversionexception.
if (wrapped.threwException()) {
throw wrapped.getThrownException();
}
// The response body was partially read bythe converter. Replace it with null.
response =Utils.replaceResponseBody(response, null);
throw RetrofitError.conversionError(url, response,converter, type, e);
}
}
response =Utils.readBodyToBytesIfNecessary(response);
throw RetrofitError.httpError(url, response,converter, type);
}catch (RetrofitError e) {
throw e;// Pass through our own errors.
}catch (IOException e) {
if (logLevel.log()) {
logException(e, url);
}
throw RetrofitError.networkError(url, e);
}catch (Throwable t) {
if (logLevel.log()) {
logException(t, url);
}
throw RetrofitError.unexpectedError(url, t);
}finally {
if (!methodInfo.isSynchronous) {
Thread.currentThread().setName(IDLE_THREAD_NAME);
}
}
}
}
标注1:调用RestMethodInfo的init方法,该方法参见附录1
标注2:先说RequestInterceptor,作用很明显,当执行请求时拦截请求以做一些特殊处理,比如添加一些额外的请求参数,参见附录2.
标注3:执行请求,执行的过程亲参见附录3。我们以UrlHttpConnction为例。
标注4:接着解析并分发请求结果了,成功请求时返回结果,解析失败调用ErrorHandler给用户一个自定义异常的机会,但最终都是通过异常抛出到invoke()中的,如果是同步调用,直接抛异常,如果是Callback调用,会回调Callback.failure
附录
1.RestMethodInfo的init方法
synchronized void init() {
if (loaded)return;
parseMethodAnnotations();
parseParameters();
loaded = true;
}
只在第一次调用时解析,因为处一次解析后这个对象就被缓存起来了,下次调同一个方法时可以直接使用
ParseMethodAnnotations()方法是解析方法的注解
ParseParameters()方法是解析方法的参数
对于每一个Annotation,也会获取它的Annotation,看它是否是被RestMethod注解的Annotation,如果是,说明是@GET,@POST类型的注解,就调用parsePath解析请求的Url,requestParam(URL中问号后的内容)及Url中需要替换的参数名(Url中大括号括起来的部分)
寻找Headers Annotation解析Header参数
解析RequestType:SIMPLE,MULTIPART,FORM_URL_ENCODED
parseParameters解析请求参数,即参数的Annotation,@PATH、@HEADER、@FIELD等
2. RequestInterceptor
/**Intercept every request before it is executed in order to add additional data.*/
public interface RequestInterceptor {
/** Called for every request. Add data using methods onthe supplied{@linkRequestFacade}. */
void intercept(RequestFacade request);
interface RequestFacade {
/** Add a header to the request. This will not replaceany existing headers. */
void addHeader(String name, String value);
/**
* Add a path parameter replacement. Thisworks exactly like a{@linkretrofit.http.Path
* @Path}-annotated method argument.
*/
void addPathParam(String name, String value);
/**
* Add a path parameter replacement withoutfirst URI encoding. This works exactly like a
* {@link retrofit.http.EncodedPath@EncodedPath}-annotated method argument.
*/
void addEncodedPathParam(String name, Stringvalue);
/** Add an additional query parameter. This will notreplace any existing query parameters. */
void addQueryParam(String name, String value);
/**
* Add an additional query parameterwithout first URI encoding. This will not replace any
* existing query parameters.
*/
void addEncodedQueryParam(String name, Stringvalue);
}
/** A {@linkRequestInterceptor} which does no modification ofrequests. */
RequestInterceptor NONE =new RequestInterceptor() {
@Override publicvoid intercept(RequestFacade request) {
// Do nothing.
}
};
}
RequestInterceptor只有一个方法intercept,接收一个RequestFacade参数,RequestFacade是RequestInterceptor内部的一个接口,这个接口的方法就是添加请求参数,Query、Header什么的。大概可以看出RequestInterceptor的作用了,如果RequestFacade表示一个请求相关的数据,RequestInteceptor.intercept的作用就是向这个RequestFacade中添加额外Header,Param等参数。
RequestFacade的一个子类叫RequestBuilder,用来处理Request请求参数,在invokeRequest中会对RequestBuilder调用intercept方法向RequestBuilder添加额外的参数。
有一个叫RequestInterceptorTape的类,同时实现了RequestFacade与RequestInterceptor,它的作用是:
当作为RequestFacade使用时作为参数传给一个RequestInteceptor,这个RequestInterceptor调用它的addHeader等方法时,它把这些调用及参数记录下来
然后作为RequestInterceptor使用时,将之前记录的方法调用及参数重新应用到它的intercept参数RequestFacade中
在RestHandler.invoke中,如果判断方法的调用不是同步调用,就通过下面的两行代码将用户设置的interceptor需要添加的参数记录到RequestInterceptorTape,然后在invokeRequest中再实际执行参数的添加。
// Apply the interceptor synchronously,recording the interception so we can replay it later.
// This way we still defer argumentserialization to the background thread.
final RequestInterceptorTapeinterceptorTape = new RequestInterceptorTape();
requestInterceptor.intercept(interceptorTape);
RequestBuilder.setArguments()解析调用接口时的实际参数。然后通过build()方法生成一个Request对象
3.urlhttpconnection执行请求
/**Retrofit client that uses {@link HttpURLConnection} for communication. */
public class UrlConnectionClient implements Client {
private static final int CHUNK_SIZE = 4096;
public UrlConnectionClient() {
}
@Override public Responseexecute(Request request) throws IOException {
HttpURLConnection connection = openConnection(request);
prepareRequest(connection, request);
return readResponse(connection);
}
protected HttpURLConnection openConnection(Request request)throws IOException {
HttpURLConnection connection =
(HttpURLConnection) new URL(request.getUrl()).openConnection();
connection.setConnectTimeout(Defaults.CONNECT_TIMEOUT_MILLIS);
connection.setReadTimeout(Defaults.READ_TIMEOUT_MILLIS);
return connection;
}
void prepareRequest(HttpURLConnection connection, Request request)throws IOException {
connection.setRequestMethod(request.getMethod());
connection.setDoInput(true);
for (Header header : request.getHeaders()) {
connection.addRequestProperty(header.getName(),header.getValue());
}
TypedOutput body = request.getBody();
if (body !=null) {
connection.setDoOutput(true);
connection.addRequestProperty("Content-Type", body.mimeType());
long length = body.length();
if (length != -1) {
connection.setFixedLengthStreamingMode((int) length);
connection.addRequestProperty("Content-Length", String.valueOf(length));
}else {
connection.setChunkedStreamingMode(CHUNK_SIZE);
}
body.writeTo(connection.getOutputStream());
}
}
Response readResponse(HttpURLConnectionconnection) throws IOException {
int status = connection.getResponseCode();
String reason = connection.getResponseMessage();
if (reason ==null) reason = ""; // HttpURLConnection treats empty reason as null.
List<Header> headers = new ArrayList<Header>();
for (Map.Entry<String, List<String>>field : connection.getHeaderFields().entrySet()) {
String name = field.getKey();
for (String value : field.getValue()) {
headers.add(new Header(name, value));
}
}
String mimeType = connection.getContentType();
int length = connection.getContentLength();
InputStream stream;
if (status >= 400) {
stream = connection.getErrorStream();
} else {
stream = connection.getInputStream();
}
TypedInput responseBody = new TypedInputStream(mimeType, length, stream);
return new Response(connection.getURL().toString(), status, reason, headers,responseBody);
}
private static class TypedInputStream implements TypedInput {
private final String mimeType;
private final long length;
private final InputStream stream;
private TypedInputStream(String mimeType,long length, InputStream stream) {
this.mimeType = mimeType;
this.length = length;
this.stream = stream;
}
@Override public String mimeType() {
return mimeType;
}
@Override publiclong length() {
return length;
}
@Override public InputStream in()throws IOException {
return stream;
}
}
}
可以看出,请求经过了打开请求,准备请求,和返回请求信息的过程,最后返回的是Response 里面包含了,请求的URL,状态,原因,和TypedInput,TypedInput里面包含了mimeType,长度,以及对应的io流。
好了,该博客系列就告一段落了。
- android网络框架retrofit源码解析四
- android网络框架retrofit源码解析一
- android网络框架retrofit源码解析二
- retrofit网络框架源码解析
- retrofit网络框架源码解析
- retrofit网络框架源码解析
- andorid网络框架retrofit源码解析三
- Android Retrofit源码解析
- Android Retrofit框架解析
- Android Retrofit框架解析
- Android网络编程(十一)源码解析Retrofit
- Android 网络框架Retrofit的使用和解析
- Android网络开发框架Retrofit(四:扩展篇,Retrofit+RxJava)
- Retrofit网络请求框架使用简析——Android网络请求框架(四)
- Retrofit源码解析(四)---ServiceMethod相关分析
- 【Retrofit】Retrofit源码解析
- Android 网络框架 Volley 源码解析
- 【框架】网络请求+Gson解析--Retrofit 2
- 类加载与类的生命周期
- 荷兰国旗问题
- IOS设置全局的导航图片和字体颜色
- http://taiyuan.ganchang.cn/info/index/14097081544494000006.html http://www.rayfile.com/zh-cn/files/f
- 使用AWStats自动分析Nginx日志
- android网络框架retrofit源码解析四
- Struts2上传图片
- 网站加速--服务器编写篇 (下)
- [C#]C#如何求出两个字符串最大的公共部分
- HDU-1003-Max Sum
- 兔子视频 OpenGL 客户端技术解析
- 实现udev/mdev自动挂载与卸载(linux)
- Java 和 Android 中的回调
- spring事务传播机制和嵌套事务