2016-05-08 09:35:58

这篇文章解析一下Retrofit的调用流程

1. 先看一下我们是如何使用Retrofit的,代码如下:

 public interface WeatherDataService {
@GET("/wtr-v2/temp/realtime")
Call<MiWeatherData> getMiWeather(@Query("cityId") String cityId); @GET("/wtr-v2/temp/realtime")
Observable<MiWeatherData> getMiWeatherObservable(@Query("cityId") String cityId); @GET("/wtr-v2/temp/realtime")
MiWeatherData getMiWeatherCustomCallAdapter(@Query("cityId") String cityId);
}

这是获取天气信息的三个请求方法,区别在于返回值不同。下面代码展示了getWeather()是如何调用的,返回值类型时标准的Call<T>:

     private void getWeather() {
retrofit = new Retrofit.Builder()
.baseUrl("http://weatherapi.market.xiaomi.com")
.addConverterFactory(GsonConverterFactory.create(gson))
.build(); (1)
service = retrofit.create(WeatherDataService.class); (2)
Call<MiWeatherData> result = service.getMiWeather("101010100"); (3)

try {
Response<MiWeatherData> r = result.execute(); (4)
MiWeatherData data = r.body(); (5)
Log.e("David", "ResponseBody data = " + data);
if (data != null) {
Log.e("David", "RxLoaderCallback data = " + data.weatherinfo.SD);
Log.e("David", "RxLoaderCallback data = " + data.weatherinfo.cityid);
Log.e("David", "RxLoaderCallback data = " + data.weatherinfo.WS);
}
} catch (IOException e) {
e.printStackTrace();
}
}

(1)构建Retrofit对象,因为返回值是json串,所以用GsonConverter,使用默认的CallAdapter,也就是说返回值是Call<T>;

(2)使用动态代理创建我们自己定义的WeatherDataService接口实例;

(3)调用接口中真正获取天气信息的方法,返回值是Call<T>;

(4)调用Call<T>的execute()方法,真正执行网络请求,前面的UML途中提到过,还有一个enqueue()方法,两方法的区别是:execute()是同步且在主线程,enqueue()是异步且在工作线程,这里的工作线程不是我们设置的callbackExecutor,而是有okhttp3来控制的;

(5)由于getWeather()方法返回的是Response<T>,body()方法取到真正的MiWeatherData对象,Gson已经将json转换为Object;

2. 调用流程解析

其实整个调用流程最关键的就是上面提到的5个点,我们顺着这5个点来探究一下Retrofit是怎么调用的。

第一步就不用多说了,构建Retrofit对象,参数的作用上一篇文章已经做过分析了。

第二、三步很重要,我们看一下create()方法:

从(1)可以发现,确实是使用了动态代理,返回值就是我们自定义的接口实例对象T。由于T的所有方法都是抽象方法,当调用T的方法时,会被InvocationHandler拦截,真正的调用会转到InvocationHandler的invoke()方法中,其中method参数就是我们自己的抽象方法。

(2)处代码基于我们自己的method构造了一个ServiceMethod,构建过程中对方法中的各种注解做了解析,作用前面讲过了。又创建了一个OkHttpCall对象,这个对象将会在被adapt之后返回给客户端,类型取决于客户端的方法返回类型和设置的CallAdapter。这里的代码其实不是很好,耦合性太高,其实本意只是将OkHttpCall转换成客户端需要的返回值,那么CallAdapter对象是否有必要放在ServiceMethod,可以再仔细斟酌一下。

第四步execute网络请求,看一下源码:

1处代码创建一个真正的网络请求okhttp3.Call对象,源码如下:

首先调用了ServiceMethod的toRequest方法得到Request,里面包含了请求类型、请求参数等。然后调用了ServiceMethod的callFactory的newCall方法,这里的CallFactory是Retrofit.Builder的callFactory()设置的参数,默认使用OkHttpClient,我们也可以定义自己的网络请求类,只需要实现okhttp3的Call接口即可。因此默认使用的最终的网络请求类其实是okhttp3.RealCall。

2处代码执行真正的网络请求,绕了这么一大圈,终于进入正题了,同时对返回值做了解析。

其中调用了ServiceMethod的toResponse()方法,源码:

这里真正使用了我们设置的Converter,返回客户端需要类型的返回值。

3. 最终的流程图

作者在Retrofit的构建中使用了不少的设计模式,达到了耦合性低、扩展性强、灵活性高的目的。

