Retrofit 2 簡介
Retrofit是一個網(wǎng)絡(luò)訪問框架,和OkHttp同樣出自Square公司,Retrofit內(nèi)部依賴于OkHttp,但是功能上做了更多的擴展,比如返回結(jié)果的轉(zhuǎn)換功能,可以直接對返回數(shù)據(jù)進行處理。
在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)建訪問請求
不同于OkHttp,Retrofit采用接口和注解的方式來設(shè)置訪問請求。比如訪問本機的一個文件,地址如下:
http://192.168.1.102:8080/aaa.txt
創(chuàng)建請求時代碼如下:
public interface ConnectService {
@GET("aaa.txt")
Call<String> getTxt();
}
GET注解表示方法為get,它接收一個字符串參數(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對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)建請求時的注解,分為三類:
-
方法注解:
用來設(shè)置請求方法,@GET、@POST、@PUT、@DELETE、@OPTIONS、@HTTP、@Headers。 除了常用的訪問方法之外,@HTTP可以設(shè)置任意方法。它包含三個參數(shù),method,path,和hasBody。@Headers用來設(shè)置請求頭,可以包含重復(fù)參數(shù),都會被保留下來。上文的例子使用@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();
-
參數(shù)注解
參數(shù)注解用來設(shè)置動態(tài)參數(shù),主要有@Url、@Query、@QueryMap、@Path、@Header,@Body、@Field、@FieldMap、@Part,@PartMap。
首先看一下@Path和@Header,@Path用來設(shè)置路徑,輸入的內(nèi)容替換方法注解中的占位符,上文已經(jīng)展示過用法了。@Header用來動態(tài)設(shè)置請求頭:
@GET()
Call<String> setHeader(@Header("Accept") String acceptType);
@Query、@QueryMap用來設(shè)置請求參數(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進行網(wǎng)絡(luò)訪問。
其余的幾個注解@Body、@Field、@FieldMap、@Part,@PartMap,用在Post方法中設(shè)置參數(shù),下文會說明
-
標記注解
包括@FormUrlEncoded、@Multipart
@FormUrlEncoded表示Post方法提交的是鍵值對數(shù)據(jù),對應(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方法對應(yīng)Content-Type: multipart/form-data,提交表單數(shù)據(jù)。對應(yīng)的參數(shù)注解為@Part,@PartMap。
除此之外還有json數(shù)據(jù),對應(yīng)參數(shù)注解@Body。
2.訪問網(wǎng)絡(luò)
請求部分設(shè)置完成后,就可以進行網(wǎng)絡(luò)訪問了,使用方法類似于OkHttp:
//構(gòu)建Retrofit對象,相當于OkHttpClient
Retrofit retrofit = new Retrofit.Builder()
//設(shè)置OKHttpClient,如果不設(shè)置會提供一個默認的
.client(new OkHttpClient())
//設(shè)置baseUrl
.baseUrl("http://192.168.1.102:8080/")
//添加字符串轉(zhuǎn)換器
.addConverterFactory(ScalarsConverterFactory.create())
.build();
//創(chuàng)建網(wǎng)絡(luò)訪問對象
ConnectService cs = retrofit.create(ConnectService.class);
//調(diào)用網(wǎng)絡(luò)訪問對象的方法,得到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一樣,調(diào)用call.enqueue方法。需要注意的是,在Retrofit 2.0中異步訪問的方式,onResponse總是會被調(diào)用。如果response不能被解析, response.body()返回null,其他的比如連接錯誤404等,也會調(diào)用onResponse,此時response.errorBody().string()可以獲取錯誤信息。
3.使用攔截器
Retrofit是依賴于OkHttp的,使用攔截器的時候仍然依賴于OkHttpClient,需要先構(gòu)建一個包含攔截器的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è)置會提供一個默認的
.client(okHttpClient )
.baseUrl("http://192.168.1.102:8080/")
.addConverterFactory(ScalarsConverterFactory.create())
.build();
處理Response
上面的例子中添加的是字符串的轉(zhuǎn)換器,得到的response.body()是簡單的字符串?,F(xiàn)在我們看一下Json是怎樣轉(zhuǎn)換的。
這里我們訪問的網(wǎng)址是 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();
}
開始網(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等等。當然也可以自己定義Converter,是繼承自Converter.Factory的。