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:调用RestMethodInfoinit方法,该方法参见附录1

标注2:先说RequestInterceptor,作用很明显,当执行请求时拦截请求以做一些特殊处理,比如添加一些额外的请求参数,参见附录2.

标注3:执行请求,执行的过程亲参见附录3。我们以UrlHttpConnction为例。

标注4:接着解析并分发请求结果了,成功请求时返回结果,解析失败调用ErrorHandler给用户一个自定义异常的机会,但最终都是通过异常抛出到invoke()中的,如果是同步调用,直接抛异常,如果是Callback调用,会回调Callback.failure


附录

1.RestMethodInfoinit方法

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

     * &#64;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&#64;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流。


好了,该博客系列就告一段落了。


0 0