retrofit的实现原理(三)

前面基本的原理和流程已经弄清了.再研究下某些实现.

CallbackRunnable(异步模式时在子线程执行的部分)

abstract class CallbackRunnable<T> implements Runnable {
  private final Callback<T> callback;
  private final Executor callbackExecutor;
  private final ErrorHandler errorHandler;

  CallbackRunnable(Callback<T> callback, Executor callbackExecutor, ErrorHandler errorHandler) {
    this.callback = callback;
    this.callbackExecutor = callbackExecutor;
    this.errorHandler = errorHandler;
  }

  @SuppressWarnings("unchecked")
  @Override public final void run() {            //在异步线程中实际执行的方法.
    try {
      final ResponseWrapper wrapper = obtainResponse();
      callbackExecutor.execute(new Runnable() {
        @Override public void run() {
          callback.success((T) wrapper.responseBody, wrapper.response);   //在主线程执行.
        }
      });
    } catch (RetrofitError e) {
      Throwable cause = errorHandler.handleError(e);     //这里的异常捕捉到了以后没有再次抛出,内部处理了.
      final RetrofitError handled = cause == e ? e : unexpectedError(e.getUrl(), cause);
      callbackExecutor.execute(new Runnable() {
        @Override public void run() {
          callback.failure(handled);                               //在主线程执行.
        }
      });
    }
  }

  public abstract ResponseWrapper obtainResponse(); //交给子类实现--->其实就是执行的前面的invokeRequest().
}

ResponseWrapper封装了response和responseBody.因为方法的返回值只能是一个.所以用它包装了下.

final class ResponseWrapper {
  final Response response;
  final Object responseBody;

  ResponseWrapper(Response response, Object responseBody) {
    this.response = response;
    this.responseBody = responseBody;
  }
}

Retrofit的Client是真正进行网络访问的逻辑.来看下具体的实现.

public interface Client {
  /**
   * Synchronously execute an HTTP represented by {@code request} and encapsulate all response data
   * into a {@link Response} instance.
   * <p>
   * Note: If the request has a body, its length and mime type will have already been added to the
   * header list as {@code Content-Length} and {@code Content-Type}, respectively. Do NOT alter
   * these values as they might have been set as a result of an application-level configuration.
   */
  Response execute(Request request) throws IOException;  //抽象方法,就是给定一个Request,执行完,返回一个Response.

  /**
   * Deferred means of obtaining a {@link Client}. For asynchronous requests this will always be
   * called on a background thread.
   */
  interface Provider {
    /** Obtain an HTTP client. Called once for each request. */
    Client get();
  }
}

OkClient

public class OkClient implements Client {
  private static OkHttpClient generateDefaultOkHttp() {
    OkHttpClient client = new OkHttpClient();
    client.setConnectTimeout(Defaults.CONNECT_TIMEOUT_MILLIS, TimeUnit.MILLISECONDS);
    client.setReadTimeout(Defaults.READ_TIMEOUT_MILLIS, TimeUnit.MILLISECONDS);
    return client;
  }

  private final OkHttpClient client;

  public OkClient() {
    this(generateDefaultOkHttp());
  }

  public OkClient(OkHttpClient client) {
    if (client == null) throw new NullPointerException("client == null");
    this.client = client;
  }

  @Override public Response execute(Request request) throws IOException {
    return parseResponse(client.newCall(createRequest(request)).execute());  //client.newCall().execute() 真正利用okHttp进行网络访问的地方.可以看到是采用的Okhttp的同步的方式.
  }

  static com.squareup.okhttp.Request createRequest(Request request) {  //把retrofit的Request中url,header等信息提取出来, 封装进okhttp的Request中.
    com.squareup.okhttp.Request.Builder builder = new com.squareup.okhttp.Request.Builder()
        .url(request.getUrl())
        .method(request.getMethod(), createRequestBody(request.getBody()));

    List<Header> headers = request.getHeaders();
    for (int i = 0, size = headers.size(); i < size; i++) {
      Header header = headers.get(i);
      String value = header.getValue();
      if (value == null) value = "";
      builder.addHeader(header.getName(), value);
    }

    return builder.build();
  }

  static Response parseResponse(com.squareup.okhttp.Response response) {
    return new Response(response.request().urlString(), response.code(), response.message(),
        createHeaders(response.headers()), createResponseBody(response.body()));
  }

