Retrofit 2使用方法举例

  RetrofitSquare推出的一个“type-safe HTTP client for Android and Java”,通过将HTTP(REST) API转换为Java接口,极大地简化了HTTP API的使用。下面通过一个例子介绍Retrofit的基本使用,通过Retrofit向TMDb请求流行电影的信息,得到JSON格式的响应,由GSON解析后,以列表的形式显示出来。

0. The Movie Database

  下面的例子中使用了The Movie Database(TMDb)的API来获取当前流行电影的信息。要使用TMDb的API,首先要获取一个API key,注册TMDb后,在这里申请API Key,然后点击账号名称,在左边栏的“API” / “Details”下查看自己的API key,如图1所示。

图1

图1

 

  下面例子中使用“/discover/movie”端点来获取一系列电影的信息,如:

可以得到以流行度(popularity)降序(desc)排列的一系列电影,注意把 [YOUR API KEY] 替换为你自己的API key。将请求粘贴到浏览器,成功的话会得到一堆挤在一起的JSON文本,为了方便阅读,可以使用一些工具来将它格式化一下,格式化后结果可以参考这里

1. 获取Retrofit

  新建工程,在/app/build.gradle中dependencies下加入retrofit:

然后执行“Tools” / “Android” / “Sync Project with Gradle Files”,Retrofit就会被下载并添加到工程中。

  不要忘了在AndroidManifest.xml中添加INTERNET权限:

2.创建POJO

  POJO即Plain Old Java Object,它类似于JavaBean,但可以不遵守JavaBean的一些约定。我们从服务器得到JSON格式的响应,使用GSON反序列化到POJO中。

  首先来看一下TMDb返回的JSON,截取开头部分如下:

"page":1 说明当前位于第一页, "results" 后是一个长度为20的列表,列表中的每个元素描述了一部电影的信息,包括 "poster_path" 、 "adult" 、 "overview" 等。我们需要创建两个POJO类,一个对应包括 "poster_path" 、 "adult" 、 "overview" 等的电影信息本身Movie类,一个对应包括 "page" 和 "results" 的整个响应MovieResponse类。

2.1. 创建Movie

  根据JSON中对电影的描述,创建Movie类,Movie类中包含若干成员变量,与JSON中描述电影的字段一一对应。比如对应 "adult" 和 "overview" ,定义:

注意这里的成员变量名要和JSON中字段的名称一致(之后要使用Gson对JSON进行格式化)。

  当然成员变量也可以使用不同的名称,如对应 "poster_path" ,定义:

这里成员变量名称为posterPath,通过@SerializedName(“poster_path”),告诉GSON它对应了”poster_path”字段。使用 @SerializedName 需要在gradle中dependencies下添加:

并在Movie.java中导入:

  完整文件可以参考这里

2.2. 创建MovieResponse

  对应JSON中的页数和电影列表,创建MovieResponse类,MovieResponse中包含 "page" 和 "results" , "results" 是一个Movie列表:

  完整文件可以参考这里

3. 定义接口

  接下来根据TMDb的API,使用Retrofit提供的annotation定义对应的接口。如前所述,这里使用了TMDb提供的“/discover/movie”端点来获取一系列电影的信息,参考文档,我们只使用 sort_by 一个参数,如前面给出的:

  据此定义接口:

@GET 表示这是一个GET方法, "/3/discover/movie" 指明请求的端点, Call<MovieResponse> 表示返回MovieResponse, @Query("sort_by") String sortBy 表示在 "/3/discover/movie" 后面追加 "sort_by" 请求参数,参数的值由 String sortBy 指定。

  完整文件可以参考这里

4. 创建Retrofit

4.1. 添加Gson

  默认情况下,Retrofit将HTTP正文反序列化为OkHttp的ResponseBody,可以通过添加额外的转换器来获得对其他类型的支持。

  在我们的例子中,需要将JSON反序列化为之前定义的MovieResponse,这里使用Gson来解析JSON。在gradle中dependencies下添加(在2.1.中已添加过):

然后创建Gson:

4.2. 添加OkHttpClient

  在不同的平台上,Retrofit使用不同的HTTP client。在JVM上, Retrofit默认使用HttpUrlConnection,在Android上, 默认使用Apache HttpClient(Android 2.2及之前)或HttpUrlConnection HttpClient(Android 2.3及之后),也可以指定使用其他的client。

  之前在定义接口getMovies()时,并没有加入与API key对应的参数,因为API key对于每个请求都是必须的,希望通过HTTP client在请求的末尾追加API key,以简化调用。这里使用OkHttp实现这一功能。首先在gradle中dependencies下添加:

然后创建OkHttpClient:

4.2.1. 追加API key参数

  为OkHttpClient添加Interceptor:

其中 addQueryParameter("api_key", BuildConfig.API_KEY) 为查询请求追加 "api_key" 参数,参数的值为 BuildConfig.API_KEY 。这里使用了BuildConfig来获取API key,详见这里

4.2.2. 打开Log

  为了方便调试,可以打开Log:

4.3. 创建Retrofit

  最后来创建Retrofit:

这里还指定了 BASE_URL ,前面定义getMovies()时使用的端点”/3/discover/movie”就是以此为基础。

  得到Retrofit对象后,进一步创建接口:

movieService 是MovieService类型的成员变量,其他类可以使用get方法获取MovieService,并调用其中的方法。

  完整文件可以参考这里

5. 调用接口

  调用接口时,首先获取MovieService:

然后调用getMovies(),得到Call<MovieResponse>:

  到这里为止,Retrofit还没有把查询请求发给服务器,Retrofit提供同步和异步的方法来进行请求。使用同步的方法只需调用call.execute();即可,向服务器的请求一般耗时较长,为防止卡顿,在UI线程应使用异步的方法:

其中 call.enqueue() 使用回调函数的方法实现异步的请求,请求完成后,onResponse()会被调用,通过 response.body() 获取由Gson反序列化后得到的movieResponse,并进一步通过 movieResponse.getMovies() 得到Movie列表,之后更新UI。

  需要注意的是,只要请求有回应,即便请求失败,onResponse()都会被调用,上面的代码使用 isSuccess() 来确认请求是否成功(返回2XX)。

  这里也是Retrofit 1.x版本和2.0版本之间的一个显著差异,1.x版本对应同步和异步的请求,需要定义不同的接口,在接口内定义回调函数;而2.0版本无论同步还是异步,接口定义都是一样的,只在调用方法上进行区别。如果发生连接错误,如没有网络连接,则会调用onFailure()。

  完整文件可以参考这里

6. 完整代码

  完整代码可以在这里找到,使用了一个ListView来显示电影名称、发行时间和平均评分,运行效果如图2所示。

图2

图2

7. 更多

  上面的例子只展示了Retrofit的一小部分功能,更多内容还请参考官方文档Consuming APIs with Retrofit这篇文章也十分有用。值得一提的是,Retrofit还支持RxJava,可以使用Observable来代替Call,如: