从Retrofit的源码来看 HTTP

关于Retrofit是啥,这里就很少解释了,仍是先来瞅下官网:java

而此次主要是了解它的底层动做机制,而在了解底层以前先来回顾一下官网的总体使用步骤:git

我们也以官网的这个例子为例,先从简单的使用开始逐步深刻,先新建一个工程:github

而后增长retrofit的build引用 ,以下:json

而后按官网的步骤,首先建立一个API接口,以下:api

我们以获取用户在github中的仓库为例,定义接口的API方法以下:数组

而后具体来调用一下,也如官网的描述同样:浏览器

而后此时并未发起HTTP请求,须要像okhttp那样调用一下这个方法,分同步和异步,固然这里得用异步喽,以下:缓存

而后增长访问网络的权限:网络

先来查看一下个人github的仓库:异步

而后运行一下:

成功了,其实这个接口返回的格式就是json,用浏览器能够访问看下结果:

接下来我们将结果打印成咱们看到的JSON格式同样,而我们目前成功返回的是一个RsponseBody对像,它是来自okhttp的,以下:

此时就须要注册一个转换器了,这里不细讲怎么用的,直接上结果,重点是经过简单的使用掌握其深层次的本质原理,也就是源码分析,下面来看怎么作这个转换:

那此时怎么写这个转换工厂呢,这时须要再加一个库,也就是gson的支持,关于gson是啥就里就很少说了,直接添加依赖以下:

此时就能够这么用了:

接下来则须要修改API接口了,由于咱们不想看到返回的ResonseBody对象,而想看到具体的JSON,从网站上返回的JSON能够看出其实就是一个JSON数组,因此返回的内容应该是一个List,因此修改一下:

而后里面的每一个对象则须要咱们手动定义出来,先假设这个对像类为Repo,以下:

接下来则定义该类:

而后再定义里面的字段,这里能够经过JSON自动转成Java的字段,能够用JsonFormat工具,以下:

而后将Json数组中的对象内容拷至其中:

接下来我们来修改一下返回值,以下:

而后运行:

ok,对于retrofit的简单用法就到此结束,重点是接下来分析它的源码:先从使用入口来进行分析的突破口,而使用入口就是它:

能把它分析明白了,那对于retrofit的核心原理也就清楚啦,因此点进去看下它的源码:

那此时就得看调用这个方法的对象是哪一个了,以下:

而这个API是我们定义的接口,也是抽象的。。

那此时就再得往前追溯了,得看它具体的对象:

若是知道了gitHubService的具体对象那么最终咱们就能够分析enqueue的具体实现了,因此定位其实现瞅一下:

这个方法是retrofit的核心,其实能够看到有动态代理的东东,因此如今就集中来分析一下该实现:

从字面意思来看是验证服务接口,看下究竟看了啥:

不重要,继续往下读:

这里是一个配置项的检查,表示是否要进行激进化的方法检查,具体就不细看了,不是核心,主要是对咱们写的api的方法合法性的检查,如:

若是开启了则会GitHubService一建立就会把全部的验证都作完了,很利于咱们的调试,很早就能够发现代码写得不对,可是!!不利于性能,大体知道就好了,继续往下看:

动态代理嘛,难道说retrofit的核心机制就是动态代理?其实确实是它,不过目前还不得而知,关于动态代理是啥这里就不过多解释了,j2se的基础,这里用伪代码来揭露其动态代码的本质,首先看第二个参数:

其实动态代理就是首先生成一个实现了该接口的对象,伪代码表示一下:

而后动态代理不是还有第三个参数InvocationHandler么?以下:

其实它就会传到动态生成的代理对象里面,而后在每一个具体方法实现中则会用到它来生成,伪代码以下:

若是说咱们在API接口中定义了多个方法,则在这个动态生成的对象中的实现也都是用invocationHandler来实现的,这就是动态代理的本质。 

