文档章节

Retrofit源码分析

街角的小丑
 街角的小丑
发布于 2017/09/11 16:06
字数 1369
阅读 13
收藏 0
点赞 0
评论 0

前言

    第一次接触retrofit的时候还是个菜鸟(现在也是),使用一套规则定义一个接口,没有实现,结果竟然能够顺利完成和okhttp的对接。于是想要了解一下它的实现原理。

    本文仅是简单的源码分析,在读本文之前,或许应该先读一下下面这篇文章:

https://my.oschina.net/zzxzzg/blog/993685

Retrofit.Build

    Retrofit总是从一个Build开始,我们会在这个build中进行一些全局性的设置,比如baseUrl,Converter,CallAdapter,okhttpclient等。

    所以Retrofit.Build.build()功能并不复杂,保存设置的项目,创建一个真正 的Retrofit对象,不过其实还是有一些值得注意的

  1.     设置了一个默认的callbackExecutor(方法回调的线程执行器),就会使用默认的,而在Android平台中,默认就是主线程中执行,通过一个叫做MainThreadExecutor的类。
  2. 无论是否添加了自己的CallAdapter,都会添加一个默认的CallAdapter,而在android中,就是类ExecutorCallAdapterFactory.

    注:    CallAdapter 就是将返回的Call类型进行封装的一个适配器,Converter是对返回的ResponseBody进行封装处理的适配器。

Retrofit.create

    Retrofit所有神奇的起源,我们通过调用该方法,将一个接口变成了一个可以调用的类。

    实际上使用的原理就是Proxy代理:

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

    首先是eagerlyValidateMethods方法,将service中的所有方法封装成一个ServiceMethod对象,并存储起来。至于ServiceMethod是什么,接下去再看。

    然后就返回了一个ProxyInstance,当我们调用定义的接口中的方法时,就会转而调用invoke方法。

    invoke方法的工作其实就是删选出该方法是否是接口中定义的,如果是,那么获取该方法的servicemethod,然后创建一个okhttpcall(请求结果回调)。最后再将okhttpcall用适当的calladapter(见上)进行封装后返回。

ExecutorCallAdapterFactory     ExecutorCallbackCall

    CallAdapter.Factory 声明了一个get的接口,就是根据返回类型和注解(你在接口中声明的某个方法的返回类型,和方法的注解)生产一个CallAdapter(每个方法都会生成一个CallAdapter).

    而CallAdapter实际上之前有说过,就是将源返回值(Call<?>)进行封装的一个适配器类,通过调用方法adapt进行封装。

    知道这两个后,那么久看下Androir默认一定会提供的ExecutorCallAdapterFactory,这个东西的get方法很简单,只要返回类型是Call的,都匹配,并返回一个CallAdapter。这个CallAdapter的adapt方法返回一个ExecutorCallbackCall类型对象,继承与call,它的作用非常简单直接,将所有call中的回调方法迁移到指定的线程。(而Android中默认指定了主线程。)

OKHttpCall

    对于okhttp的使用这里我们不做介绍,我们知道okhttp总是会有大概如下的调用

    Call call = client.newCall(request);

    call.enqueue(CallBack ...)

    call.execute

    通过request产生一个call,然后在该call上进行同步或者异步的请求。而retrofit的okhttpcall也是采用同样的接口。

    我们在retrofit.create中已经介绍了,我们通过一个servicemethod和对应的参数来生成一个okhttpcall,然后将这个call在传给对应的calldapter进行一次封装,等待触发enqueue或者execute(不同的calladapter封装成不同的类型,rxjava2calladapter或者之前的ExecutorCallbackCall等,触发方式不同,但是本质上就是调用call的这两个方法。)    

    我们已enqueue为例(execute其实一样),首先通过调用createRawCall方法来生成一个call。

 private okhttp3.Call createRawCall() throws IOException {
    Request request = serviceMethod.toRequest(args);
    okhttp3.Call call = serviceMethod.callFactory.newCall(request);
    if (call == null) {
      throw new NullPointerException("Call.Factory returned null.");
    }
    return call;
  }

    很简单,调用serviceMethod中的 toRequest方法生成request,然后调用callFactory的newCall(callFactory就是我们设置的okhttpclient或者默认的new OkhttpClient)。

    生成call以后,就通过在这个call上调用enqueue进行请求,并且注册回调。回调的onResponse方法中,我们会对response进行一次解析,通过方法parseResponse。该方法实现有些复杂,但是功能很清晰,就是将okhttp的response包装成retrofit自己提供的Response对象(converter的body解析也是在这里进行调用的,具体就是调用了serviceMethod的toResponse的方法)。

