从Retrofit的源码来看 HTTP

关于Retrofit是啥,这里就不多解释了,还是先来瞅下官网:

而这次主要是了解它的底层动作机制,而在了解底层之前先来回顾一下官网的整体使用步骤:

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

然后增加retrofit的build引用 ,如下:

然后按官网的步骤,首先创建一个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也更加踏实了~~说实话还是挺复杂的。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

webor2006

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值