Android开源网络框架Retrofit(入门篇)

96
Mr云台
2016.12.12 19:06* 字数 1564

Restful风格接口基本成了业界主流,Retrofit框架也大火特火,最近项目中也决定更新换代,采用Retrofit。本着学习之余,也对接下来的学习者有一些帮助,于是写了本文,主要的内容是对官网内容的一个翻译和补充解释
plus:本文假设你对于基本的HTTP协议有所了解。

一、Retrofit简介

Retrofit内部使用OKhttp来进行网络请求, 会把网络请求转化为一个java接口,使用了编译阶段的注解提高开发效率。如下,在开发中把网络请求定义在一个专门的java接口中

public interface GitHubService {
  @GET("users/{user}/repos")
  Call<List<Repo>> listRepos(@Path("user") String user);
}

通过Retrofit为此接口生成一个实现,代码如下

//定制OKHttpClient
OkHttpClient okHttpClient = new OkHttpClient.Builder()
                .readTimeout(DEFAULT_TIMEOUT_MS, TimeUnit.MILLISECONDS)
                .connectTimeout(DEFAULT_TIMEOUT_MS, TimeUnit.MILLISECONDS)
                .writeTimeout(DEFAULT_TIMEOUT_MS, TimeUnit.MILLISECONDS)
                .build();

//实例化 Retrofit
Retrofit retrofit = new Retrofit.Builder()
    .baseUrl("https://api.github.com/")
     .client(okHttpClient)
    .build();


//生成实现类
GitHubService service = retrofit.create(GitHubService.class);

通过生成的GitHubService,开发者可以使用同步或者异步的方式去请求网络接口。

Call<List<Repo>> repos = service.listRepos("octocat");

