在上文,我們了解了如何定義請求Url,感興趣的朋友可以參見《Retrofit之請求Url》。Retrofit系列持續更新,本文介紹如何使用Retrofit定義請求參數。
請求參數從傳遞方式可分三種:url參數,請求主體以及表單編碼。我們來一一討論。
url參數
單個請求參數
url參數就是在url鏈接后面的鍵值對,例如https://api.weibo.com/2//statuses/public_timeline.json?access_token=xxx中,access_token就是url參數,xxx為其值。
Retrofit定義url參數非常直接,只要在方法參數前面添加@Query("key")注解即可。@Query中key的值與url中的參數名稱是一致的,Retrofit會自動添加這些參數到url中。
以之前獲取微博公共動態的API為例,具體API詳見http://open.weibo.com/wiki/2/statuses/public_timeline。從接口中看到,必選參數只有access_token一個,我們定義個方法如下:
@GET("/statuses/public_timeline.json")
Call<Timeline> timelineForPublic(@Query("access_token") String token);
方法timelineForPublic需要參數token,Retrofit會通過@Query中定義的名稱access_token將token映射成請求參數access_token。此時,請求url就會變成:
/statuses/public_timeline.json?access_token=<token>
多個請求參數
從獲取微博公共動態的API中我們可以看到,除了必選的access_token,還有三個可選參數count、page以及base_app,也就是說現在請求參數有四個了。有了上面定義請求參數的介紹,我們只需要往方法上添加相應的參數并用@Query進行注解:
@GET("/statuses/public_timeline.json")
Call<Timeline> timelineForPublic(@Query("access_token") String token, @Query("count") int count, @Query("page") int page, @Query("base_app") int baseApp);
此時,如果我們只需要傳遞access_token,而不需要其他參數,則可以在調用方法的時候傳null。當然,我們不能傳null給int這樣的原生類型,而需要使用對應的Integer。而Retrofit會跳過值為null的參數,并在組裝請求的時候忽略它們。
獲取公共微博最多有四個參數,那么我們再查看下獲取好友微博APIhttp://open.weibo.com/wiki/2/statuses/friends_timeline,發現其有一個必選參數以及七個可選參數,也就是方法會有八個參數。那么問題來了,如果有更多的可選參數,那方法的參數是不是也會特別多,而且很多時候我們只需要其中一兩個請求參數,卻需要提供所有的參數值,顯然很繁瑣。為了處理這種情況,QueryMap就該登場了。
我們可以使用下面的方式定義獲取公共微博API的方法:
@GET("/statuses/public_timeline.json")
Call<Timeline> timelineForPublic(@QueryMap Map<String, String> options);
@QueryMap后面需要緊跟著一個Map< String, String >類型,這樣就可以動態地添加查詢參數了。如果說只需要accept_token參數,則可以像下面這樣調用:
Map<String, String> options = new HashMap<>();
options.put("access_token", AccessTokenKeeper.readAccessToken(getContext()).getToken());
call = weiboService.timelineForPublic(options);
這樣,我們只需要傳遞我們需要設置的參數就可以了。
給每個請求添加url參數
在我們查看微博的各個API時,會發現每個請求都需要一個access_token參數,于是我們就在所有的方法中都添加了對應的參數。那么有沒有簡單的方式來給每個請求都添加相同的參數,從而不需要每個請求都做相同處理呢?
強大的Retrofit是支持的,但是是通過OkHttp中的攔截器來實現的。我們在《Retrofit之初體驗》提及過,Retrofit直接依賴OkHttp,使用OkHttp作為底層網絡客戶端。而使用OkHttp可以添加攔截器,用來修改即將發出去的請求,這個可以參見《OkHttp之攔截器》。這樣我們就可以在攔截器中,對每個請求添加一個access_token參數了:
private static OkHttpClient.Builder okHttpClientBuilder =
new OkHttpClient.Builder().addInterceptor(new Interceptor() {
@Override
public Response intercept(Chain chain) throws IOException {
Request originalRequest = chain.request();
HttpUrl originalHttpUrl = originalRequest.url();
HttpUrl url = originalHttpUrl.newBuilder()
.addQueryParameter("access_token", AccessTokenKeeper.readAccessToken(MyApplication.getInstance()).getToken())
.build();
Request request = originalRequest.newBuilder()
.url(url)
.method(originalRequest.method(), originalRequest.body())
.build();
return chain.proceed(request);
}
});
首先獲取到了HttpUrl對象,然后基于原始的HttpUrl對象創建一個新的構建器,從而可以使用addQueryPatameter()方法添加額外的查詢參數,最后將這個新的HttpUrl對象通過Request.Builder方法設置到Request中。
請求主體
在我們實際應用中,大多數時候會通過請求主體向服務器發送數據。以我們的慣例,都會以微博API為例,但可惜的是并沒有找過微博使用這種方式的API,而都使用的是表單方式,這個會在后面討論。所以個很常見的例子,那就是登陸,通常請求參數如下:
{
"username":"xxx",
"password":"xxx"
}
好的,登錄的方法定義如下:
@POST("login-path")
Call<User> login(@Body LoginParam param);
其中LoginParam.java類如下:
public class LoginParam {
private String username;
private String password;
public LoginParam(String username, String password) {
this.username = username;
this.password = password;
}
}
首先,我們使用了@Body注解了方法參數,而我們在創建Retrofit.Builder的時候也為其添加了GsonConverter轉換器。
new Retrofit.Builder()
.baseUrl(BASE_URL)
.addConverterFactory(GsonConverterFactory.create());
這樣,Retrofit會將LoginParam對象轉換為Json,并將其作為主體數據添加到請求中,支持了使用請求主體向服務器發送數據。
表單
在上面我們使用了用請求主體的方式來向服務器發送數據,除了這種方式,還可以通過表單編碼(form-urlencoded)的形式。微博API中的寫入接口都是采用這種方式向服務器發送數據的,我們這里以轉發微博為例,具體API詳見http://open.weibo.com/wiki/2/statuses/repost。
首先,定義轉發微博的方法:
@FormUrlEncoded
@POST("statuses/repost.json")
Call<WeiboTimeline> repost(@Field("id") String id);
我們看到了@FormUrlEncoded注解,這個注解不能使用在GET請求上,因為它代表要想服務器發送數據。此外,在參數id上使用@Field注解,表示請求會發送此參數到服務器,而@Field("id")中的id則定義的是參數名稱。
與@Query類似,當你有多個參數要發送時,只需要使用@Field注解它們即可。同樣與@QueryMap對應的有個@FieldMap注解,具體使用類似。
@Field與@FieldMap都有一個屬性encoded,表示鍵值對是否進行url編碼,默認為false。以@Field為例,使用如下:
@FormUrlEncoded
@POST("statuses/repost.json")
Call<WeiboTimeline> repost(@Field(value="id", encoded=true) String id);
了解完@Field之后,我們討論下表單編碼與url參數的區別:表單編碼使用在POST請求中的,而url參數是用在GET請求中的。表單編碼使用請求主體發送數據到服務器,而不是url參數。而url參數的使用主要是為了從服務器過濾或者獲取指定的數據。
Ok,本文就討論到這里,感謝大家的閱讀,下文我們將討論Retrofit如何定義請求頭。
如果你對retrofit感興趣,同時你也覺得我的文章可以給你帶來那么一丟丟的幫助,敬請關注,后續會繼續介紹Retrofit的相關使用。
源碼地址:
https://github.com/FILWAndroid/DevJourney
關于源碼:
- 不只是本文涉及的代碼,會包含很多知識點的代碼,應該都會在我的簡書中進行介紹。
- 有可能代碼與本文中所貼出來的有差異,但應該都是我覺得更好的方式吧。
- 新浪微博相關的代碼運行顯示不出來結果,感興趣的可以參考新浪微博SDK,配置工程。
- 歡迎大家對我進行批評教育。