第二篇:Retrofit调用流程图和使用到的设计模式的更多相关文章

  1. #PHP#微信支付 第二篇 JSAPI 调用统一下单接口获取预支付交易数据

    上一篇讲到成功获取 openid,本篇要调用微信统一接口创建预支付交易单,并获取到相关数据,以便(后边)在微信内调起H5支付 第三步,调用微信统一下单接口创建预支付交易单 微信统一下单API是微信支付 ...

  2. 微信支付 第三篇 微信调用H5页面进行支付

    上一篇讲到拿到了 预支付交易标识 wx251xxxxxxxxxxxxxxxxxxxxxxxxxxxxx078700 第四步,是时候微信内H5调起支付了! 先准备网页端接口请求参数列表 微信文档中已经明 ...

  3. [置顶] android利用jni调用第三方库——第二篇——编写库android程序直接调用第三方库libhello.so

    0:前言 1:本文主要作为丙方android公司的身份来写 2:作者有不对的地方,请指出,谢谢 [第一篇:android利用jni调用第三方库——编写库libhello.so] [第二篇:androi ...

  4. javascript立即调用的函数表达式N种写法(第二篇)

    原文:javascript立即调用的函数表达式N种写法(第二篇) 上一篇博客我谈到将函数声明转换为函数表达式最常见的一种写法是:通过括号()将匿名函数声明转换为函数表达式即(function(){}) ...

  5. ETL第二篇 调用webservice

    ETL第一篇(Kettle Spoon) 初遇 ETL第二篇 调用webservice 前言 这里使用ETL [Java代码] 实现 代码中使用axis调用webservice 在ETL提供了 Pro ...

  6. android调用第三方库——第二篇——编写库android程序直接调用第三方库libhello.so (转载)

    转自:http://blog.csdn.net/jiuyueguang/article/details/9449737 版权声明:本文为博主原创文章,未经博主允许不得转载. 0:前言 1:本文主要作为 ...

  7. [ 高并发]Java高并发编程系列第二篇--线程同步

    高并发,听起来高大上的一个词汇,在身处于互联网潮的社会大趋势下,高并发赋予了更多的传奇色彩.首先,我们可以看到很多招聘中,会提到有高并发项目者优先.高并发,意味着,你的前雇主,有很大的业务层面的需求, ...

  8. 深入理解javascript对象系列第二篇——属性操作

    × 目录 [1]查询 [2]设置 [3]删除[4]继承 前面的话 对于对象来说,属性操作是绕不开的话题.类似于“增删改查”的基本操作,属性操作分为属性查询.属性设置.属性删除,还包括属性继承.本文是对 ...

  9. [转]Android开源项目第二篇——工具库篇

    本文为那些不错的Android开源项目第二篇--开发工具库篇,主要介绍常用的开发库,包括依赖注入框架.图片缓存.网络相关.数据库ORM建模.Android公共库.Android 高版本向低版本兼容.多 ...

随机推荐

  1. 游戏对象消失三种方法的区别?(enabled/Destroy/active)

    gameObject.renderer.enabled=fasle是控制一个物体是否在屏幕上渲染或显示  而物体实际还是存在的 只是想当于隐身 而物体本身的碰撞体还依然存在的GameObject.De ...

  2. ECMAScript toString() 方法

    ECMAScript 定义所有对象都有 toString() 方法,无论它是伪对象,还是真对象. ECMAScript 的 Boolean 值.数字和字符串的原始值的有趣之处在于它们是伪对象,这意味着 ...

  3. Android 录音器

    Android自带的mediarecoder录音器不含pause暂停功能,解决方法:录制多个音频片段,最后合成一个文件. 参照 : http://blog.csdn.net/a601445984/ar ...

  4. SublimeText个性化快捷键设置

    一.光标跳出括号 在编写js函数的时候,输入函数名和括号的时候,要想光标跳出括号还得手动的按left键.离两个手的区域比较远,可自行配置 preferences - keys bindings - u ...

  5. FtpClient.storeFile返回false解决方法

    在确定路径和文件名没有中文的情况下添加以下代码 ftp.setFileTransferMode(ftp.BINARY_FILE_TYPE); ftp.enterLocalPassiveMode();/ ...

  6. window 配置wnmp(转下整理 ,全)

    工具/原料   RunHiddenConsole.exe 下载地址:http://pan.baidu.com/share/link?shareid=100074&uk=822373947 方法 ...

  7. an interview question(4)

    版权声明:本文为博主原创文章,未经博主允许不得转载. 写这篇博客前请让博主先吐糟下自己的PC. i3+2G内存+开了一上午=C盘剩下0字节+打开VS2012花了半个小时+一晚上的心情不好 吐槽完PC, ...

  8. php访问远程服务器上的文件

    test.php <?php $fp=fopen('http://www.baidu.com', 'r'); while (!feof($fp)) { $chunk=fgets($fp); ec ...

  9. ~/.config/user-dirs.dirs【桌面设置】

    # This file is written by xdg-user-dirs-update # If you want to change or add directories, just edit ...

  10. Calibre - book library management application

    http://calibre-ebook.com/ Library Management E-book conversion Syncing to e-book reader devices Down ...