什么是Retrofit2
Retrofit是一個給Android和Java用的類型安全的HTTP客戶端俐东,它將網(wǎng)絡(luò)請求封裝成接口虏辫,并采用注解的形式聲明請求锈拨,由Retrofit自動生成接口實現(xiàn)對象給開發(fā)者調(diào)用。
Retofit2的入門
- 引用Retrofit2
Gradle引用
compile 'com.squareup.retrofit2:retrofit:2.1.0'
Maven引用
<dependency>
<groupId>com.squareup.retrofit2</groupId>
<artifactId>retrofit</artifactId>
<version>2.1.0</version>
</dependency>
- Retrofit2入門
接下來我們一步一步的學(xué)習(xí)使用Retrofit2的使用娄昆。
- 創(chuàng)建Retrofit2實例
Retrofit retrofit = new Retrofit.Builder().baseUrl("http://wthrcdn.etouch.cn/").build();
創(chuàng)建Retrofit2實例時需要通過Retrofit.Builder方法,并調(diào)用baseUrl方法設(shè)置根URL萌焰。Retrofit2的baseUlr必須以/結(jié)束谷浅,不然會拋出一個IllegalArgumentException。
- 定義一個和HTTP的API對應(yīng)的接口
/** * 天氣服務(wù) */
public interface WeatherService {
@GET("weather_mini")
Call<WeatherBean> getWeatherInfo(@Query("city") String city);
}
- 生成網(wǎng)絡(luò)請求接口的對象
Retrofit retrofit = new Retrofit.Builder()
.baseUrl("http://wthrcdn.etouch.cn/")
.addConverterFactory(GsonConverterFactory.create(gson))//這塊定義了一個轉(zhuǎn)換器撼玄,之后會講解
.build();
WeatherService service = retrofit.create(WeatherService.class);
- 調(diào)用實現(xiàn)的方法來進行同步或異步的HTTP請求
Call<WeatherBean> call1 = service.getWeatherInfo("沈陽");
//異步請求
call1.enqueue(new Callback<WeatherBean>() {
@Override
public void onResponse(Call<WeatherBean> call, Response<WeatherBean> response) {
Log.d("Retrofit2Example", response.body().getDesc());
}
@Override
public void onFailure(Call<WeatherBean> call, Throwable t) {
Log.d("Retrofit2Example", t.getMessage());
}});
try {
//同步請求
Response<WeatherBean> response = call1.execute();
if (response.isSuccessful()) {
Log.d("Retrofit2Example", response.body().getDesc());
}
} catch (IOException e) {
e.printStackTrace();
}
以上就是一個非常簡單的GET請求掌猛,可以看到通過Retrofit2的create()
方法生成網(wǎng)絡(luò)接口的實現(xiàn)對象留潦,之后調(diào)用具體的接口調(diào)用方法會生成一個請求對應(yīng)的Call
對象辣往,在這個時候請求并沒有發(fā)送出去殖卑,接下來調(diào)用Call
對象的enqueue
或者execute
方法實現(xiàn)異步和同步的網(wǎng)絡(luò)請求。
- 詳細分析每一步
- Retrofit2對象的創(chuàng)建
Retrofit retrofit = new Retrofit.Builder().baseUrl("http://wthrcdn.etouch.cn/").build();
可以看到我們可以使用Retrofit.Builder().build()
這個方法創(chuàng)建一個Retrofit2對象许起,在創(chuàng)建的時候還可以使用baseUrl()
方法為Retrofit2對象設(shè)置一個根路徑。這塊很好理解惦积,那么我們再介紹一下Retrofit2對象創(chuàng)建的時候還可以做的一些操作猛频。
1.首先介紹一下轉(zhuǎn)換器,轉(zhuǎn)換器顧名思義就是將服務(wù)端返回的Response
進行轉(zhuǎn)換睦柴。在默認情況下Retrofit只支持將HTTP的響應(yīng)體轉(zhuǎn)換換為ResponseBody
毡熏。比如要用Gson解析的話就需要添加依賴并使用addConverterFactory
方法設(shè)置轉(zhuǎn)換器。
<!-- 引入gson -->
compile 'com.squareup.retrofit2:converter-gson:2.0.2'
Gson gson = new GsonBuilder().setDateFormat("yyyy-MM-dd hh:mm:ss").create();
Retrofit retrofit = new Retrofit.Builder()
.baseUrl("http://wthrcdn.etouch.cn/")
.addConverterFactory(GsonConverterFactory.create(gson))
.build();
上面的代碼首先創(chuàng)建了一個gson對象狱窘,之后再創(chuàng)建retrofit對象的時候使用addConverterFactory方法設(shè)置了轉(zhuǎn)換器财搁。經(jīng)過這個設(shè)置之后呢我們在之后的請求中就可以使用泛型去作為請求體或者返回值了。
/** * 天氣服務(wù) */
public interface WeatherService {
@GET("weather_mini")
Call<WeatherBean> getWeatherInfo(@Query("city") String city);
}
類似這塊的代碼幻馁,返回值就是一個DTO越锈,我們看一下這個DTO內(nèi)部代碼。
public class WeatherBean {
private String desc;
private int status;
private Data data;
public void setDesc(String desc) {
this.desc = desc;
}
public String getDesc() {
return this.desc;
}
public void setStatus(int status) {
this.status = status;
}
public int getStatus() {
return this.status;
}
public void setData(Data data) {
this.data = data;
}
public Data getData() {
return this.data;
}
}
當(dāng)我們設(shè)置了gson轉(zhuǎn)換器之后服務(wù)端返回的json格式的字符串就會被轉(zhuǎn)換成相應(yīng)的對象了稀拐。當(dāng)然如果你沒有設(shè)置轉(zhuǎn)換器的話這塊的泛型就是默認的ResponseBody
了丹弱。
/** * 天氣服務(wù) */
public interface WeatherService {
@GET("weather_mini")
Call<ResponseBody> getWeatherInfo(@Query("city") String city);
}
- 自定義Converter
retrofit自帶的轉(zhuǎn)換器:- 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,String):com.squareup.retrofit2:converter-scalars
如果自帶的轉(zhuǎn)換器不能滿足我們的需要躲胳,這個時候我們就需要自定義一個轉(zhuǎn)換器Converter
了。
public interface Converter<F, T> {
// 實現(xiàn)從 F(rom) 到 T(o)的轉(zhuǎn)換
T convert(F value) throws IOException;
// 用于向Retrofit提供相應(yīng)Converter的工廠
abstract class Factory {
// 這里創(chuàng)建從ResponseBody向其它類型的Converter隆檀,如果不能轉(zhuǎn)換返回null
// 主要用于對響應(yīng)體的處理
public Converter<ResponseBody, ?> responseBodyConverter(Type type, Annotation[] annotations, Retrofit retrofit) {
return null;
}
// 在這里創(chuàng)建 從自定類型到RequestBody的Converter,不能處理就返回null,
// 主要用于對Part泉坐、PartMap裳仆、Body注解的處理
public Converter<?, RequestBody> requestBodyConverter(Type type, Annotation[] parameterAnnotations, Annotation[] methodAnnotations, Retrofit retrofit) {
return null;
}
// 這里用于對Field、FieldMap纯丸、Header构捡、Path、Query勾徽、QueryMap注解的處理
// Retrfofit對于上面的幾個注解默認使用的是調(diào)用toString方法
public Converter<?, String> stringConverter(Type type, Annotation[] annotations, Retrofit retrofit) {
return null;
}
}
}
比如我們要定義Call<ResponseBody>
轉(zhuǎn)換為Call<String>
的轉(zhuǎn)換器喘帚,那么對應(yīng)的F和T則分別對應(yīng)ResponseBody
和String
,我們定義一個StringConverter
并實現(xiàn)Converter
接口吹由。
public static class StringConverter implements Converter<ResponseBody, String> {
public static final StringConverter INSTANCE = new StringConverter();
@Override
public String convert(ResponseBody value)throws IOException {
return value.string();
}
}
還需要一個Factory來向Retrofit注冊StringConverter倾鲫。
public static class StringConverterFactory extends Converter.Factory {
public static final StringConverterFactory INSTANCE = new StringConverterFactory();
public static StringConverterFactory create() {
return INSTANCE;
}
// 我們只關(guān)實現(xiàn)從ResponseBody 到 String 的轉(zhuǎn)換,所以其它方法可不覆蓋
@Override
public Converter<ResponseBody, ?> responseBodyConverter(Type type, Annotation[] annotations, Retrofit retrofit) {
if (type == String.class) {
return StringConverter.INSTANCE;
}
//其它類型我們不處理隙疚,返回null就行
return null;
}
}
好了磕道,之后我們再使用Retrofit.Builder.addConverterFactory
向Retrofit注冊我們StringConverterFactory
。
Retrofit retrofit = new Retrofit.Builder()
.baseUrl("http://wthrcdn.etouch.cn/") // 如是有Gson這類的Converter 一定要放在其它前面
.addConverterFactory(StringConverterFactory.create())
.addConverterFactory(GsonConverterFactory.create())
.build();
這里addConverterFactory
是有先后順序的伶丐,如果有多個ConverterFactory
都支持同一種類型疯特,那么就是只有第一個才會被使用,而GsonConverterFactory
是不判斷是否支持的啡彬,所以這里交換順序還會有一個異常拋出羹与,原因是類型不匹配∈樱現(xiàn)在只要返回值類型的泛型參數(shù)是String
就會由我們的StringConverter處理往踢。