1 簡介
Retrofit是Square公司開發的Android裏對HTTP網絡請求的框架,官網是https://square.github.io/retrofit/。其底層是基於OkHttp實現的,也就是說Retrofit就對OkHttp的進一步封裝。Retrofit最大的特點就是簡潔易用,它使用了大量的運行時註解的方式來提供功能。
2 快速上手
假設有一服務器接口:https://api.xx.com/url.json?id=123,其請求後的返回值是
[
{
"app_name": "今日頭條",
"package_name": "com.ss.android.article.news"
},
{
"app_name": "騰訊新聞",
"package_name": "com.tencent.news"
},
{
"app_name": "鳳凰新聞",
"package_name": "com.ifeng.news2"
}
]
現在我們需要在代碼裏對其進行網絡請求,並將返回結果轉化成AppInfo對象。AppInfo類代碼如下所示:
AppInfo.java
public class AppInfo {
private String app_name;
private String package_name;
@Override
public String toString() {
return "{app_name:" + app_name + ",package_name:" + package_name + "}";
}
}
在build.gradle中添加支持庫依賴:
dependencies {
implementation 'com.squareup.retrofit2:retrofit:2.6.2'
implementation 'com.squareup.retrofit2:converter-gson:2.5.0'
}
在AndroidManifest.xml中聲明網絡請求權限:
<uses-permission android:name="android.permission.INTERNET"/>
於是我們使用Retrofit進行網絡請求的代碼可以這樣:
private void getRequest() {
Retrofit retorfit = new Retrofit.Builder()
.baseUrl("https://api.xx.com/")
.addConverterFactory(GsonConverterFactory.create())
.build();
AppInfoService appInfoService = retorfit.create(AppInfoService.class);
Call<List<AppInfo>> call = appInfoService.getAppInfoList(123);
call.enqueue(new Callback<List<AppInfo>>() {
@Override
public void onResponse(Call<List<AppInfo>> call, Response<List<AppInfo>> response) {
List<AppInfo> appInfos = response.body();
Log.e("zyx", appInfos.toString());
Log.e("zyx", "當前線程:" + Thread.currentThread().getName());
}
@Override
public void onFailure(Call<List<AppInfo>> call, Throwable t) {
Log.e("zyx", t.toString());
Log.e("zyx", "當前線程:" + Thread.currentThread().getName());
}
});
}
public interface AppInfoService {
@GET("url.json")
Call<List<AppInfo>> getAppInfoList(@Query("id") int id);
}
在你的代碼中執行getRequest方法,運行程序,便可見輸出如下結果,而且回調還會在主線程中進行:
2019-12-04 19:27:23.209 30842-30842/com.zyx.myapplication E/zyx: [{app_name:今日頭條,package_name:com.ss.android.article.news}, {app_name:騰訊新聞,package_name:com.tencent.news}, {app_name:鳳凰新聞,package_name:com.ifeng.news2}]
2019-12-04 19:27:23.212 30842-30842/com.zyx.myapplication E/zyx: 當前線程:main
3 介紹
3.1 庫的依賴和轉換器
要使用Rerofit庫,就必須在build.gradle中配置其依賴:
dependencies {
implementation 'com.squareup.retrofit2:retrofit:2.6.2'
}
上述示例中除添加Rerofit庫外還添加了一個gson的庫。其實gson庫是可選的,默認情況下,Retrofit只能把HTTP響應結果反序列化成OkHttp的ResponseBody的類型並且只能接受它的RequsetBody的類型是@Body(後面介紹)。然而通過addConverterFactory方法可添加其它轉換器來支持其他的類型。可以通過添加如下依賴包來支持其序列化操作。
- 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
當然你也可以通過自定義的方式創建自己的轉換器,只要創建一個繼承於Converter.Factory的類並實現responseBodyConverter 和 requestBodyConverter方法,然後使用addConverterFactory添加適配器的時候傳入其一個實例就可以了。
3.2 請求方法註解
從上述示例中AppInfoService你會發現在getAppInfoList方法中就定義了一個@GET的註解,其實Rerofit裏頭很多是藉助註解來完成。HTTP請求方法註解有8種,它們是GET、POST、PUT、DELETE、HEAD、PATCH、OPTIONS和HTTP。其中前面7種分別對應HTTP的請求方法;而最後一種HTTP 是通用註解,可以通過參數來替換以上 7 種。使用如:
@HTTP(method = "GET", path = " url.json", hasBody = false)
Call<List<AppInfo>> getAppInfoList();
method 表示請求的方法,注意它是區分大小寫
path 表示網絡請求地址路徑
hasBody 表示是否有請求體
3.3 請求頭註解
請求頭註解有兩種:Headers 和 Header。它們區別在於,前者一般用於固件的請求頭,可添加多個;而後者一般用於不固定請求頭,作爲方法的參數輸入,使用如:
@Headers({"Accept: application/vnd.github.v3.full+json", "User-Agent: Retrofit-Sample-App"})
@GET("url.json")
Call<List<AppInfo>> getAppInfoList(@Header("Authorization") String authorization);
3.3 參數類註解
參數類註解有:Body、Path、Field、FieldMap、Part、PartMap、Query、QueryMap 和 Url 等。
3.3.1 Body
用於Post請求發送HTTP請求體,比如已添加過GsonConverterFactory,則可將類對象傳入,使用如:
@POST("users/new")
Call<User> createUser(@Body User user);
3.3.2 Path
用於Url中的佔位符,使用如:
@GET("group/{id}/users")
Call<List<User>> groupList(@Path("id") int groupId);
3.3.3 Field 和 FieldMap
用於Post請求時以表單的形式傳遞參數,需要結合@FromUrlEncoded使用,使用如:
@FormUrlEncoded
@POST("user/edit")
Call<User> updateUser(@Field("first_name") String first, @Field("last_name") String last);
@FormUrlEncoded
@POST("user/edit")
Call<User> updateUser(@FieldMap Map<String, String> map);
3.3.4 Part 和 PartMap
用於Post請求時以表單的形式傳遞參數,與@Field區別於@Part可攜帶更加豐富的參數類型,比如用於文件上傳時可攜帶數據流,需要結合@Multipart使用,使用如:
@Multipart
@PUT("user/photo")
Call<User> updateUser(@Part("photo") RequestBody photo, @Part("description") RequestBody description);
@Multipart
@PUT("user/photo")
Call<User> updateUser(@PartMap Map<String, RequestBody> map);
3.3.5 Query 和 QueryMap
用於Get請求時傳遞參數,使用如:
@GET("group/{id}/users")
Call<List<User>> groupList(@Path("id") int groupId, @Query("sort") String sort);
@GET("group/{id}/users")
Call<List<User>> groupList(@Path("id") int groupId, @QueryMap Map<String, String> options);
3.3.6 Url
用於指定請求路徑,使用如:
@GET
Call<List<AppInfo>> getAppInfoList(@Url String url, @Query("id") int id);
3.4 標記類註解
標記類註解有 3 種:FormUrlEncoded、Multipart、Streaming。
3.4.1 FormUrlEncoded
用於Post請求時,請求實體是一個From表單的時,每個鍵值對需要使用@Field註解,使上如上面介紹Field 和 FieldMap。
3.4.2 Multipart
用於Post請求時,請求實體是豐富的參數類型,比如用於文件上傳時可攜帶數據流,每個鍵值對需要使用@Part註解,使上如上面介紹Part和PartMap。
3.4.3 Streaming
一般在下載比較大的文件時,需要添加@Streaming註解,表示響應字節流的形式返回,這樣就可避免大文件全部加載到內存中。
3.5 同步和異步
Call對象可以同步地或異步地進行網絡請求,而且每一個對象只能使用一次,若想執行多次可以調用clone()來創建一個新對象。還要注意的是,在Android中,回調會在主線程中執行,而在JVM中,回調會在與執行HTTP請求的相同線程。
同步執行示例代碼:
try {
Response<List<AppInfo>> response = call.execute();
List<AppInfo> appInfos = response.body();
Log.e("zyx", appInfos.toString());
} catch (IOException e) {
e.printStackTrace();
}
異步執行示例代碼:
call.enqueue(new Callback<List<AppInfo>>() {
@Override
public void onResponse(Call<List<AppInfo>> call, Response<List<AppInfo>> response) {
List<AppInfo> appInfos = response.body();
Log.e("zyx", appInfos.toString());
}
@Override
public void onFailure(Call<List<AppInfo>> call, Throwable t) {
Log.e("zyx", t.toString());
}
});
看到上述同步和異步代碼後,你是不是有似曾相識的感謝。我們之前學習OkHttp中同步和異步請求也是差不多的代碼,因爲Rerofit框架底層本來就是使用了OkHttp的。