那接下来就把精力花在这个invoke方法的具体实现上了,只要分析清楚了它,那么就知道为啥咱们仅仅声明一个API接口retrofit就能够实现一个网络请求了,因此,研究一下invoke方法的具体实现:

而若是调用的是接口中的默认实现方法【这是Java8才有的】,直接也不作其它任何处理了,对于使用retrofit而言不可能有这种默认方法,因此能够略过这个判断细节,继续往下探究:

好晕呀,这三行中涉及到彻底陌生的ServiceMethod、OkHttpCall,彻底不明白,这里就涉及到一个读源码的小技巧了,对于都看不懂的状况下,先对涉及到的类都大至认识一下既可,不用深究,因此我们一个个先来大体瞅一下:

啥意思?首先得理解一下什么是adapter,这个在咱们listview的开发中必用的概念,仍是先看一下它词的本义:

也就是作转接用的,也就是能够猜想ServiceMethod的做用是:

而后此类的代码量太大,也无法继续往下看了,仍是返回到主调代码处继续了解其它的东东,继续看下它:

而后我们来看一下ServiceMethod是如何生成的,经过生成细节看是否能进对ServiceMethod有一个进一步的了解,以下:

而后再看一下build()方法的细节:

而后再经过构造来实例化:

很经典的Builder模式,不过整个构建对象的细节彻底看不懂,先暂且放着,等回过头按需再来查看,先来讲一下Builder模式,人人皆知,这里简单说一下它的好处,一般咱们用Builder模式一般会这样写:

那它有啥好处呢?对于Person中有字段是有初始化成本的,什么意思?好比咱们用正常的方式来初始化会这样写:

首先就在内存中有person对象了,接着再来修改一下性别:

而默认性别是女的,此句执行以后就须要在内存中将女姓给擦掉,而后用这个设置的男性来替代,这是有性能损耗的,接着再来修改年龄:

若是默认年龄是24,那此时内存中又得将24给擦掉而后再画一个31岁的人,再接下来:

默认人是走路的行为,此时又得内存进行擦除改掉用户的行为,因此说这种传统的方式是有性能损耗的,而Builder模式则在构建对象时没有提早生成内存,先生成一个配置清单,最终一块儿来构建对象,这是它的最大好处之一,另一个好处就是当参数较多的时候这样写层次也比较清晰,关于builder模式这里简单提一下,还得回到我们所关心的retrofit实现原理上来:

打开瞅下它是啥?

那不就是说:

因此此时我们能够看一下enqueue的具体实现:

先跳出这个实现细节,总的来回顾一下:

因此点进入再看最后一行的细节:

没办法,还得硬着头皮点进去瞅下:

那看不懂呀,怎么整,目前咱们要了解的这三行代码,前两行大体猜到了一些意思,而最后一行彻底不晓得其内部的细节,那接下来就从头来细看一下,看是否经过细看能发现一些线索:

这个以前稍加看过,里在就是维护了一个缓存,不过这里仍是要看一下ServiceMethod的建立过程:

其中第一句看到了一个以前的疑问:

其中这上callAdapter是一个接口,因此此时不就解惑了么,因此看一下callAdapter是如何建立的?

跟进去:

再往下跟:

接下来就得看一下这段代码的实现了,先来瞅一下callAdapterFactories对象:

因此看一下它的调用,其实就是在build()方法中,如咱们在Activity写的:

因此此时再看一下callAdapterFactories的建立来源:

而后就得看下一句了:

因此。。得看一下"platform.defaultCallAdapterFactory(callbackExecutor)"的细节:

如:

其中咱们能够看到其实现中用到了一个“callbackExecutor”,经过它的执行而后再处理的回调:

因此得看一下callBackExecutor是如何传递进来的,此时就又得回调Retrofit.build()方法来了:

而后再进一步跟一下此callbackExecutor的建立细节:

那。。原来咱们看到的calladapter的做用是进行线程的转换哦,那咱们继续回到ServiceMethod.build()方法分析:

拿咱们定义的API接口方法来讲就是指的:

