Retrofit 2 簡(jiǎn)介
Retrofit是一個(gè)網(wǎng)絡(luò)訪問框架蟆盹,和OkHttp同樣出自Square公司斥季,Retrofit內(nèi)部依賴于OkHttp,但是功能上做了更多的擴(kuò)展,比如返回結(jié)果的轉(zhuǎn)換功能,可以直接對(duì)返回?cái)?shù)據(jù)進(jìn)行處理我磁。
在Android Studio中使用圃阳,先添加依賴:
compile 'com.squareup.retrofit2:retrofit:2.3.0'
compile 'com.squareup.retrofit2:converter-gson:2.3.0' //json轉(zhuǎn)換
compile 'com.squareup.retrofit2:converter-scalars:2.3.0'//String 類型轉(zhuǎn)換
使用方法
1.創(chuàng)建訪問請(qǐng)求
不同于OkHttp厌衔,Retrofit采用接口和注解的方式來設(shè)置訪問請(qǐng)求。比如訪問本機(jī)的一個(gè)文件捍岳,地址如下:
http://192.168.1.102:8080/aaa.txt
創(chuàng)建請(qǐng)求時(shí)代碼如下:
public interface ConnectService {
@GET("aaa.txt")
Call<String> getTxt();
}
GET注解表示方法為get富寿,它接收一個(gè)字符串參數(shù)(aaa.txt)作為path睬隶,并且支持占位符寫法:
public interface ConnectService {
@GET("{name}")
Call<String> getTxt(@Path("name") String name);
}
在這里,http://192.168.1.102:8080/
作為BaseUrl页徐,不需要在接口文件中定義苏潜,結(jié)尾的“/”,必須包含在BaseUrl中变勇,注解中的路徑不能以“/”開頭恤左。
Retrofit對(duì)Url的組合規(guī)則如下:
@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
創(chuàng)建請(qǐng)求時(shí)的注解,分為三類:
-
方法注解:
用來設(shè)置請(qǐng)求方法搀绣,@GET飞袋、@POST、@PUT链患、@DELETE巧鸭、@OPTIONS、@HTTP麻捻、@Headers纲仍。 除了常用的訪問方法之外,@HTTP可以設(shè)置任意方法芯肤。它包含三個(gè)參數(shù)巷折,method,path崖咨,和hasBody锻拘。@Headers用來設(shè)置請(qǐng)求頭,可以包含重復(fù)參數(shù)击蹲,都會(huì)被保留下來署拟。上文的例子使用@HTTP注解如下(添加了請(qǐng)求頭,僅供參考):
@Headers({
"Accept: application/vnd.github.v3.full+json",
"User-Agent: RetrofitBean-Sample-App",
})
@HTTP(method = "GET", path = "aaa.txt",hasBody = false)
Call<String> getTxtHttp();
-
參數(shù)注解
參數(shù)注解用來設(shè)置動(dòng)態(tài)參數(shù)歌豺,主要有@Url推穷、@Query、@QueryMap类咧、@Path馒铃、@Header,@Body痕惋、@Field区宇、@FieldMap、@Part值戳,@PartMap议谷。
首先看一下@Path和@Header,@Path用來設(shè)置路徑堕虹,輸入的內(nèi)容替換方法注解中的占位符卧晓,上文已經(jīng)展示過用法了芬首。@Header用來動(dòng)態(tài)設(shè)置請(qǐng)求頭:
@GET()
Call<String> setHeader(@Header("Accept") String acceptType);
@Query、@QueryMap用來設(shè)置請(qǐng)求參數(shù):
/\*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則用在非統(tǒng)一Url的情況下逼裆,可以接收參數(shù)作為Url進(jìn)行網(wǎng)絡(luò)訪問郁稍。
其余的幾個(gè)注解@Body、@Field波附、@FieldMap艺晴、@Part,@PartMap掸屡,用在Post方法中設(shè)置參數(shù)封寞,下文會(huì)說明
-
標(biāo)記注解
包括@FormUrlEncoded、@Multipart
@FormUrlEncoded表示Post方法提交的是鍵值對(duì)數(shù)據(jù)仅财,對(duì)應(yīng)content-type=application/x-www-form-urlencoded狈究。提交的內(nèi)容由參數(shù)注解@Field、@FieldMap來設(shè)置盏求。
@FormUrlEncoded
@POST("user/edit")
//@Field逐一設(shè)置
Call<User> updateUser(@Field("first_name") String first, @Field("last_name") String last);
//@FieldMap統(tǒng)一設(shè)置
@FormUrlEncoded
@POST("user/edit")
Call<User> updateUser(@FieldMap Map<String,String> fieldMap);
@Multipart表示Post方法對(duì)應(yīng)Content-Type: multipart/form-data抖锥,提交表單數(shù)據(jù)。對(duì)應(yīng)的參數(shù)注解為@Part碎罚,@PartMap磅废。
除此之外還有json數(shù)據(jù),對(duì)應(yīng)參數(shù)注解@Body荆烈。
2.訪問網(wǎng)絡(luò)
請(qǐng)求部分設(shè)置完成后拯勉,就可以進(jìn)行網(wǎng)絡(luò)訪問了,使用方法類似于OkHttp:
//構(gòu)建Retrofit對(duì)象憔购,相當(dāng)于OkHttpClient
Retrofit retrofit = new Retrofit.Builder()
//設(shè)置OKHttpClient,如果不設(shè)置會(huì)提供一個(gè)默認(rèn)的
.client(new OkHttpClient())
//設(shè)置baseUrl
.baseUrl("http://192.168.1.102:8080/")
//添加字符串轉(zhuǎn)換器
.addConverterFactory(ScalarsConverterFactory.create())
.build();
//創(chuàng)建網(wǎng)絡(luò)訪問對(duì)象
ConnectService cs = retrofit.create(ConnectService.class);
//調(diào)用網(wǎng)絡(luò)訪問對(duì)象的方法宫峦,得到Call對(duì)象
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一樣导绷,調(diào)用call.enqueue方法。需要注意的是屎飘,在Retrofit 2.0中異步訪問的方式妥曲,onResponse
總是會(huì)被調(diào)用。如果response不能被解析钦购, response.body()
返回null檐盟,其他的比如連接錯(cuò)誤404等,也會(huì)調(diào)用onResponse肮雨,此時(shí)response.errorBody().string()
可以獲取錯(cuò)誤信息。
3.使用攔截器
Retrofit是依賴于OkHttp的箱玷,使用攔截器的時(shí)候仍然依賴于OkHttpClient怨规,需要先構(gòu)建一個(gè)包含攔截器的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()
//設(shè)置OKHttpClient,如果不設(shè)置會(huì)提供一個(gè)默認(rèn)的
.client(okHttpClient )
.baseUrl("http://192.168.1.102:8080/")
.addConverterFactory(ScalarsConverterFactory.create())
.build();
處理Response
上面的例子中添加的是字符串的轉(zhuǎn)換器,得到的response.body()是簡(jiǎn)單的字符串〔ǚ幔現(xiàn)在我們看一下Json是怎樣轉(zhuǎn)換的壳坪。
這里我們?cè)L問的網(wǎng)址是 https://cdn.heweather.com/china-city-list.json 返回的是中國(guó)城市列表。
先用GsonFormat工具建立JavaBean文件CityEntity:
public class CityEntity {
/**
* id : CN101010100
* cityEn : beijing
* cityZh : 北京
* countryCode : CN
* countryEn : China
* countryZh : 中國(guó)
* 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();
}
開始網(wǎng)絡(luò)訪問:
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) {
}
});
關(guān)于Converter掰烟,Retrofit已經(jīng)提供了Gson爽蝴,Scalars等等。當(dāng)然也可以自己定義Converter纫骑,是繼承自Converter.Factory的蝎亚。