ServiceMethod

   同上上述分析,我们发现ServiceMethod承载的最主要的功能就是toRequest方法,生成一个request(当然也有toResponse来进行解析,但是其实代码很简单)。

   如果你觉得我会详细去分析这个东西,那么你就错了,不妨亲自打开代码,看看build方法和toRequest方法在干嘛。其中已经并不涉及到架构问题,虽然不一定简单,甚至说是复杂,但是至少我们知道它做的就是通过解析我们定义时使用的注解,将他和传入的参数进行合并,生成request的过程。

总结

    retrofit代码的原理并不复杂,但是在笔者看来,算得上是adapter界的楷模,在适配器的使用上非常规范标准,值得借鉴。 

© 著作权归作者所有

共有 人打赏支持
街角的小丑
粉丝 1
博文 88
码字总数 143196
作品 0
杭州
Android网络框架

Android OKHttp3.0 以上使用方法 Android OKHttp3.0 以上使用方法详解 Retrofit 之日志拦截 Retrofit 日志拦截相关介绍 Retrofit源码解析 Retrofit的源码分析将从基本的使用方法入手,分析r...

掘金官方
2017/12/12
0
0
Retrofit源码分析

关于Retrofit的使用这里就不再赘述了,还不太了解Retrofit使用的同学 Retrofit简介 今天我们来聊一聊Retrofit的内部实现,通过源码来分析Retrofit的整个执行顺序。 在没有框架的时候,做一次...

乐小文
2017/04/09
0
0
第三方开源库 Retrofit - 源码设计模式分析

Retrofit 这个开源库对我成长还是挺大的,自己虽不常用,但他的源码封装思想,却需要用到实际的开发中。这些年有两本书一直都在看 《Android的源码》和《 JakeWharton的源码》。JakeWharton...

红橙Darren
2017/12/15
0
0
网络库Retrofit2原理简析

之前我们分析过了Okhttp这个优秀的网络请求库,但是在实际的使用时,还是会觉得有很多的不方便,你会发现它跟HttpUrlConnection,或者HttpClient一样,是一个比较底层的网络请求库,处理的是...

Ihesong
2017/12/11
0
0
Retrofit2.0- 源码分析

1. 阅读引导 在分析 Retrofit 源码之前,你首先得理解动态代理模式,因为Retrofit是通过动态代理的方式进行统一网络请求接口类的处理。Retrofit的代码量并不大,只是对OkHttp进行了封装,用更...

负了时光不负卿
2017/12/13
0
0
自省

就在前两天写完一部分Handler源码分析的文章后意识到了两个问题: 现在的学习进度太慢了,如果以这个速度下去定好的目标根本完不成。 我忘了来简书的初心,来简书不是来交朋友的,也不是为了...

吴七禁
2017/11/10
0
0
我的微信公众号开通了!

你好,感谢你来到这里。 我是张拭心,在内蒙古大草原长大,拥有十多年羊群管理经验。 后来由于放的羊都长不肥,家里只好把我送去大学好好培养一下。 大学阴差阳错调剂到西电计科专业,失去了...

u011240877
2016/09/23
0
0
你真的会用Retrofit2吗?Retrofit2完全教程