接下来往下:

继续往下

这不就是指么:

好,再继续往下:

另外有一个细节须要注意retrofit会对咱们写的注解的正确性作验证,会让咱们更加规范的使用okhttp,好比multipart须要配合part来使用等,好对于ServiceMethod的build()方法能够发现其实就是对咱们定义的API方法进行了解析并存下来,而后再实例化它:

至此,我们要想分析关键的第一句代码就完全搞清楚其做用了,回顾一下:

好,接下来再分析核心的第二句代码,好比好理解:

接着再来看第三句代码,其实经过上面的分析也晓得其做用了,挼一下:

而后此时得回顾一下callAdpater是如何建立出来的:

而后此方法的调用是在调用build()时进行的,以下:

因此最终调用adapt()方法的实际上是ExecutorCallAdapterFactory里面的了,以下:

也就是最终retrofit动态生成的对像在调用它里面的getRepos()方法返回的是ExecutorCallbackCall对像,以下:

因此接下来咱们再来分析最初咱们分析不动的方法就顺其天然啦,也就是:

那就是直接调用ExecutorCallbackCall.enqueue()方法,以下:

而代理的call是在咱们代理对像方法执行时动态建立的,以下:

因此最终就会转到OkHttpCall.enqueue()方法来,以下:

其中仍是利用了ServiceMethod来对以前解析的东东来转换成了okhttp的call,以下:

而后再利用Okhttp的Call进行异步请求,以下:

@Override public void enqueue(final Callback<T> callback) {
    checkNotNull(callback, "callback == null");

    okhttp3.Call call;
    Throwable failure;

    synchronized (this) {
      if (executed) throw new IllegalStateException("Already executed.");
      executed = true;

      call = rawCall;
      failure = creationFailure;
      if (call == null && failure == null) {
        try {
          call = rawCall = createRawCall();
        } catch (Throwable t) {
          throwIfFatal(t);
          failure = creationFailure = t;
        }
      }
    }

    if (failure != null) {
      callback.onFailure(this, failure);
      return;
    }

    if (canceled) {
      call.cancel();
    }

    call.enqueue(new okhttp3.Callback() {
      @Override public void onResponse(okhttp3.Call call, okhttp3.Response rawResponse) {
        Response<T> response;
        try {
          response = parseResponse(rawResponse);
        } catch (Throwable e) {
          callFailure(e);
          return;
        }

        try {
          callback.onResponse(OkHttpCall.this, response);
        } catch (Throwable t) {
          t.printStackTrace();
        }
      }

      @Override public void onFailure(okhttp3.Call call, IOException e) {
        callFailure(e);
      }

      private void callFailure(Throwable e) {
        try {
          callback.onFailure(OkHttpCall.this, e);
        } catch (Throwable t) { t.printStackTrace(); } } });
  }

其中回调是先对OkHttp的Response进行解析,解析成Retrofit的Response:

而后这个parseResponse()方法就能够体现出它与http的关系了,就是用了http的知识来编写的,大体瞅一下:

其中从okhttp的reponse转成retrofit的response最终还用到了converter了,以下:

最后还有一个知识就是retrofit如何集成rxjava,首先得集成一下rxjava,以下:

而后此时须要在增长一个calladapter,以下:

此时咱们的API定义返回就不用返回Call对像了,而是能够返回一个Observable,以下:

而后就能够用rxjava的那一套来进行接口请求及返回处理了,Retrofit是能够支持多个Adapter的,瞅一下:

其中咱们知道Retrofit默认的Adapter为CallAdapter,是能够将ResponseBody转换成一个Call对象,以下:

其具体实现是:

并达到一个切换线程的做用。

而此时加了一个Rxjava的CallAdapter,以下:

因此咱们在api能够为:

到此!!已经完整将Retrofit的整个核心机制分析完了,对于以后在实际工做中用Retrofit也更加踏实了~~说实话仍是挺复杂的。

相关文章
相关标签/搜索