  private static RequestBody createRequestBody(final TypedOutput body) {
    if (body == null) {
      return null;
    }
    final MediaType mediaType = MediaType.parse(body.mimeType());  //解析Response的类型.
    return new RequestBody() {
      @Override public MediaType contentType() {
        return mediaType;
      }

      @Override public void writeTo(BufferedSink sink) throws IOException {
        body.writeTo(sink.outputStream());
      }

      @Override public long contentLength() {
        return body.length();
      }
    };
  }

  private static TypedInput createResponseBody(final ResponseBody body) {
    if (body.contentLength() == 0) {
      return null;
    }
    return new TypedInput() {
      @Override public String mimeType() {
        MediaType mediaType = body.contentType();
        return mediaType == null ? null : mediaType.toString();
      }

      @Override public long length() {
        return body.contentLength();
      }

      @Override public InputStream in() throws IOException {
        return body.byteStream();
      }
    };
  }

  private static List<Header> createHeaders(Headers headers) {
    int size = headers.size();
    List<Header> headerList = new ArrayList<Header>(size);
    for (int i = 0; i < size; i++) {
      headerList.add(new Header(headers.name(i), headers.value(i)));
    }
    return headerList;
  }
}

retrofit的大体流程.

  用户自定义配置项的设置(如client,converter,拦截器等)--->解析接口的方法(如果曾经解析过就从缓存中获取),确定http访问的url,header,method等,确定是异步还是同步的方式------>使用具体的Client进行网络访问,并将数据封装到Response---->执行Converter的逻辑(有可能不用执行),把Response数据转换为一个具体对象.--->根据同步或者异步的方式,执行方法或者callBack的逻辑.

retrofit框架的需要注意的几个小点.

  1.为什么同步方式不像正常的方式一样要求用户try_catch来提醒用户捕捉异常?

  通过上面的逻辑可以看到,真正进行网络访问,converter转换的逻辑都在invokeHandler.invoke()方法执行的时候执行. 而这个方法的调用是在 用户自定义接口调用接口方法的时候执行的.(不明白的可以看下动态代理的原理).而用户自定义的接口方法是没有抛出异常的.在java中,如果父类方法没有抛出异常,子类方法也不能显示的抛出异常.(子类方法只能抛出父类方法抛出异常或其子类).所以Retrofit就不能抛出各种异常(如IO异常). 并且要抓住异常后转换为RuntimeException抛出.(动态代理生成的接口的实现类其实内部也采用了同样的方法.)

  异常抓住后不能直接内部处理,应该提醒用户代码执行的时候出了问题,所以必须抓住异常后再次抛出.而对于CallBack的方式,因为有failure()方法提示用户代码逻辑出了问题,所以就不用re-throw异常了.

  2.关于 InvocationHandler的invoke()方法, 这个方法有个返回值. 那这个返回值返回的是什么呢?

  首先明确Method.invoke(Object receiver,Object.. args)是和 receiver.method(args)等价的,2个方法的返回值是一样的.

public Object invoke(Object receiver, Object... args)  这里的Object是方法执行的的返回值.
---> Returns the result of dynamically invoking this method. Equivalent to {@code receiver.methodName(arg1, arg2, ... , argN)}.

  动态代理生成了接口A的代理类B,B的同名方法内部其实调用的是invocationHandler的 invoke()方法.返回的也是invoke方法的返回值. 所以invoke返回的类型就应该和接口方法的返回值类型一样.

时间: 2024-10-03 13:21:00

retrofit的实现原理(三)的相关文章

支持向量机原理(三)线性不可分支持向量机与核函数

支持向量机原理(一) 线性支持向量机 支持向量机原理(二) 线性支持向量机的软间隔最大化模型 支持向量机原理(三)线性不可分支持向量机与核函数 支持向量机原理(四)SMO算法原理(待填坑) 支持向量机原理(五)线性支持回归(待填坑) 在前面两篇我们讲到了线性可分SVM的硬间隔最大化和软间隔最大化的算法,它们对线性可分的数据有很好的处理,但是对完全线性不可分的数据没有办法.本文我们就来探讨SVM如何处理线性不可分的数据,重点讲述核函数在SVM中处理线性不可分数据的作用. 1. 回顾多项式回归 在线

相似图片搜索原理三(颜色直方图—c++实现)

