Retrofit 2 简介
Retrofit是一个网络访问框架,和OkHttp同样出自Square公司,Retrofit内部依赖于OkHttp,但是功能上做了更多的扩展,比如返回结果的转换功能,可以直接对返回数据进行处理。
在Android Studio中使用,先添加依赖:
compile 'com.squareup.retrofit2:retrofit:2.3.0'
compile 'com.squareup.retrofit2:converter-gson:2.3.0' //json转换
compile 'com.squareup.retrofit2:converter-scalars:2.3.0'//String 类型转换
使用方法
1.创建访问请求
不同于OkHttp,Retrofit采用接口和注解的方式来设置访问请求。比如访问本机的一个文件,地址如下:
http://192.168.1.102:8080/aaa.txt
创建请求时代码如下:
public interface ConnectService {
@GET("aaa.txt")
Call<String> getTxt();
}
GET注解表示方法为get,它接收一个字符串参数(aaa.txt)作为path,并且支持占位符写法:
public interface ConnectService {
@GET("{name}")
Call<String> getTxt(@Path("name") String name);
}
在这里,http://192.168.1.102:8080/
作为BaseUrl,不需要在接口文件中定义,结尾的“/”,必须包含在BaseUrl中,注解中的路径不能以“/”开头。
Retrofit对Url的组合规则如下:
@GET("user1") + baseUrl("https://www.baidu.com/image/list/") = https://www.baidu.com/image/list/user1
@GET("user1") + baseUrl("https://www.baidu.com/image/list") = https://www.baidu.com/image/user1
@GET("/user1") + baseUrl("https://www.baidu.com/image/list") = https://www.baidu.com/user1
创建请求时的注解,分为三类:
-
方法注解:
用来设置请求方法,@GET、@POST、@PUT、@DELETE、@OPTIONS、@HTTP、@Headers。 除了常用的访问方法之外,@HTTP可以设置任意方法。它包含三个参数,method,path,和hasBody。@Headers用来设置请求头,可以包含重复参数,都会被保留下来。上文的例子使用@HTTP注解如下(添加了请求头,仅供参考):
@Headers({
"Accept: application/vnd.github.v3.full+json",
"User-Agent: RetrofitBean-Sample-App",
})
@HTTP(method = "GET", path = "aaa.txt",hasBody = false)
Call<String> getTxtHttp();
-
参数注解
参数注解用来设置动态参数,主要有@Url、@Query、@QueryMap、@Path、@Header,@Body、@Field、@FieldMap、@Part,@PartMap。
首先看一下@Path和@Header,@Path用来设置路径,输入的内容替换方法注解中的占位符,上文已经展示过用法了。@Header用来动态设置请求头:
@GET()
Call<String> setHeader(@Header("Accept") String acceptType);
@Query、@QueryMap用来设置请求参数:
/\*https://api.heweather.com/x3/weather?cityid=CN101010300&key=035591c2b7*/
//使用@Query注解
@GET("{version}/weather")
Call<String> getWeather(@Path("version") String version, @Query("cityid") String id, @Query("key") String key);
//使用QueryMap注解
@GET("{version}/weather")
Call<String> getWeatherQueryMap(@Path("version") String version, @QueryMap Map<String, String> params);
@Url则用在非统一Url的情况下,可以接收参数作为Url进行网络访问。
其余的几个注解@Body、@Field、@FieldMap、@Part,@PartMap,用在Post方法中设置参数,下文会说明
-
标记注解
包括@FormUrlEncoded、@Multipart
@FormUrlEncoded表示Post方法提交的是键值对数据,对应content-type=application/x-www-form-urlencoded。提交的内容由参数注解@Field、@FieldMap来设置。
@FormUrlEncoded
@POST("user/edit")
//@Field逐一设置
Call<User> updateUser(@Field("first_name") String first, @Field("last_name") String last);
//@FieldMap统一设置
@FormUrlEncoded
@POST("user/edit")
Call<User> updateUser(@FieldMap Map<String,String> fieldMap);
@Multipart表示Post方法对应Content-Type: multipart/form-data,提交表单数据。对应的参数注解为@Part,@PartMap。
除此之外还有json数据,对应参数注解@Body。
2.访问网络
请求部分设置完成后,就可以进行网络访问了,使用方法类似于OkHttp:
//构建Retrofit对象,相当于OkHttpClient
Retrofit retrofit = new Retrofit.Builder()
//设置OKHttpClient,如果不设置会提供一个默认的
.client(new OkHttpClient())
//设置baseUrl
.baseUrl("http://192.168.1.102:8080/")
//添加字符串转换器
.addConverterFactory(ScalarsConverterFactory.create())
.build();
//创建网络访问对象
ConnectService cs = retrofit.create(ConnectService.class);
//调用网络访问对象的方法,得到Call对象
final Call<String> myCall = cs.getTxtHttp();
//final Call<String> myCall = cs.getTxt("aaa.txt");
//final Call<String> myCall = cs.getUrl("aaa.txt");
new Thread(new Runnable() {
@Override
public void run() {
try {
Response<String> response = myCall.execute();
String result = response.body().toString();
InputStream is = response.body().s
Log.d("retrofit", "同步返回: " + result);
} catch (IOException e) {
e.printStackTrace();
}
}
}).start();
/*
myCall.clone();
//异步方式
myCall.enqueue(new Callback<String>() {
@Override
public void onResponse(Call<String> call, Response<String> response) {
String result = response.body().toString();
Log.d("retrofit", "异步返回: " + result);
}
@Override
public void onFailure(Call<String> call, Throwable t) {
}
});*/
这里采用的是同步访问的方式,如果是异步,和okHttp一样,调用call.enqueue方法。需要注意的是,在Retrofit 2.0中异步访问的方式,onResponse
总是会被调用。如果response不能被解析, response.body()
返回null,其他的比如连接错误404等,也会调用onResponse,此时response.errorBody().string()
可以获取错误信息。
3.使用拦截器
Retrofit是依赖于OkHttp的,使用拦截器的时候仍然依赖于OkHttpClient,需要先构建一个包含拦截器的OkHttpClient,然后传入到Retrofit中:
class MyInterceptor implements Interceptor{
@Override
public Response intercept(Chain chain) throws IOException {
Request request = chain.request();
Response response = chain.proceed(request);
return response;
}
OkHttpClient okHttpClient = new OkHttpClient()
.newBuilder()
.addInterceptor(new MyInterceptor() )
.build();
Retrofit retrofit = new Retrofit.Builder()
//设置OKHttpClient,如果不设置会提供一个默认的
.client(okHttpClient )
.baseUrl("http://192.168.1.102:8080/")
.addConverterFactory(ScalarsConverterFactory.create())
.build();
处理Response
上面的例子中添加的是字符串的转换器,得到的response.body()是简单的字符串。现在我们看一下Json是怎样转换的。
这里我们访问的网址是 https://cdn.heweather.com/china-city-list.json 返回的是中国城市列表。
先用GsonFormat工具建立JavaBean文件CityEntity:
public class CityEntity {
/**
* id : CN101010100
* cityEn : beijing
* cityZh : 北京
* countryCode : CN
* countryEn : China
* countryZh : 中国
* provinceEn : beijing
* provinceZh : 北京
* leaderEn : beijing
* leaderZh : 北京
* lat : 39.904989
* lon : 116.405285
*/
private String id;
private String cityEn;
private String cityZh;
private String countryCode;
private String countryEn;
private String countryZh;
private String provinceEn;
private String provinceZh;
private String leaderEn;
private String leaderZh;
private String lat;
private String lon;
public String getId() {
return
public void setId(String id) {
this.id = id;
}
public String getCityEn() {
return cityEn;
}
public void setCityEn(String cityEn) {
this.cityEn = cityEn;
}
public String getCityZh() {
return cityZh;
}
public void setCityZh(String cityZh) {
this.cityZh = cityZh;
}
public String getCountryCode() {
return countryCode;
}
public void setCountryCode(String countryCode) {
this.countryCode = countryCode;
}
public String getCountryEn() {
return countryEn;
}
public void setCountryEn(String countryEn) {
this.countryEn =
public String getCountryZh() {
return countryZh;
}
public void setCountryZh(String countryZh) {
this.countryZh = countryZh;
}
public String getProvinceEn() {
return provinceEn;
}
public void setProvinceEn(String provinceEn) {
this.provinceEn = provinceEn;
}
public String getProvinceZh() {
return provinceZh;
}
public void setProvinceZh(String provinceZh) {
this.provinceZh = provinceZh;
}
public String getLeaderEn() {
return
public void setLeaderEn(String leaderEn) {
this.leaderEn = leaderEn;
}
public String getLeaderZh() {
return leaderZh;
}
public void setLeaderZh(String leaderZh) {
this.leaderZh = leaderZh;
}
public String getLat() {
return lat;
}
public void setLat(String lat) {
this.lat = lat;
}
public String getLon() {
return lon;
}
public void setLon(String lon) {
this.lon = lon;
}
}
然后定义访问接口API:
public interface ConnectService {
@GET("china-city-list.json")
Call<List<com.cris.miniweather.model.CityEntity>> getCityList();
}
开始网络访问:
Retrofit retrofit = new Retrofit.Builder()
.baseUrl("https://cdn.heweather.com/")
.addConverterFactory(GsonConverterFactory.create())
.build();
ConnectService cs = retrofit.create(ConnectService.class);
final Call<List<com.cris.miniweather.model.CityEntity>> cityListCall = cs.getCityList();
cityListCall.enqueue(new Callback<List<CityEntity>>(){
@Override
public void onResponse(Call<List<CityEntity>> call, Response<List<CityEntity>> response) {
List<CityEntity> cityList = response.body();
for (CityEntity cityEntity:cityList ){
Log.d("retrofit","City name is: " + cityEntity.getCityZh());
}
}
@Override
public void onFailure(Call<List<CityEntity>> call, Throwable t) {
}
});
关于Converter,Retrofit已经提供了Gson,Scalars等等。当然也可以自己定义Converter,是继承自Converter.Factory的。