Retrofit源码解析

来源:互联网 发布:道路横断面效果图软件 编辑:程序博客网 时间:2024/06/18 00:04

借鉴的

别人写的源码解析



比较详细的retrofit2源码解读


首先Retrofit的使用:

1、创建出Retrofit对应实例

2、构建API对应的Interface实例

3、通过api调用对应方法获取返回

4、call.enqueue()加入队列转入主线程


retrofit的使用

GET方式@Query 以及@Querymap

  @GET("/appServer/interface/commonconf/checkVersion")//    Call<Version> getVersion(@Query("appId")String apid,@Query("curVersion")String version);//query要求("这之中的形参与服务端对应")    Call<Version> getVersion(@QueryMap Map<String,String> params);//querymap要求map中put进去的key与形参对应
@Post方式

@FormUrlEncoded与@Multipart两者其中FormUrlEncoded对应Field以及FieldMap而Multipart对应part以及partMap功能大致相同只是Multpart可以用来上传文件

@Field以及@FieldMap与@query相同

@Part @PartMap 与Field@FieldMap相同但可以使用流做文件上传


b. @Body

  • 作用:以 Post方式 传递 自定义数据类型 给服务器
  • 特别注意:如果提交的是一个Map,那么作用相当于 @Field 

不过Map要经过 FormBody.Builder 类处理成为符合 Okhttp 格式的表单,如:

FormBody.Builder builder = new FormBody.Builder();builder.add("key","value");


Retrofit的原理

retrofit.create(ZhuanLan.class)对应的使用动态代理的方案,实现了解耦

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);          }        });  }
调用invoke后调用loadServiceMethod方法
 ServiceMethod<?, ?> loadServiceMethod(Method method) {    ServiceMethod<?, ?> result = serviceMethodCache.get(method);    if (result != null) return result;    synchronized (serviceMethodCache) {      result = serviceMethodCache.get(method);      if (result == null) {        result = new ServiceMethod.Builder<>(this, method).build();        serviceMethodCache.put(method, result);      }    }    return result;  }

Retrofit中请求交友okhttp执行;通过动态代理解析出interface中的参数、路径等这个在ServiceMethod中进行    等等

