Android Async HTTP Clients: Volley vs Retrofit

来源:互联网 发布:软件测试28原则 编辑:程序博客网 时间:2024/05/21 11:28

原文链接:http://instructure.github.io/blog/2013/12/09/volley-vs-retrofit/


我们最近发布了一个新版的android移动应用。虽然和上一个版本相比,在特设,设计和使用性上都有了较大的提升,仍然有一个头疼的问题我们没来得及考虑:速度。有时候APP并不像我们想象中那么流畅。在经过了一系列的断点和基础测试,以及从经验上的判断,我们认为从网络API获取数据是一个瓶颈。

一、传统方式 —— AsyncTasks

我们最近发布了一个新版的android移动应用。虽然和上一个版本相比,在特设,设计和使用性上都有了较大的提升,仍然有一个头疼的问题我们没来得及考虑:速度。有时候APP并不像我们想象中那么流畅。在经过了一系列的断点和基础测试,以及从经验上的判断,我们认为从网络API获取数据是一个瓶颈。

在最新的版本中,我们使用集成好的AsyncTasks来从Canvas服务器获取书序。简单的Google就能解释这个方式存在的许多问题:没有重定向支持,不能取消网络请求,不能轻易的实现并行化。除了Froyo和Gingerbread版本(编:当然包括之后的版本),AsyncTasks默认以序列化的方式运行。从实际意义上来讲,这就意味着一段时间内只能有一个AsyncTasks任务。如果一个View需要多个API来完成(主菜单目前需要7个),运行十分缓慢,有时候需要好几秒才能加载完成。

二、引入Volley和Retrofit

幸运的是,已经有一些第三方的库提供了后台并行化,网络缓存,以及一些特性,很大程度上简化了网络部分的代码。我们第一个关注的库是Volley,一个由Google开发的开源库。现在被AOSPAndroid(Android 开放源代码项目)和大部分的Google开发的应用所使用(编:例如Google Play)。另外一个被我们关注的库是RetroFit,另一个由Square开发的开源库。

 

三、我们如何比较的

将我们的APP中的网络架构转移到哪一个库不是一个简单的选择。和Canvas API的接口使我们APP中的一个重要部分。在本博客发表之前,Android项目中关于Canvas服务器的代码已经有43000行左右。其中,大约3500行代码用于和Canvas API做连接(建立API端口等)。JSON构造用了大约7300行。这就意味着超过25%的代码是用来从Canvas API获取并存储数据的。这还只是发起API请求和解析结果。这还不包扩任何AsyncTask代码或者缓存部分,并且我们认为这些应该也属于网络架构的代码。


正如你所见,如果我们想要转移我们的网络架构代码,必须要有一个有说服力的理由,因为这是一个艰巨的重构过程。为了决定这个步骤是否值得,我们对Volley和Retrofit进行了大量的调研。我们浏览了各种论坛、博客、实例代码。我们读了很多,我们也学习了很多,并且我们也会尽量以最好的方式总结出来。

从使用上来看,这两个库十分的相似。它们都提供了Callback接口来让你处理返回结果,其中包括了一些你必须实现的方法:成功和失败。这两个方法中的某一个会在异步网络请求完成时候被主线程调用。这两个库存在一个巨大的差异:你如何定义API端口以及你真正得到的是什么。

通过Volley,你在构造API请求的时候动态的定义整个端口。默认的,Volley根据请求类型的不同来返回一个JSONObject或者一个JSONArray。而Retrofit则是让你为所有API设置一个统一的端口,然后通过JAVA注解来定义静态的接口。你可以在发起API请求的时候,简单并且动态的替换部分路径,POST/GET变量等等。想要通过Retrofit发起API请求,你需要调用一个接口的方法,传入需要替换的变量,然后它就会返回一个JAVA对象。默认的,Retrofit自动的使用GSON来解析JSON(这种做法非常非常快),同时你也可以定义你自己的JSON构造器。尽管在构造过程中有一些区别,这两个库在发起API请求的时候是十分相似的。

为了对性能进行基础测试,我们编写了一个实例程序,使得我们可以控制并模拟实际场景中Android向Canvas服务器发起的API请求。这个过程同样可以让我们有一些对这两个库的使用经验。在开始阶段,我们写了一个基本APP,可以让我们在一个简单的API(返回简单的JSON)和一个复杂的API(返回很长的数据)之间进行切换。发起的API请求数量也可以在APP中进行修改。为了模拟一个更加真实的场景,我们包含了主菜单中向Canvas发起的一系列API请求。从这个基础测试中,我们发现以下三点:1)它们都比AsyncTasks更容易使用,2)它们都减少了很多代码量,3)它们都比我们目前使用的方案快很多。当然,这个基础测试的结果会由于网络情况产生偏差,但是,它们总体上来说胜过了我们目前使用的网络结构。

