Retrofit 網路請求框架
1、什麼是Retrofit框架?
它是Square公司開發的現在非常流行的網路框架
2.為什麼使用Retrofit框架
效能好,處理快,使用簡單,Retrofit 是安卓上最流行的HTTP Client庫之一 預設使用OKHttp處理網路請求,我覺得可以看成是OKHttp的增強。預設使用Gson解析.
怎麼配置Retrofit2.0?
//配置retrofit2.0 compile 'com.squareup.retrofit2:retrofit:+' compile 'com.squareup.retrofit2:converter-gson:+' //配置支援Rxjava2 compile 'com.squareup.retrofit2:adapter-rxjava2:+' compile 'io.reactivex.rxjava2:rxjava:+' compile 'io.reactivex.rxjava2:rxandroid:+'
1.構造Retrofit:
***baseUrl:****
1. 預設的伺服器地址,Retrofit在進行請求一個介面時會根據你填寫的
baseurl+方法名 去請求。
****addConverterFactory:****
新增返回資料的解析方式,Retrofit支援多種格式的解析,xml,json,
jackson,Moshi,Protobuf,wire等,新增對這些資料格式的支援同樣需要在
Gradle中進行依賴。這些依賴Square公司同樣提供了支援。
****CallAdapter:****
Retrofit會將網路請求的介面以回撥的方式返回,我們通常會得到一個叫做
Call<型別>的結果,如果我們需要返回其他的結果,例如RxJava形式的結果,需
要對RxJava做支援。如果不需要特殊型別的返回結果,我們是不需要配置的。
****Client:****
通過該方法我們可以新增自己定製的OkHttp
三、建立介面 宣告API
四、 Retrofit各個註解的含義及作用
GET註解 1. 用於傳送一個get請求 2. GET註解一般必須新增相對路徑或絕對路徑或者全路徑,如果不想在GET註解後新增請求路徑,則可以在方法的第一個引數中用@Url註解新增請求路徑。 Url註解: 1. 作用於方法引數 2. 用於新增請求的介面地址 3.示例:
@GET Call<ResponseBody> list(@Url String url);Path註解: 1. 作用於方法的引數 2. 在URL路徑段中替換指定的引數值。使用String.valueOf()和URL編碼 3. 將值轉換為字串。 4. 使值不可為用該註解定義的引數的空 5. 引數值預設使用URL編碼 6. 示例://預設使用URL編碼 @GET("/user/{name}") Call<ResponseBody> encoded(@Path("name") String name); //不使用URL編碼 @GET("/user/{name}") Call<ResponseBody> notEncoded(@Path(value="name", encoded=true) String name);Query註解: 1. 作用於方法的引數 2. 用於新增查詢引數,即請求引數 3. 引數值通過String.valueOf()轉換為String並進行URL編碼 4. 使用該註解定義的引數,引數值可以為空,為空時,忽略該值,當傳入一個Listarray時,為每個非空item拼接請求鍵值對,所有的鍵是統一的,如:name=張三&name=李&name=王五. 5. 示例:@GET("/list") Call<ResponseBody> list(@Query("page") int page); @GET("/list") Call<ResponseBody> list(@Query("category") String category); //傳入一個數組 @GET("/list") Call<ResponseBody> list(@Query("category") String... categories); //不進行URL編碼 @GET("/search") Call<ResponseBody> list(@Query(value="foo", encoded=true) String foo);QueryMap註解: 1. 作用於方法的引數 2. 以map的形式新增查詢引數,即請求引數 3. 引數的鍵和值都通過String.valueOf()轉換為String格式 4. map的鍵和值預設進行URL編碼 5. map中每一項的鍵和值都不能為空,否則拋IllegalArgumentException異常 6. 示例://使用預設URL編碼 @GET("/search") Call<ResponseBody> list(@QueryMap Map<String, String> filters); //不使用預設URL編碼 @GET("/search") Call<ResponseBody> list(@QueryMap(encoded=true) Map<String, String> filters);
POST註解: 1. 用於傳送一個POST請求 2. POST註解一般必須新增相對路徑或絕對路徑或者全路徑,如果不想在POST註解後新增請求路徑,則可以在方法的第一個引數中用@Url註解新增請求路徑 FormUrlEncoded註解: 1. 用於修飾Field註解和FieldMap註解 2. 使用該註解,表示請求正文將使用表單網址編碼。欄位應該宣告為引數,並用@Field註釋或FieldMap註釋。使用FormUrlEncodied註解的請求將具”applicaton / x-www-form-urlencoded” MIME型別 Field註解 1. 作用於方法的引數 2. 用於傳送一個表單請求
@FormUrlEncoded @POST("/") Call<ResponseBody> example(@Field("name") String name,@Field("occupation") String occupation); //固定或可變陣列 @FormUrlEncoded @POST("/list") Call<ResponseBody> example(@Field("name") String... names);FieldMap註解: 1. 作用於方法的引數 2. 用於傳送一個表單請求 3. map中每一項的鍵和值都不能為空,否則丟擲IllegalArgumentException異常@FormUrlEncoded @POST("/things") Call<ResponseBody> things(@FieldMap Map<String, String> fields);Header註解: 1. 作用於方法的引數,用於新增請求頭 2. 使用該註解定義的請求頭可以為空,當為空時,會自動忽略,當傳入一個List或array時,為拼接每個非空的item的值到請求頭中.具有相同名稱的請求頭不會相互覆蓋,而是會照樣新增到請求頭中 3. 示例:@GET("/") Call<ResponseBody> foo(@Header("Accept-Language") String lang);HeaderMap註解: 1. 作用於方法的引數,用於新增請求頭 2. 以map的方式新增多個請求頭,map中的key為請求頭的名稱,value為請求頭的值,且value使用String.valueOf()統一轉換為String型別,map中每一項的鍵和值都不能為空,否則丟擲IllegalArgumentException異常 3. 示例:@GET("/search") void list(@HeaderMap Map<String, String> headers); //map Map<String,String> headers = new HashMap()<>; headers.put("Accept","text/plain"); headers.put("Accept-Charset", "utf-8");Headers註解: 1. 作用於方法,用於新增一個或多個請求頭 2. 具有相同名稱的請求頭不會相互覆蓋,而是會照樣新增到請求頭中 3. 示例://新增一個請求頭 @Headers("Cache-Control: max-age=640000") @GET("/") ... //新增多個請求頭 @Headers({"X-Foo: Bar", "X-Ping: Pong" }) @GET("/") ...
2.1 HTTP註解: (瞭解) 1. 作用於方法,用於傳送一個自定義的HTTP請求 2. 示例:
//自定義HTTP請求的標準樣式 interface Service { @HTTP(method = "CUSTOM", path = "custom/endpoint/") Call<ResponseBody> customEndpoint(); } //傳送一個DELETE請求 interface Service { @HTTP(method = "DELETE", path = "remove/", hasBody = true) Call<ResponseBody> deleteObject(@Body RequestBody object); }Multipart註解: 1. 作用於方法 2. 使用該註解,表示請求體是多部分的。 每一部分作為一個引數,且用Part註解宣告 Part註解: 1. 作用於方法的引數,用於定義Multipart請求的每個part 2. 使用該註解定義的引數,引數值可以為空,為空時,則忽略 3. 使用該註解定義的引數型別有以下3種方式可選: 1, 如果型別是okhttp3.MultipartBody.Part,內容將被直接使用。 省略 part中的名稱,即 @Part MultipartBody.Part part 2, 如果型別是RequestBody,那麼該值將直接與其內容型別一起使用。 在註釋中提供part名稱(例如@Part(“foo”)RequestBody foo)。 3, 其他物件型別將通過使用轉換器轉換為適當的格式。 在註釋中提供part名稱(例如,@Part(“foo”)Image photo) 4. 示例:@Multipart @POST("/") Call<ResponseBody> example( @Part("description") String description, @Part(value = "image", encoding = "8-bit") RequestBody image);PartMap註解: 1. 作用於方法的引數,以map的方式定義Multipart請求的每個partmap中每一項的鍵和值都不能為空,否則丟擲IllegalArgumentException異常 使用該註解定義的引數型別有以下2種方式可選: 1, 如果型別是RequestBody,那麼該值將直接與其內容型別一起使用。 2, 其他物件型別將通過使用轉換器轉換為適當的格式。 示例:@Multipart @POST("/upload") Call<ResponseBody> upload( @Part("efile") RequestBody file, @PartMap Map<String, RquestBody> params);3.2 Streaming註解: 1. 作用於方法 2. 處理返回Response的方法的響應體,即沒有將body()轉換為byte[]。
注意事項:
1,以上部分註解真正的實現在ParameterHandler類中,,每個註解的真正實現都是ParameterHandler類中的一個final型別的內部類,每個內部類都對各個註解的使用要求做了限制,比如引數是否可空,鍵和值是否可空等.
2,FormUrlEncoded註解和Multipart註解不能同時使用
3,Path註解與Url註解不能同時使用
4,對於FiledMap,HeaderMap,PartMap,QueryMap這四種作用於方法的註解,其引數型別必須為Map的例項,且key的型別必須為String型別
5,使用Body註解的引數不能使用form 或multi-part編碼,即如果為方法使用了FormUrlEncoded或Multipart註解,則方法的引數中不能使用Body註解
6,Retrofit提供了MultiPart註解,說明我們可以上傳檔案,又提供了Streaming註解,說明我們可以下載檔案,我們知道Retrofit可以幹這些事,但是我們還是沒有辦法直接寫上傳下載程式碼,這些東西都需要我們自己去封裝,這也是為什麼目前有很多基於Retrofit構建的二次封裝庫的原因
Retrofit 網路請求之@Body標籤遇到的坑
程式碼:
/**
* 封裝Retrofit網路請求框架
* 懶漢式單例模式
*/
public class RetrofitManager {
public static final String BASE_URL = "http://www.zhaoapi.cn/";
private final Retrofit retrofit;
// 靜態內部類的單例
private static final class SINGLE_INSTANCE {
private static final RetrofitManager _INSTANCE = new RetrofitManager();
}
public static RetrofitManager getInstance() {
return SINGLE_INSTANCE._INSTANCE;
}
/**
* 在構造方法中構造Retrofit物件
* 設定公共url,解析方式gson,配置OkHttpClient
*/
private RetrofitManager() {
// 構造Retrofit物件
retrofit = new Retrofit.Builder()
// 設定公共的url部分
.baseUrl(BASE_URL)
// 配置解析方式為Gson
.addConverterFactory(GsonConverterFactory.create())
// 配置OKHttpClient
.client(buildOkHttpClient())
.build();
}
@NonNull
private OkHttpClient buildOkHttpClient() {
// 構造OkHttpClient物件
return new OkHttpClient.Builder()
.readTimeout(5, TimeUnit.SECONDS)
.writeTimeout(5, TimeUnit.SECONDS)
.connectTimeout(5, TimeUnit.SECONDS)
.build();
}
/**
* 因為retrofit在create的時候需要傳入class
* 並且返回這個類
* @param clazz
* @param <T>
* @return
*/
public <T> T create(Class<T> clazz) {
return retrofit.create(clazz);
}
}
/**
* 點選請求網路登入
* @param view
*/
public void request(View view) {
// 利用Retrofit.create方法構造一個Api介面的例項
ILoginApi iLoginApi = RetrofitManager.getInstance().create(ILoginApi.class);
// 構造一個Call請求,拿到Api介面的例項呼叫對應的方法去構造一個Call請求
final Call<LoginBean> call = iLoginApi.login("login", "18210926066", "123456");
/**
* 非同步請求
*/
call.enqueue(new Callback<LoginBean>() {
@Override
public void onResponse(Call<LoginBean> call, Response<LoginBean> response) {
// 通過response.body拿到最後解析後的bean物件
LoginBean loginBean = response.body();
if (loginBean != null && "0".equals(loginBean.getCode())) {
Toast.makeText(MainActivity.this, "請求成功", Toast.LENGTH_SHORT).show();
}
}
@Override
public void onFailure(Call<LoginBean> call, Throwable t) {
Toast.makeText(MainActivity.this, "請求失敗", Toast.LENGTH_SHORT).show();
}
});
}