Retrofit也是Square公司的一個開源庫裆站,主要是對Http網絡請求框架的封裝搏恤,其本質上還是由OkHttp(默認)完成具體的網絡請求梢莽,Retrofit只是對網絡請求接口進行了封裝(把每一個API網絡請求都變成一個Java接口)嗓袱。
具體流程如下:
- App應用程序通過 Retrofit 請求網絡,實際上是使用 Retrofit 接口層封裝請求參數胸蛛、Header污茵、Url 等信息,之后由 OkHttp 完成后續(xù)的請求操作
- 在服務端返回數據之后葬项,OkHttp 將原始的結果交給 Retrofit泞当,Retrofit根據用戶的需求對結果進行解析
使用Retrofit的步驟:
1.添加Retrofit庫的依賴,添加網絡請求權限
2.創(chuàng)建接收服務器返回數據的類(JavaBean)
3.創(chuàng)建用于描述網絡請求的接口
- Retrofit把每一個http請求抽象成了Java接口,通過注解來描述請求方式和請求參數民珍,還可以通過注解來配置網絡請求參數 襟士。
- 內部的實現原理就是通過動態(tài)代理將接口的注解翻譯成一個個http請求,再有線程池來執(zhí)行這一個個的網絡請求嚷量。
4.創(chuàng)建Retrofit實例(Builder構造者模式)
5.創(chuàng)建網絡請求接口的實例
6.發(fā)送網絡請求(異步enqueue/同步execute)
7.處理服務器返回的數據
PS: 這里具體說一下第三點陋桂,創(chuàng)建網絡請求的接口時用到了大量的注解處理,包括請求頭津肛、請求方式章喉、請求參數等等。
注解詳情:
1.網絡請求方式的注解:
這里注意一下@HTTP的使用:
import okhttp3.ResponseBody;
import retrofit2.Call;
import retrofit2.http.Field;
import retrofit2.http.GET;
import retrofit2.http.HTTP;
import retrofit2.http.Path;
public interface ServiceApi {
/**
* method:網絡請求的方法(區(qū)分大小寫)
* path:網絡請求地址路徑
* hasBody:是否有請求體
*/
@HTTP(method = "GET", path = "xxx/{id}", hasBody = false)
Call<ResponseBody> getCall(@Path("id") int id);
// {id} 表示是一個變量
// method 的值 retrofit 不會做處理身坐,所以要自行保證準確
}
- 作用:替換@GET、@POST落包、@PUT部蛇、@DELETE、@HEAD等注解的作用及更多功能拓展
- 具體使用:通過屬性method咐蝇、path涯鲁、hasBody進行設置
2.標記具體作用的注解:
a. @FormUrlEncoded
作用:表示發(fā)送form-encoded的數據
每個鍵值對需要用@Filed來注解鍵名,隨后的對象需要提供值有序。
b. @Multipart
作用:表示發(fā)送form-encoded的數據(適用于 有文件 上傳的場景)
每個鍵值對需要用@Part來注解鍵名抹腿,隨后的對象需要提供值。
/**
* 表明是一個表單格式的請求(Content-Type:application/x-www-form-urlencoded)
* Field("username") 表示將后面的 String name 中name的取值作為 username 的值
*/
@POST("/form")
@FormUrlEncoded
Call<ResponseBody> testFormUrl(@Field("username") String name, @Field("password") int pwd);
/**
* Part 后面支持三種類型旭寿,RequestBody警绩、okHttp3.MultipartBody.Part 、任意類型
* 除 okHttp3.MultipartBody.Part 以外盅称,其它類型都必須帶上表單字段(okHttp3.MultipartBody.Part 中已經包含了表單字段的信息)肩祥,
*/
@POST("/form")
@Multipart
Call<ResponseBody> testFileUpLoad(@Part("name") RequestBody name, @Part("password") RequestBody pwd, @Part MultipartBody.Part file);
具體使用:
Retrofit retrofit = new Retrofit.Builder()
.baseUrl("https://api.github.com/")
.addConverterFactory(GsonConverterFactory.create())
.build();
ServiceApi service = retrofit.create(ServiceApi.class);
// @FormUrlEncoded
Call<ResponseBody> call = service.testFormUrl("Jack", 123456);
// @Multipart
MediaType textType = MediaType.parse("text/plain"); // 文本類型
RequestBody name = RequestBody.create(textType, "Jack");
RequestBody pwd = RequestBody.create(textType, "123456");
RequestBody file = RequestBody.create(MediaType.parse("application/octet-stream"), "這里是模擬文件的內容");
MultipartBody.Part filePart = MultipartBody.Part.createFormData("file", "test.txt", file);
// Call<ResponseBody> call = service.testFileUpLoad(name, pwd, filePart);
//發(fā)送網絡請求(異步)
call.enqueue(new Callback<ResponseBody>() {
//請求成功時回調
@Override
public void onResponse(@NonNull Call<ResponseBody> call, @NonNull Response<ResponseBody> response) {
//請求處理,輸出結果
assert response.body() != null;
response.body().toString();
}
//請求失敗時候的回調
@Override
public void onFailure(@NonNull Call<ResponseBody> call, @NonNull Throwable throwable) {
System.out.println("連接失敗");
}
});
3.網絡請求參數的注解:
詳解:
1.@Header 和 @Headers
作用:添加不固定的請求頭 和 添加請求頭
具體使用如下:
// @Header 使用
@GET("xxx")
Call<Test> getTest(@Header("Authorization") String authorization);
// @Headers 使用
@Headers("Authorization: authorization")
@GET("xxx")
Call<Test> getTest();
// 以上的效果是一致的后室。
// 區(qū)別在于使用場景和使用方式
// 1. 使用場景:@Header用于添加不固定的請求頭,@Headers用于添加固定的請求頭
// 2. 使用方式:@Header作用于方法的參數混狠;@Headers作用于方法
2.@Body:
作用:以 Post方式 傳遞 自定義數據類型 給 服務器(Server)
特別注意:如果提交的是一個Map岸霹,那么作用相當于 @Field
不過Map要經過 FormBody.Builder 類處理成為符合 OkHttp 格式的表單,如下:
FormBody.Builder builder = new FormBody.Builder();
builder.add("key","value");
3.@Field 和 @FieldMap:
作用:發(fā)送 Post請求時提交請求的表單字段
具體使用:與 @FormUrlEncoded 注解配合使用
/**
* 表明是一個表單格式的請求(Content-Type:application/x-www-form-urlencoded)
* Field("username") 表示將后面的 String name 中name的取值作為 username 的值
*/
@POST("/form")
@FormUrlEncoded
Call<ResponseBody> testFormUrl(@Field("username") String name, @Field("password") int pwd);
/**
* Map的key作為表單的鍵
*/
@POST("/form")
@FormUrlEncoded
Call<ResponseBody> testFormUrl(@FieldMap Map<String, Object> map);
調用:
// @Field
Call<ResponseBody> call = service.testFormUrl("Jack", 123456);
// @FieldMap
// 實現的效果與上面相同将饺,但要傳入Map
Map<String, Object> map = new HashMap<>();
map.put("username", "Jack");
map.put("password", 123456);
Call<ResponseBody> call = service.testFormUrl(map);
//發(fā)送網絡請求(異步)
call.enqueue(new Callback<ResponseBody>() {
//請求成功時回調
@Override
public void onResponse(@NonNull Call<ResponseBody> call, @NonNull Response<ResponseBody> response) {
//請求處理,輸出結果
assert response.body() != null;
response.body().toString();
}
//請求失敗時候的回調
@Override
public void onFailure(@NonNull Call<ResponseBody> call, @NonNull Throwable throwable) {
System.out.println("連接失敗");
}
});
4.@Part & @PartMap:
作用:發(fā)送 Post請求 時提交請求的表單字段
- 與@Field的區(qū)別:功能相同贡避,但攜帶的參數類型更加豐富,包括數據流予弧,所以適用于 有文件上傳 的場景
- 具體使用:與 @Multipart 注解配合使用:
/**
* Part 后面支持三種類型刮吧,RequestBody、okHttp3.MultipartBody.Part 桌肴、任意類型
* 除 okHttp3.MultipartBody.Part 以外皇筛,其它類型都必須帶上表單字段(okHttp3.MultipartBody.Part 中已經包含了表單字段的信息),
*/
@POST("/form")
@Multipart
Call<ResponseBody> testFileUpLoad(@Part("name") RequestBody name, @Part("age") RequestBody age, @Part MultipartBody.Part file);
/**
* PartMap 注解支持一個Map作為參數坠七,支持 RequestBody 類型水醋,
* 如果有其它的類型,會被retrofit2.Converter轉換彪置,如后面會介紹的 使用Gson的 retrofit2.converter.gson.GsonRequestBodyConverter
* 文件只能用 @Part MultipartBody.Part
*/
@POST("/form")
@Multipart
Call<ResponseBody> testFileUpLoad(@PartMap Map<String, RequestBody> args, @Part MultipartBody.Part file);
@POST("/form")
@Multipart
Call<ResponseBody> testFileUpLoad(@PartMap Map<String, RequestBody> args);
調用:
MediaType textType = MediaType.parse("text/plain");
RequestBody name = RequestBody.create(textType, "Jack");
RequestBody pwd = RequestBody.create(textType, "123456");
RequestBody file = RequestBody.create(MediaType.parse("application/octet-stream"), "這里是模擬文件的內容");
// @Part
MultipartBody.Part filePart = MultipartBody.Part.createFormData("file", "test.txt", file);
Call<ResponseBody> call = service.testFileUpLoad(name, pwd, filePart);
// @PartMap
// 實現和上面同樣的效果
Map<String, RequestBody> mapParam = new HashMap<>();
mapParam.put("name", name);
mapParam.put("password", pwd);
//這里并不會被當成文件拄踪,因為沒有文件名(包含在Content-Disposition請求頭中),但上面的 filePart 有
//mapParam.put("file", file);
Call<ResponseBody> call = service.testFileUpLoad(mapParam, filePart); //單獨處理文件
//發(fā)送網絡請求(異步)
call.enqueue(new Callback<ResponseBody>() {
//請求成功時回調
@Override
public void onResponse(@NonNull Call<ResponseBody> call, @NonNull Response<ResponseBody> response) {
//請求處理,輸出結果
assert response.body() != null;
response.body().toString();
}
//請求失敗時候的回調
@Override
public void onFailure(@NonNull Call<ResponseBody> call, @NonNull Throwable throwable) {
System.out.println("連接失敗");
}
});
5.@Query和@QueryMap:
- 作用:用于
@GET
方法的查詢參數(Query = Url 中 ‘?’ 后面的 key-value)
如:url = http://www.platfrom.net/?type=android其中拳魁,Query = type
- 具體使用:
@GET("xxx/")
Call<String> tag(@Query("type") String type);
@GET("xxx/")
Call<String> tag(@QueryMap Map<String, Object> args);
調用:
Retrofit retrofit = new Retrofit.Builder()
.baseUrl("https://api.xxx.com/")
.addConverterFactory(GsonConverterFactory.create())
.build();
ServiceApi service = retrofit.create(ServiceApi.class);
// @Query
Call<String> call = service.tag("android");
// @QueryMap
// 實現的效果與上面相同惶桐,但要傳入Map
Map<String, Object> map = new HashMap<>();
map.put("type", "android");
Call<String> call = service.tag(map);
//發(fā)送網絡請求(異步)
call.enqueue(new Callback<String>() {
//請求成功時回調
@Override
public void onResponse(@NonNull Call<String> call, @NonNull Response<String> response) {
//請求處理,輸出結果
assert response.body() != null;
response.body().toString();
}
//請求失敗時候的回調
@Override
public void onFailure(@NonNull Call<String> call, @NonNull Throwable throwable) {
System.out.println("連接失敗");
}
});
6.@Path:
作用:URL地址的缺省值
具體使用:
@GET("xxx/{yyy}/zzz")
Call<ResponseBody> getGitHub(@Path("yyy") String yyy);
// 訪問的API是:https://api.github.com/xxx/{yyy}/zzz
// 在發(fā)起請求時, {yyy} 會被替換為方法的第一個參數 yyy(被@Path注解作用)
7.@Url:
作用:直接傳入一個請求的 URL變量 用于URL設置
具體使用:
@GET
Call<ResponseBody> testUrlAndQuery(@Url String url, @Query("showAll") boolean showAll);
// 當有URL注解時潘懊,@GET傳入的URL就可以省略
// 當GET姚糊、POST...HTTP等方法中沒有設置Url時,則必須使用 @Url
總結:
以上注解知識摘自網上及自己的一些總結授舟,這里做記錄純屬為了以后查閱方便救恨。
下面創(chuàng)建一個完整的例子來走一遍Retrofit的網絡請求:
1.添加Retrofit庫的依賴,添加網絡請求權限:
"com.squareup.retrofit2:retrofit:2.5.0",
"com.squareup.retrofit2:converter-gson:2.5.0",
"com.squareup.retrofit2:adapter-rxjava:2.5.0",
"com.squareup.retrofit2:converter-scalars:2.0.1",
"com.squareup.okhttp3:okhttp:3.12.1",
"com.squareup.okhttp3:logging-interceptor:3.10.0",
<uses-permission android:name="android.permission.INTERNET"/>
2.創(chuàng)建接收服務器返回數據的類(JavaBean)
public class WorksListVo {
public WorksItem data;
public class WorksItem {
public ArrayList<Works> content;
}
public static class Works {
public String tid;
public String hits;
public String utime;
public String f_catalog_id;
public String uid;
public String content;
public String province;
public String avatar;
public String sname;
public String genderid;
public String gender;
public String intro;
}
}
3.創(chuàng)建用于描述網絡請求的接口
public interface ApiService {
@POST("/xxx/thread/yyy")
@FormUrlEncoded
Call<WorksListVo> getWorkMoreData(@Field("last_id") String last_id,
@Field("utime") String utime, @Field("rn") String rn);
}
4.創(chuàng)建Retrofit實例(Builder構造者模式)
Retrofit retrofit = new Retrofit.Builder()
.baseUrl("https://api.xxx.com/")
.addConverterFactory(GsonConverterFactory.create())
.build();
5.創(chuàng)建網絡請求接口的實例
ApiService service = retrofit.create(ApiService.class);
Call<WorksListVo> call = service.getWorkMoreData("9526","2019-01-11",android");
6.發(fā)送網絡請求(異步enqueue/同步execute)
7.處理服務器返回的數據(通過response類的 body()對返回的數據進行處理)
//發(fā)送網絡請求(異步)
call.enqueue(new Callback<String>() {
//請求成功時回調
@Override
public void onResponse(@NonNull Call<String> call, @NonNull Response<String> response) {
//請求處理,輸出結果
assert response.body() != null;
response.body().toString();
}
//請求失敗時候的回調
@Override
public void onFailure(@NonNull Call<String> call, @NonNull Throwable throwable) {
System.out.println("連接失敗");
}
});
Retrofit源碼解讀傳送門:http://www.reibang.com/p/cef4cfc4f756