在对所有这三个库进行重复测试(1-25次)的结果中,Volley在任何一个方面都快了50%-70%。Retrofit在请求同一个端口相同数量的情况下,则十分惊人的比AsyncTasks快了50%-90%。在主菜单测试中,这代表了在加载和解析过程中能快好几秒。这在实际应用中是一个很大的不同。为了确保测试的准确性,这个时间包括了AsyncTasks和Volley解析JSON的事件,因为Retrofit自动帮你完成了这个过程。

在这一点上,我们希望通过替换网络库来提高性能。但是我们的决定还需要其他标准来衡量。如果我们需要花费时间来重构1/4的代码,我们必须十分谨慎。所以我们从以下几个方面做了考量:速度,易用性,简洁性,兼容性,以及重构所需要花费的事件。

四、Retrofit胜出

最终,我们决定将我们的应用转移到Retrofit上。不仅仅是因为它十分迅速(ridiculouslyfast),而且它和我们现有的架构十分契合。我们可以构造一个基础的Callback接口来自动处理异常和缓存,并且不需要对我们的API进行修改。为了集成Retrofit,我们需要重命名变量,使得我们的模型类服从GSON的规则。同时需要添加几个接口,从旧API中删除一些方法,并且修改我们的fragments的代码,使其不使用AsyncTasks。现在,我们已经完全转移了部分fragment,几乎不花费任何代价。尽管我们需要克服的困难和问题的数量在增长,但总体上来说并不多。在开始阶段,我们面临了几个技术上的问题和bug,但是Square提供了非常棒的Google+交流系统(编:没VPN的哭去吧),能够帮助我们解决问题。最终,我们成功转移了整个应用到Retrofit上。一个只依靠Retrofit的版本将在几星期后在Google Play上发布。

如果有任何问题,欢迎再评论中提问。

Feel free to ask questions in thecomments if you have any.

参考资源:

Retrofit:

·        Sourceand Samples (Source and samples)

·        SquareHomepage

·        SquareGoogle+ Community

Volley:

·        Source

·        Demonstration

·        Github of Examples

·        UsageExample

 

五、个人总结

对于 Volley和Retrofit的比较,我已经阅读了许多比较性的文章,两方的支持者都有。不过这个应该是给出数据比较明确的一个,并且从各个方面都有了一定的考虑(尽管没有具体说明)。下面我也来给出一个我自己的观点:

首先我是支持Retrofit的。

原因有很多,但总体上来说是因为Retrofit的高度封装和注解可以大大简化代码。并且从使用来说,Retrofit提供了对RxJava的支持,可以让开发人员无缝衔接当前流行的响应式编程。(关于响应式编程,可以参考最简洁的Android结构)因此,在目前关于架构的实例代码中,都是清一色使用Retrofit的。另外,由于是Square开发,就是为REST API量身打造的网络库。资料方面,其实需要使用的情形并不多,对于国内的开发人员来说基本百度就够了,官方文档看看基础实例基本够用,不过确实Retrofit的文档支持比较全面。

从性能上来说,两者的比较我并没有做过实验,网上的争议也很多。这也说明了其实两者性能差异不大,几毫秒或者几十毫秒的速度差异往往起不到决定性的作用。不过在新版本(2.3)的Retrofix中,默认使用了OKHttp和Okio进行处理,性能又将随之提升。而反观Volley,本身并不兼容OKHttp,尽管Jake大神给出了一个使用OKHttp版本的Volley,但是相当与需要自己扩展,自然是差了一些。

当然,使用Retrofit也是有一定的难处,就是在扩展性方面(高度封装所带来的必然后果)。由于解析数据都是使用的统一的converter,如果服务器不能给出统一的API的形式,将很难进行处理。(当然,这应该是服务器的锅)。另外,Retrofit也不支持动态的生成请求,使用场景上会受到一定限制,虽然我并没有遇到过限制。

也有人提到了Retrofit没办法取消已发出的请求这一点。据说新版本会支持这个特性,不过目前并没有做仔细的了解。另外,我在使用Volley开发的过程中,由于比较麻烦,也并没有使用到取消请求这一操作。

至于集成ImageLoader方面,其实并不影响Retrofit的使用,因为这个功能完全可以使用UltraImageLoader来实现。另外,Facebook最近也推出自己使用的Image加载库Fresco,还很良心的给出了中文文档,但是集成进去直接增加2MB的APK体积,于是我直接抛弃了。

翻译部分纯属手翻,有错误的地方欢迎指出。

3 0