本文注目录: Retrofit入门 Retrofit注解详解 Gson与Converter RxJava与CallAdapter 自定义Converter 自定义CallAdapter 其它说明 前言 本文中的Retrofit均指代Retrofit2.0。 本文涉及到的代...

火云
2016/12/27
79
0
Lifecycle+Retrofit+Room完美结合 领略架构之美

安卓开发技术发展到现在已经非常成熟,有很多的技术专项如插件,热修,加固,瘦身,性能优化,自动化测试等已经在业界有了完善的或者开源的解决方案。 作为一枚多年的安卓研发,有必要学习或...

常兴E站
2017/12/11
0
0
从架构角度看Retrofit的作用、原理和启示

Retrofit是squareup公司的开源力作,和同属squareup公司开源的OkHttp,一个负责网络调度,一个负责网络执行,为Android开发者提供了即方便又高效的网络访问框架。 不过,对于Retrofit这样设计...

蓝灰_q
2017/10/28
0
0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

Call to undefined function Workerman\posix_getpid

workerman 在centos下报PHP Fatal error: Call to undefined function Workerman\posix_getpid posix在下面这个包中php-process...

dragon_tech
5分钟前
0
0
mysql 7.4 创建表 时  所建表 字段太多 导致失败

报错:[Err] 1118 - Row size too large (> 8126). Changing some columns to TEXT or BLOB may help. In current row format, BLOB prefix of 0 bytes is stored inline. 解决方法:把表的引......

qimh
8分钟前
0
0
语法补漏

1.if...elif...else... 代码示例: test=10 if test>9: print(1) elif test>8: print(2) elif test>7: print(3) else: print('nothing') 输出代码: 1...

十年磨一剑3344
8分钟前
0
0
Python -re模块及正则表达式解析

传送门: https://blog.csdn.net/pipisorry/article/details/25909899 ps:上面文章中"命名分组"的语法格式不能执行。正确的如下: (?P<name>正则表达式) #name是一个合法的标识符 除了使用别名...

一口今心
15分钟前
0
0
mybatis中session.getMapper方法源码分析

0开始代码AuthorMapper mapper = session.getMapper(AuthorMapper.class); 1 DefaultSqlSession类 @Override public <T> T getMapper(Class<T> type) { //最后会去调用MapperRegistry.getMap......

writeademo
24分钟前
1
0
spring cloud zuul网关的作用

zuul一般有两大作用,1是类似于Nginx的网址重定向,但zuul的重定向的一般是整个spring cloud里在Eureka注册中心的模块. zuul: ignored-services: '*' sensitiveHeaders: routes: ...

算法之名
24分钟前
9
0
java按比例之原图生成缩略图

package com.wxp.test; import java.awt.Image; import java.awt.image.BufferedImage; import java.io.File; import java.io.FileOutputStream; import javax.imageio.ImageIO; import sun.......

恋码之子
34分钟前
1
0
SpringCloud 微服务 (十五) 服务容错 Hystrix

壹 工作中的微服务架构,某个服务通常会被多个服务调用或者多层调用完成需求,如果某个服务不可用,导致一个系统功能不可用或者服务直接没用了的情况,这种情况称为雪崩效应 有A服务调用B服务,B服...

___大侠
36分钟前
1
0
Spring框架中的设计模式(五)

Spring框架中的设计模式(五) 通过以前的4篇文章,我们看到Spring采用了大量的关于创建和结构方面的设计模式。本文将描述属于行为方面的两种设计模式:命令和访问者。 前传: Spring框架中的...

瑞查德-Jack
38分钟前
1
0
解决phpstorm运行很卡问题!

phpStorm一旦达到这个临界值,所有智能提示、自动补全都失效了 这TM就很尴尬了,顿时感觉自己就是个废人了,纯手写代码跟便秘一样 众所周知phpStorm基于JAVA,那么这个内存限制肯定跟JAVA的虚...

sjcehui2010
41分钟前
0
0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

返回顶部
顶部