private void parseMethodAnnotation(Annotation annotation) {      if (annotation instanceof DELETE) {        parseHttpMethodAndPath("DELETE", ((DELETE) annotation).value(), false);      } else if (annotation instanceof GET) {        parseHttpMethodAndPath("GET", ((GET) annotation).value(), false);      } else if (annotation instanceof HEAD) {        parseHttpMethodAndPath("HEAD", ((HEAD) annotation).value(), false);        if (!Void.class.equals(responseType)) {          throw methodError("HEAD method must use Void as response type.");        }      } else if (annotation instanceof PATCH) {        parseHttpMethodAndPath("PATCH", ((PATCH) annotation).value(), true);      } else if (annotation instanceof POST) {        parseHttpMethodAndPath("POST", ((POST) annotation).value(), true);      } else if (annotation instanceof PUT) {        parseHttpMethodAndPath("PUT", ((PUT) annotation).value(), true);      } else if (annotation instanceof OPTIONS) {        parseHttpMethodAndPath("OPTIONS", ((OPTIONS) annotation).value(), false);      } else if (annotation instanceof HTTP) {        HTTP http = (HTTP) annotation;        parseHttpMethodAndPath(http.method(), http.path(), http.hasBody());      } else if (annotation instanceof retrofit2.http.Headers) {        String[] headersToParse = ((retrofit2.http.Headers) annotation).value();        if (headersToParse.length == 0) {          throw methodError("@Headers annotation is empty.");        }        headers = parseHeaders(headersToParse);      } else if (annotation instanceof Multipart) {        if (isFormEncoded) {          throw methodError("Only one encoding annotation is allowed.");        }        isMultipart = true;      } else if (annotation instanceof FormUrlEncoded) {        if (isMultipart) {          throw methodError("Only one encoding annotation is allowed.");        }        isFormEncoded = true;      }    }    private void parseHttpMethodAndPath(String httpMethod, String value, boolean hasBody) {      if (this.httpMethod != null) {        throw methodError("Only one HTTP method is allowed. Found: %s and %s.",            this.httpMethod, httpMethod);      }      this.httpMethod = httpMethod;      this.hasBody = hasBody;      if (value.isEmpty()) {        return;      }      // Get the relative URL path and existing query string, if present.      int question = value.indexOf('?');      if (question != -1 && question < value.length() - 1) {        // Ensure the query string does not have any named parameters.        String queryParams = value.substring(question + 1);        Matcher queryParamMatcher = PARAM_URL_REGEX.matcher(queryParams);        if (queryParamMatcher.find()) {          throw methodError("URL query string \"%s\" must not have replace block. "              + "For dynamic query parameters use @Query.", queryParams);        }      }      this.relativeUrl = value;      this.relativeUrlParamNames = parsePathParameters(value);    }    private Headers parseHeaders(String[] headers) {      Headers.Builder builder = new Headers.Builder();      for (String header : headers) {        int colon = header.indexOf(':');        if (colon == -1 || colon == 0 || colon == header.length() - 1) {          throw methodError(              "@Headers value must be in the form \"Name: Value\". Found: \"%s\"", header);        }        String headerName = header.substring(0, colon);        String headerValue = header.substring(colon + 1).trim();        if ("Content-Type".equalsIgnoreCase(headerName)) {          MediaType type = MediaType.parse(headerValue);          if (type == null) {            throw methodError("Malformed content type: %s", headerValue);          }          contentType = type;        } else {          builder.add(headerName, headerValue);        }      }      return builder.build();    }    private ParameterHandler<?> parseParameter(        int p, Type parameterType, Annotation[] annotations) {      ParameterHandler<?> result = null;      for (Annotation annotation : annotations) {        ParameterHandler<?> annotationAction = parseParameterAnnotation(            p, parameterType, annotations, annotation);        if (annotationAction == null) {          continue;        }        if (result != null) {          throw parameterError(p, "Multiple Retrofit annotations found, only one allowed.");        }        result = annotationAction;      }      if (result == null) {        throw parameterError(p, "No Retrofit annotation found.");      }      return result;    }



真正执行网络请求的是OkHttpCall的execute方法

@Override public Response<T> 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 = rawCall = createRawCall();        } catch (IOException | RuntimeException e) {          creationFailure = e;          throw e;        }      }    }    if (canceled) {      call.cancel();    }    return parseResponse(call.execute());  }
可以看到默认使调用的是okhttp的网络请求

  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;    }  }

Retrofit  build()创建时相关的4是类

Call  默认okhtpClient,CallFactory 负责网络请求

CallAdapter 平台适配器 适配不同的平台的网络请求 Retrofit目前支持Android Rxjava 等

CallbackExecutor负责将请求结果返回到主线程上

Converter 负责将请求结果转化成对应的数据类型如JSON   XML  等


Retrofit 接口

1、Callback<T>回调

void onReSponse(Response<T> response)

void onFailure()

2、Converter<F,T> 解析

3、Call

4、CallAdapter

注意ServiceMethod中获取到request  交友httpCall(这个和volley的httpstack一样)执行最后获取到请求结果交友CallAdaper 处理

 /** Builds an HTTP request from method arguments. */  Request toRequest(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 requestBuilder.build();  }  /** Builds a method return value from an HTTP response body. */  T toResponse(ResponseBody body) throws IOException {    return responseConverter.convert(body);  }



执行Http请求

之前讲到,OkHttpCall是实现了Call接口的,并且是真正调用OkHttp3发送Http请求的类。OkHttp3发送一个Http请求需要一个Request对象,而这个Request对象就是从ServiceMethodtoRequest返回的

总的来说,OkHttpCall就是调用ServiceMethod获得一个可以执行的Request对象,然后等到Http请求返回后,再将response body传入ServiceMethod中,ServiceMethod就可以调用Converter接口将response body转成一个Java对象