Retrofit 使用注解来描述HTTP接口,提升开发效率,它有如下特性:

  • URL参数动态替换、动态地址栏参数。
  • 对象能动态转化成RequestBody,比如 JSON和protocol buffers。
  • 支持Multipart RequestBody(对这个不了解的,看下这篇博文
  • 支持文件上传。

上述特性,具体看下面的章节就明白了。

二、Retrofit的注解用法详解

1. 网络接口的声明

在Retrofit中,每个网络接口都必须有一个注解,网络接口上的注解表明了类型、Url访问路径。这里有五个内建的注解: GET, POST, PUT, DELETE, and HEAD。如下示例,声明了一个简单的GET网络接口,其相对路径是users/list。

@GET("users/list")

跟传统一样,你可以在路径中加上参数。

@GET("users/list?sort=desc")

2. URL相关的注解

相对路径中可以声明动态参数,用 {str1} 代码 表示,在接口里面通过 @Path("str1") 修饰相应用于动态替换的参数。

具体用法如下展示,其中groupId的值会动态替换掉访问路径中的{id}。

@GET("group/{id}/users")
Call<List<User>> groupList(@Path("id") int groupId);

地址栏参数也是可以使用 @Query("str")加上,效果就是在访问URL后面动态加上 @Query("str")标识的参数

如下方法,最终的URL会是这样:group/{id}/users/?sort=xxx

@GET("group/{id}/users")
Call<List<User>> groupList(@Path("id") int groupId, @Query("sort") String sort);

如果是想加上很多个地址栏参数,Retrofit提供了map的方式,map里面的多个键值对会依次加入到URL后边。

@GET("group/{id}/users")
Call<List<User>> groupList(@Path("id") int groupId, @QueryMap Map<String, String> options);

3. Request Body 相关注解

上文已经提到,对象能动态转化成RequestBody,具体是要在对象前使用 @Body注解。如下代码所示,User对象会被转化成Body。

@POST("users/new")
Call<User> createUser(@Body User user);

The object will also be converted using a converter specified on the Retrofit instance. If no converter is added, only RequestBody can be used.

对象转化为Body的方式是Retrofit默认提供的一个转化器,你也可以自己创建一个转换器,在Retrofit实例化的时候进行指定。

4. 表单和使用Multipart Body

Retrofit使用注解 @FormUrlEncoded 来表示使用表单,表单里面相应的字段在接口参数前使用 @Field("XXX")标识,括号里面是key值。

@FormUrlEncoded
@POST("user/edit")
Call<User> updateUser(@Field("first_name") String first, @Field("last_name") String last);

Multipart Body直接使用 @Multipart 在接口方法上标识. Body中的Parts 直接使用 @Part 标识。

@Multipart
@PUT("user/photo")
Call<User> updateUser(@Part("photo") RequestBody photo, @Part("description") RequestBody description);

同上文所说,Multipart parts也采用了Retrofit默认的序列化转换器,如果你希望自己定义序列化,可以继承RequestBody类,重写序列化方法。

5. Retrofit设置请求头的相关注解

你可以为一些方法设定请求头字段,这个需使用 @Headers 注解

@Headers("Cache-Control: max-age=640000")
@GET("widget/list")
Call<List<Widget>> widgetList();
@Headers({
    "Accept: application/vnd.github.v3.full+json",
    "User-Agent: Retrofit-Sample-App"
})
@GET("users/{username}")
Call<User> getUser(@Path("username") String username);

请求头字段也可以被动态改变,在接口参数前加上 @Header就可以了。

额外说明:如果该参数传入为null,那么请求头的该字段会被删除。该字段的参数类型可以不是String类型,Retrofit最终会调用该参数的toString()方法,得到的值作为请求头该字段的值。

@GET("user")
Call<User> getUser(@Header("Authorization") String authorization)

一路看下来,可能有读者会问了,难道我需要为每个接口方法都设置一遍请求头吗?

不是的,对于所有接口都要用到的请求头,可以通过 Okhttp拦截器统一设置。

三、Retrofit 的配置

通过上述描述,大家应该已经发现,Retrofit提供的功能就是把网络请求变成一个可调用的实例对象,这个对象里面是Restful风格的各个网络请求接口。Retrofit提供了一些好的注解,帮我们减轻了封装网络模块的工作。

Retrofit默认会为我们的平台提供合适的,健壮的默认配置,但是这些配置我们也是可以自己定制的。

默认情况下,Retrofit 只能 把 HTTP Body类型 反序列化成 OKhttp的 ResponseBody类型,而且接口里面默认接受的是 RequestBody类型,这个上文已经讲过。

Retrofit支持配置其他的转换器,来支持其他的类型,官网提供了如下6个可动态配置的libraries,需要哪个你就在Gradle里面配置进来。

  • Gson: com.squareup.retrofit2:converter-gson
  • Jackson: com.squareup.retrofit2:converter-jackson
  • Moshi: com.squareup.retrofit2:converter-moshi
  • Protobuf: com.squareup.retrofit2:converter-protobuf
  • Wire: com.squareup.retrofit2:converter-wire
  • Simple XML: com.squareup.retrofit2:converter-simplexml
  • Scalars (primitives, boxed, and String): com.squareup.retrofit2:converter-scalars

下面是官网的例子,展示了如何使用其他的转换器,这个只需要在创建Retrofit对象的时候动态传入转换器就可以了。下面例子使用了Gson作为反序列化工具。

Retrofit retrofit = new Retrofit.Builder()
    .baseUrl("https://api.github.com")
    .addConverterFactory(GsonConverterFactory.create())
    .build();

GitHubService service = retrofit.create(GitHubService.class);

如果官网提供的转换器无法满足你,你可以自己继承 Converter.Factory类,扩展你需要的功能

四、使用Retrofit

1. GRADLE配置

compile 'com.squareup.retrofit2:retrofit:2.1.0'

注意:Retrofit仅支持 Java 7 及 Android 2.3 以上版本。

2. PROGUARD配置

如果你的项目使用了混淆,请将如下代码添加到proguards-rules 文件夹当中。

# Platform calls Class.forName on types which do not exist on Android to determine platform.
-dontnote retrofit2.Platform
# Platform used when running on RoboVM on iOS. Will not be used at runtime.
-dontnote retrofit2.Platform$IOS$MainThreadExecutor
# Platform used when running on Java 8 VMs. Will not be used at runtime.
-dontwarn retrofit2.Platform$Java8
# Retain generic type information for use by reflection by converters and adapters.
-keepattributes Signature
# Retain declared checked exceptions for use by a Proxy instance.
-keepattributes Exceptions

Android技术
Web note ad 1