1 简介
Retrofit是Square公司开发的Android里对HTTP网络请求的框架,官网是https://square.github.io/retrofit/。其底层是基于OkHttp实现的,也就是说Retrofit就对OkHttp的进一步封装。Retrofit最大的特点就是简洁易用,它使用了大量的运行时注解的方式来提供功能。
2 快速上手
假设有一服务器接口:https://api.xx.com/url.json?id=123,其请求后的返回值是
[
{
"app_name": "今日头条",
"package_name": "com.ss.android.article.news"
},
{
"app_name": "腾讯新闻",
"package_name": "com.tencent.news"
},
{
"app_name": "凤凰新闻",
"package_name": "com.ifeng.news2"
}
]
现在我们需要在代码里对其进行网络请求,并将返回结果转化成AppInfo对象。AppInfo类代码如下所示:
AppInfo.java
public class AppInfo {
private String app_name;
private String package_name;
@Override
public String toString() {
return "{app_name:" + app_name + ",package_name:" + package_name + "}";
}
}
在build.gradle中添加支持库依赖:
dependencies {
implementation 'com.squareup.retrofit2:retrofit:2.6.2'
implementation 'com.squareup.retrofit2:converter-gson:2.5.0'
}
在AndroidManifest.xml中声明网络请求权限:
<uses-permission android:name="android.permission.INTERNET"/>
于是我们使用Retrofit进行网络请求的代码可以这样:
private void getRequest() {
Retrofit retorfit = new Retrofit.Builder()
.baseUrl("https://api.xx.com/")
.addConverterFactory(GsonConverterFactory.create())
.build();
AppInfoService appInfoService = retorfit.create(AppInfoService.class);
Call<List<AppInfo>> call = appInfoService.getAppInfoList(123);
call.enqueue(new Callback<List<AppInfo>>() {
@Override
public void onResponse(Call<List<AppInfo>> call, Response<List<AppInfo>> response) {
List<AppInfo> appInfos = response.body();
Log.e("zyx", appInfos.toString());
Log.e("zyx", "当前线程:" + Thread.currentThread().getName());
}
@Override
public void onFailure(Call<List<AppInfo>> call, Throwable t) {
Log.e("zyx", t.toString());
Log.e("zyx", "当前线程:" + Thread.currentThread().getName());
}
});
}
public interface AppInfoService {
@GET("url.json")
Call<List<AppInfo>> getAppInfoList(@Query("id") int id);
}
在你的代码中执行getRequest方法,运行程序,便可见输出如下结果,而且回调还会在主线程中进行:
2019-12-04 19:27:23.209 30842-30842/com.zyx.myapplication E/zyx: [{app_name:今日头条,package_name:com.ss.android.article.news}, {app_name:腾讯新闻,package_name:com.tencent.news}, {app_name:凤凰新闻,package_name:com.ifeng.news2}]
2019-12-04 19:27:23.212 30842-30842/com.zyx.myapplication E/zyx: 当前线程:main
3 介绍
3.1 库的依赖和转换器
要使用Rerofit库,就必须在build.gradle中配置其依赖:
dependencies {
implementation 'com.squareup.retrofit2:retrofit:2.6.2'
}
上述示例中除添加Rerofit库外还添加了一个gson的库。其实gson库是可选的,默认情况下,Retrofit只能把HTTP响应结果反序列化成OkHttp的ResponseBody的类型并且只能接受它的RequsetBody的类型是@Body(后面介绍)。然而通过addConverterFactory方法可添加其它转换器来支持其他的类型。可以通过添加如下依赖包来支持其序列化操作。
- 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
当然你也可以通过自定义的方式创建自己的转换器,只要创建一个继承于Converter.Factory的类并实现responseBodyConverter 和 requestBodyConverter方法,然后使用addConverterFactory添加适配器的时候传入其一个实例就可以了。
3.2 请求方法注解
从上述示例中AppInfoService你会发现在getAppInfoList方法中就定义了一个@GET的注解,其实Rerofit里头很多是借助注解来完成。HTTP请求方法注解有8种,它们是GET、POST、PUT、DELETE、HEAD、PATCH、OPTIONS和HTTP。其中前面7种分别对应HTTP的请求方法;而最后一种HTTP 是通用注解,可以通过参数来替换以上 7 种。使用如:
@HTTP(method = "GET", path = " url.json", hasBody = false)
Call<List<AppInfo>> getAppInfoList();
method 表示请求的方法,注意它是区分大小写
path 表示网络请求地址路径
hasBody 表示是否有请求体
3.3 请求头注解
请求头注解有两种:Headers 和 Header。它们区别在于,前者一般用于固件的请求头,可添加多个;而后者一般用于不固定请求头,作为方法的参数输入,使用如:
@Headers({"Accept: application/vnd.github.v3.full+json", "User-Agent: Retrofit-Sample-App"})
@GET("url.json")
Call<List<AppInfo>> getAppInfoList(@Header("Authorization") String authorization);
3.3 参数类注解
参数类注解有:Body、Path、Field、FieldMap、Part、PartMap、Query、QueryMap 和 Url 等。
3.3.1 Body
用于Post请求发送HTTP请求体,比如已添加过GsonConverterFactory,则可将类对象传入,使用如:
@POST("users/new")
Call<User> createUser(@Body User user);
3.3.2 Path
用于Url中的占位符,使用如:
@GET("group/{id}/users")
Call<List<User>> groupList(@Path("id") int groupId);
3.3.3 Field 和 FieldMap
用于Post请求时以表单的形式传递参数,需要结合@FromUrlEncoded使用,使用如:
@FormUrlEncoded
@POST("user/edit")
Call<User> updateUser(@Field("first_name") String first, @Field("last_name") String last);
@FormUrlEncoded
@POST("user/edit")
Call<User> updateUser(@FieldMap Map<String, String> map);
3.3.4 Part 和 PartMap
用于Post请求时以表单的形式传递参数,与@Field区别于@Part可携带更加丰富的参数类型,比如用于文件上传时可携带数据流,需要结合@Multipart使用,使用如:
@Multipart
@PUT("user/photo")
Call<User> updateUser(@Part("photo") RequestBody photo, @Part("description") RequestBody description);
@Multipart
@PUT("user/photo")
Call<User> updateUser(@PartMap Map<String, RequestBody> map);
3.3.5 Query 和 QueryMap
用于Get请求时传递参数,使用如:
@GET("group/{id}/users")
Call<List<User>> groupList(@Path("id") int groupId, @Query("sort") String sort);
@GET("group/{id}/users")
Call<List<User>> groupList(@Path("id") int groupId, @QueryMap Map<String, String> options);
3.3.6 Url
用于指定请求路径,使用如:
@GET
Call<List<AppInfo>> getAppInfoList(@Url String url, @Query("id") int id);
3.4 标记类注解
标记类注解有 3 种:FormUrlEncoded、Multipart、Streaming。
3.4.1 FormUrlEncoded
用于Post请求时,请求实体是一个From表单的时,每个键值对需要使用@Field注解,使上如上面介绍Field 和 FieldMap。
3.4.2 Multipart
用于Post请求时,请求实体是丰富的参数类型,比如用于文件上传时可携带数据流,每个键值对需要使用@Part注解,使上如上面介绍Part和PartMap。
3.4.3 Streaming
一般在下载比较大的文件时,需要添加@Streaming注解,表示响应字节流的形式返回,这样就可避免大文件全部加载到内存中。
3.5 同步和异步
Call对象可以同步地或异步地进行网络请求,而且每一个对象只能使用一次,若想执行多次可以调用clone()来创建一个新对象。还要注意的是,在Android中,回调会在主线程中执行,而在JVM中,回调会在与执行HTTP请求的相同线程。
同步执行示例代码:
try {
Response<List<AppInfo>> response = call.execute();
List<AppInfo> appInfos = response.body();
Log.e("zyx", appInfos.toString());
} catch (IOException e) {
e.printStackTrace();
}
异步执行示例代码:
call.enqueue(new Callback<List<AppInfo>>() {
@Override
public void onResponse(Call<List<AppInfo>> call, Response<List<AppInfo>> response) {
List<AppInfo> appInfos = response.body();
Log.e("zyx", appInfos.toString());
}
@Override
public void onFailure(Call<List<AppInfo>> call, Throwable t) {
Log.e("zyx", t.toString());
}
});
看到上述同步和异步代码后,你是不是有似曾相识的感谢。我们之前学习OkHttp中同步和异步请求也是差不多的代码,因为Rerofit框架底层本来就是使用了OkHttp的。