结合上面说的就可以看出,ServiceMethod中几乎保存了一个api请求所有需要的数据,OkHttpCall需要从ServiceMethod中获得一个Request对象,然后得到response后,还需要传入ServiceMethodConverter转换成Java对象


Rertofit线程转换

Retrofit创建过程当中platform

static class Android extends Platform    {        @Override        public Executor defaultCallbackExecutor()        {            return new MainThreadExecutor();        }        static class MainThreadExecutor implements Executor        {            private final Handler handler = new Handler(Looper.getMainLooper());            @Override            public void execute(Runnable r)            {                handler.post(r);            }        }    }
而在ExecutorCallAdapter当中的enqueue方法通过callbackExecutor回调调用

@Override public void enqueue(final Callback<T> callback) {      if (callback == null) throw new NullPointerException("callback == null");      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()) {                // 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<T> call, final Throwable t) {          callbackExecutor.execute(new Runnable() {            @Override public void run() {              callback.onFailure(ExecutorCallbackCall.this, t);            }          });        }      });    }





Volley对比优势

  1. 缓存处理;Volley自己就提供了一套完整的缓存处理方案,默认使用文件存储到磁盘中,并且提供了TTL SOFTTTL这么体贴入微的机制;一个Request可能存在两个Response,对于需要显示缓存,再显示网络数据的场景真是爽的不要不要的。而Retrofit中并没有提供任何和缓存相关的方案。
  2. 代码简单,可读性高。Volley的代码是写的如此的直接了当,让你看起代码来都不需要喝口茶,这样的好处是我们我们需要修改代码时不太容易引入bug....囧
  3. 同一个请求如果同时都在发送,那么实际上只会有一个请求真正发出去, 其它的请求会等待这个结果回来,算小小优化吧。实际上这种场景不是很多哈,如果有,可能是你代码有问题...
  4. 请求发送的时候提供了优先级的概念,但是是只保证顺序出去,不保证顺序回来,然并卵。
  5. 支持不同的Http客户端实现,默认提供了HttpClientHttpUrlConnection的实现,而Retrofit在2.0版本之后,锁死在OkHttp上了。6.支持请求取消

Retrofit

1.发送请求真简单,定义一个方法就可以了,这么简单的请求框架还有谁?Volley?2.较好的可扩展性,Volley中每一个新建一个Request时都需要指定一个父类,告知序列化数据的方式,而Retrofit中只需要在配置时,指定各种转换器即可。CallAdapter的存在,可以使你随意代理调用的Call,不错不错。。。3.OkHttpClient自带并发光环,而Volley中的工作线程是自己维护的,那么就有可能存在线程由于异常退出之后,没有下一个工作线程补充的风险(线程池可以弥补这个缺陷),这在Retrofit中不存在,因为即使有问题,这个锅也会甩给OkHttp,嘿嘿4.支持请求取消

再次说明的是,Retrofit没有对缓存提供任何额外支持,也就是说它只能通过HTTPCache control做文件存储,这样就会有一些问题:1.需要Server通过Cache control头部来控制缓存,需要修改后台代码2.有些地方比较适合使用数据库来存储,比如关系存储,此时,Retrofit就无能为力了3.缓存不在我们的控制范围之内,而是完全通过OkHttp来管理,多少有些不便,比如我们要删除某一个指定的缓存,或者更新某一个指定缓存,代码写起来很别扭(自己hack请求头里面的cache contrl)

而在我们项目的实际使用过程中,缓存是一个比较重要的角色,Retrofit对缓存的支持度不是很好,真是让人伤心。。。但是,我们还是觉得在使用中Retrofit真心比较方便,容易上手,通过注解代码可读性和可维护性提升了N个档次,几乎没有样板代码(好吧,如果你觉得每个请求都需要定义一个方法,那这也算。。),所以最后的决定是选择Retrofit。

有人说了,Volley中的两次响应和缓存用起来很happy怎么办?嗯,我们会修改Retrofit,使它支持文件存储和ORM存储,并将Volley的缓存 网络两次响应回调移接过来,这个项目正在测试阶段,待我们项目做完小白鼠,上线稳定之后,我会把代码开源,大家敬请关注。





原创粉丝点击