盒子
盒子
文章目录
  1. Retrofit
    1. 配置
  2. 使用
    1. GET
    2. Path
    3. Query
    4. QueryMap
    5. 示例
    6. POST
    7. FormUrlEncoded And Multipart
      1. Field And FiledMap
      2. Part And PartMap
    8. Headers And Header
    9. 示例

带你走进Retrofit的新世界

对于Android来说网络请求的实现有很多,例如可以直接使用没有封装的HttpUrlConnection,或者使用一些已经进行了封装的例如VolleyOkHttp,这些封装好的开源库也是不错的,如果要讲的话也可以单独写好几篇文章,如果需要了解Volley原理的的可以查看这两篇文章Android Volley源码分析(一)Android Volley源码分析(二)。但是今天要说的是现在Android开发人员进阶必备的网络库Retrofit,也是现在最流行的,如果你之前使用过OkHttp那么恭喜你有了一个好的优势,因为Retrofit内部就是使用OkHttp进行请求的。Retrofit做的只是封装请求参数、HeaderUrl等信息,最后再传给OkHttp完成请求,请求完之后OkHttp在传回给Retrofit。下面来说下Retrofit的基本使用。

本章介绍的都是入门的知识,只做一个引导作用,大神请自动跳过

Retrofit

对于Retrofit的介绍我引用官方的一句话Type-safe HTTP client for Android and Java by Square, Inc.简单的翻译一下就是:对于AndroidJava是一个类型安全的Http请求库。它是使用注释来描述Http请求,主要有以下特点:

  • 支持URL参数替代与query参数的查询
  • 对于请求body类型的转换(例如:Jsonprotocol buffers
  • 支持Multipart请求与文件上传

配置

现在已经更新到Retrofit2了,对于Android来说直接在Gradle中配置:

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

或者查看官网配置最新版本,Retrofit需要的Android最低版本为2.3
对于Retrofit2就不需要配置OkHttp依赖了,因为它默认会一起配置,在使用Retrofit时,会使用一些类型的转换,提供了以下6中流行的类型供选择

  • 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

    Gradle中配置与Retrofit对于的版本号

    1
    compile 'com.squareup.retrofit2:converter-gson:2.1.0'

使用

前面说了Retrofit是使用注释来定义请求,且它定义的注释是在接口方法中,所以每一个方法都必须要有相应的Http注释与请求的URLRetrofit支持5中内置的注释的Http请求方式:GETPOSTPUTDELETEHEAD。下面来看下简单的定义。

定义的URL包括两部分一部分为host也称为baseUrl例如:https://api.github.com,别一部分为path例如:users/list,我们在注释中定义的url一般为path部分。

以下的介绍都是针对Retrofit2来进行的,与Retrofit1.9有的地方还是有区别。

GET

1
@GET("users/list")

也可以直接带参数

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

Path

其实也可以使用动态更新的方式来定义path,再path中通过使用{}来定义需要动态替代的字段,再到方法中使用@Path()来定义替代的变量值

1
2
@GET("group/{address}/users")
Call<List<User>> groupList(@Path("address") String address);

@Path还有一个可选的字段encoded默认为false,如果为true将会对特殊的字符进行编码转换,例如keyaddressvaluemaer-se,默认编码的urlgroup/maer-se/users,但如果对encoded设置为true将会对-字符进行转换最终变成了group/maer%2Dse/users,使用如下:

1
2
@GET("group/{address}/users")
Call<List<User>> groupList(@Path(value = "address", encoded = true) String address);

后续的QueryQueryMapPartPartMapFieldFieldMap都有这个特性

Query

参数查询也可以通过使用@Query()来定义

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

QueryMap

如果参数过多可以借助@QueryMap 来构造Map

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

同时如果在调用.groupList时,只要参数的类型不为基本类型(intdouble等),或者将基本型转换为Integer,Double等时可以为null,如果为null那么Retrofit将会忽略不会匹配该参数。另外在使用QueryMap时,可以动态匹配你需要的参数,没有提供的参数将会自动忽略,例如:

1
2
3
4
Map<String, String> data = new HashMap<>();
data.put("user", "Marcus");
Call<List<User>> call = newsService.groupList(2,data);
call.enqueue(…)

这个请求的urlhttps://api.github.com/group/2/users?user=Marcus对于address将自动忽略。

说了这么多还是以一个示例来展示整个调用流程,下面的示例是Github上的

示例

定义接口

1
2
3
4
5
6
public interface GithubService {
@GET("/repos/{owner}/{repo}/contributors")
Call<List<Contributors>> contributors(
@Path("owner") String owner,
@Path("repo") String repo);
}

Contributors是一个实体类,对应于请求之后返回的数据类型

1
2
3
4
5
6
7
8
9
public class Contributors {
public String login;
public String contributions;
public Contributors(String contributions, String login) {
this.contributions = contributions;
this.login = login;
}
}

通过Retrofit生成你定义的接口,其中响应的数据是Json类型,所以要对body做相应的转化,通过添加Converter,如果没有添加Converter它默认是使用OkHttpResponseBodyResponseBody对于后续POST设置的@Body

1
2
3
4
5
6
Retrofit retrofit = new Retrofit.Builder()
.baseUrl("https://api.github.com")
.addConverterFactory(GsonConverterFactory.create())
.build();
GithubService service = retrofit.create(GithubService.class)

使用GsonConverterFactory.create()必须在Gradle中配置了前面所说的6种类型中的Gson

每一个Call都是来自于创建的接口通过同步或者异步请求远程服务而获得。在调用接口的方法时可以传入对应的参数数据

1
Call<List<Contributors>> call = service.contributors("square", "retrofit");

最后同调用Callexecute执行同步请求,或者调用Callenqueue来执行异步请求,获取响应的结果

1
2
3
4
5
6
7
8
try {
List<Contributors> contributorses = call.execute().body();
for (Contributors contributors : contributorses) {
Log.v("TAG", contributors.login + "(" + contributors.contributions + ")");
}
} catch (IOException e) {
e.printStackTrace();
}

POST

对于一个对象可能会作为Http请求的body。这时可以使用@Body注释

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

这个对象也可以使用Retrofitconverter来进行特殊的转化,如果没有添加converter它将默认使用RequestBody

FormUrlEncoded And Multipart

对于接口定义的方法也可以发送form-encodedmultipart数据类型。

Field And FiledMap

对于form-encoded的数据类型,一般Content-typex-www-form-urlencoded可以使用@FormUrlEncoded来对方法进行注释说明,每一对的key-value可以使用@Field来进行定义,分别提供keyvalue。对于多对数据可以使用@FieldMap进行统一设置,使用与@QueryMap类似。

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

如果有多个相同的字段,可以通过数组来定义字段的值

1
2
3
@FormUrlEncoded
@POST("tasks")
Call<List<Task>> createTasks(@Field("title") List<String> titles);

FromUrlEncodedQuery Parameter的主要区别是,前者是POST后者是GET,同时前者的请求数据是放在request body中,而后者是放在url中。

Part And PartMap

需要Multipart请求时即上传文件时,一般Content-typemultipart/form-data,只要在方法上面使用@Multipart注释,同时对于Parts部分的定义可以使用@Part注释,相应的@PartMap也存在

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

Multipart默认也是使用RequestBody操作它的序列化,或者也可以使用converter来实现。

Headers And Header

@Headers注释是为方法添加静态的headers,且相同的name不会被覆盖,都会包含到请求中;而@Header注释是为方法添加动态的header,它将会相当与参数来对@Header进行提供,如果这个参数的值为null,那么它将会被忽略。

1
2
3
4
@Headers({"Connection: keep-alive",
"Accept-Language:zh-CN,zh;q=0.8,en;q=0.6"})
@GET("/settings")
Call<String> settingService(@Header("Cookie") String cookie);

如果是最新版本的Retrofit2.1,也支持@HeaderMap

示例

再来贴一个多说文章请求的完整示例,先来看下需要请求示例资源

General

Query String Parameters

相信应该很清楚了,请求方式为GET,请求的Urlhttp://idisfkj.duoshuo.com/api/threads/list.json?short_name=idisfkj&order=desc&limit=30&nonce=57f1b19209428,请求查询的参数为short_nameorderlimitnonceOk需要知道的都有了,下面来code

1
2
3
4
5
6
7
8
9
10
11
public interface DuoShuoArticleService {
@GET("/api/threads/list.json")
Call<DuoShuoArticle> articleService(@Query("short_name") String shortName,
@Query("order") String order,
@Query("limit") int limit,
@Query("nonce") String onnce);
@GET("/api/threads/list.json")
Call<DuoShuoArticle> articleServiceMap(@Query("limit") int limit,
@QueryMap Map<String, String> options);
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
/**
* get 对url的传参查询发起请求 返回json类型的response
*/
public void getDuoShuoArticle() {
Retrofit retrofit = new Retrofit.Builder()
.baseUrl("http://idisfkj.duoshuo.com")
.addConverterFactory(GsonConverterFactory.create())
.build();
DuoShuoArticleService articleService = retrofit.create(DuoShuoArticleService.class);
Call<DuoShuoArticle> call = articleService.articleService("idisfkj", "desc", 5, "57f1b19209428");
//使用QueryMap
// Map<String,String> map = new HashMap<String, String>();
// map.put("short_name","idisfkj");
// map.put("order","desc");
// map.put("nonce","57f1b19209428");
// Call<DuoShuoArticle> call = articleService.articleServiceMap(5,map);
try {
Response<DuoShuoArticle> response = call.execute();
List<DuoShuoArticle.ResponseBean> list = response.body().getResponse();
for (DuoShuoArticle.ResponseBean responseBean : list) {
Log.v("TAG", responseBean.getThread_key() + "->" + responseBean.getTitle());
}
} catch (IOException e) {
e.printStackTrace();
}
}
1
2
3
4
5
11-16 02:09:43.989 2061-2079/? V/TAG: duoshuo->带你走进Retrofit的新世界
11-16 02:09:43.989 2061-2079/? V/TAG: duoshuo->Android 自定义View
11-16 02:09:43.989 2061-2079/? V/TAG: duoshuo->Android Handler、Looper与MessageQueue源码分析
11-16 02:09:43.989 2061-2079/? V/TAG: duoshuo->Android Volley源码分析(二)
11-16 02:09:43.989 2061-2079/? V/TAG: duoshuo->Android Volley源码分析(一)

如果你看到这里,那么恭喜你对Retrofit也入门了,后续我还会继续探究与大家进行分享。

支持一下
赞赏是一门艺术