图像的颜色直方图可以用于图像检索,适应有相同色彩,并且可以有平移.缩放.旋转不变性的图像检索,当然了这三大特点不如sift或者surf稳定性强,此外最大的局限就是如果形状内容一样,但色彩不一,结果是搜不到的.不过它在某些情况下达到较好的结果. 颜色直方图两种计算方式: 彩色图像的颜色直方图,这里可以有两种处理方式,得到的效果应该差不多. 首先第一种就是对像素的每个通道都进行划分,每个通道的最大像素值为255,可以等分8.16或者64等分,这样每个通道的范围就是0~15(以16等分为例,当然等分越

GCC编译器原理(三)------编译原理三:编译过程---预处理

Gcc的编译流程分为了四个步骤: 预处理,生成预编译文件(.文件):gcc –E hello.c –o hello.i 编译,生成汇编代码(.s文件):gcc –S hello.i –o hello.s 汇编,生成目标文件(.o文件):gcc –c hello.s –o hello.o 链接,生成可执行文件:gcc hello.o –o hello 一.预处理 预编译程序读出源代码,对其中内嵌的指示字进行响应,产生源代码的修改版本,修改后的版本会被编译程序读入. 在 GNU 术语中,预处理程序叫

GCC编译器原理(三)------编译原理三:编译过程(2-2)---编译之语法分析

2.2 语法分析 语法分析器(Grammar Parser)将对由扫描器产生的记号进行语法分析,从而产生语法树(Syntax Tree).整个分析过程采用了上下文无关语法(Context-free Grammar)的分析手段. 由语法分析器生成的语法树就是以表达式(Expression)为节点的树.如下所示: 从图中可以知道,整个语句就是一个赋值表达式:赋值表达式的左边是一个数组表达式,右边是一个乘法表达式:数组表达式又由两个符号表达式组成,等等.符号和数字是最小的表达式,它们不是由其他表达式来

Spring AOP 实现原理(三) 使用 使用 CGLIB 生成代理类

CGLIB(Code Generation Library),简单来说,就是一个代码生成类库.它可以在运行时候动态是生成某个类的子类. 此处使用前面定义的 Chinese 类,现在改为直接使用 CGLIB 来生成代理,这个代理类同样可以实现 Spring AOP 代理所达到的效果. 下面先为 CGLIB 提供一个拦截器实现类: public class AroundAdvice implements MethodInterceptor { public Object intercept(Obje

Java多线程系列--“JUC线程池”04之 线程池原理(三)

本章介绍线程池的生命周期. 线程有5种状态:新建状态,就绪状态,运行状态,阻塞状态,死亡状态.线程池也有5种状态:然而,线程池不同于线程,线程池的5种状态是:Running, SHUTDOWN, STOP, TIDYING, TERMINATED. 线程池状态定义代码如下: /** * The main pool control state, ctl, is an atomic integer packing * two conceptual fields * workerCount, indi

JVM原理三-----GC模块,垃圾回收

GC方法:在JVM启动时填入参数(比如:-XX:+UseConcMarkSweepGC ) 算法区分: 1.古老回收算法: Reference Counting  ,对象有一个引用,即增加一个计数,删除一个引用则减少一个计数.垃圾回收时,只用收集计数为0的对象.此算法最致命的是无法处理循环引用的问题. 2,Mark-sweep: 其实也是老的此算法执行分两阶段.第一阶段从引用根节点开始标记所有被引用的对象,第二阶段遍历整个堆,把未标记的对象清除.此算法需要暂停整个应用,会产生内存碎片. 3,Co

Android--带你一点点封装项目 MVP+BaseActivity+Retrofit+Dagger+RxJava(三)

1,这一篇博客是和大家一起来封装我们最后的Dagger2,其实之前也写过关于简单的Dagger2,这里是地址,完全没了解的同学可以先去看一下这篇,感谢很多小伙伴一直在耐心的等待这一篇 2,Dagger2可以说是些技术中最难上手的,不过把主要的四个注解理解到位了,基本上就可以用了: @Inject Inject主要有两个作用,一个是使用在构造函数上,通过标记构造函数让Dagger2来使用(Dagger2通过Inject标记可以在需要这个类实例的时候来找到这个构造函数并把相关实例new出来)从而提供

钩子教程 - 原理(三)

原文地址:http://www.zdexe.com/program/201004/577.html 勿在浮沙筑高台.继续学习.今天主要学习官方的资料.但是查找到的都是E文,俺只是翻译了一下.全部查找自msdn. Hooks tend to slow down the system because they increase the amount of processing the system must perform for each message. You should install a