在上文吩抓,我們了解了如何定義請(qǐng)求Url朵纷,感興趣的朋友可以參見(jiàn)《Retrofit之請(qǐng)求Url》传趾。Retrofit系列持續(xù)更新,本文介紹如何使用Retrofit定義請(qǐng)求參數(shù)负蚊。
請(qǐng)求參數(shù)從傳遞方式可分三種:url參數(shù)神妹,請(qǐng)求主體以及表單編碼。我們來(lái)一一討論家妆。
url參數(shù)
單個(gè)請(qǐng)求參數(shù)
url參數(shù)就是在url鏈接后面的鍵值對(duì)鸵荠,例如https://api.weibo.com/2//statuses/public_timeline.json?access_token=xxx中,access_token就是url參數(shù)伤极,xxx為其值腰鬼。
Retrofit定義url參數(shù)非常直接,只要在方法參數(shù)前面添加@Query("key")注解即可塑荒。@Query中key的值與url中的參數(shù)名稱是一致的熄赡,Retrofit會(huì)自動(dòng)添加這些參數(shù)到url中。
以之前獲取微博公共動(dòng)態(tài)的API為例齿税,具體API詳見(jiàn)http://open.weibo.com/wiki/2/statuses/public_timeline彼硫。從接口中看到,必選參數(shù)只有access_token一個(gè)凌箕,我們定義個(gè)方法如下:
@GET("/statuses/public_timeline.json")
Call<Timeline> timelineForPublic(@Query("access_token") String token);
方法timelineForPublic需要參數(shù)token拧篮,Retrofit會(huì)通過(guò)@Query中定義的名稱access_token將token映射成請(qǐng)求參數(shù)access_token。此時(shí)牵舱,請(qǐng)求url就會(huì)變成:
/statuses/public_timeline.json?access_token=<token>
多個(gè)請(qǐng)求參數(shù)
從獲取微博公共動(dòng)態(tài)的API中我們可以看到串绩,除了必選的access_token,還有三個(gè)可選參數(shù)count芜壁、page以及base_app礁凡,也就是說(shuō)現(xiàn)在請(qǐng)求參數(shù)有四個(gè)了高氮。有了上面定義請(qǐng)求參數(shù)的介紹,我們只需要往方法上添加相應(yīng)的參數(shù)并用@Query進(jìn)行注解:
@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);
此時(shí)顷牌,如果我們只需要傳遞access_token剪芍,而不需要其他參數(shù),則可以在調(diào)用方法的時(shí)候傳null窟蓝。當(dāng)然罪裹,我們不能傳null給int這樣的原生類(lèi)型,而需要使用對(duì)應(yīng)的Integer运挫。而Retrofit會(huì)跳過(guò)值為null的參數(shù)状共,并在組裝請(qǐng)求的時(shí)候忽略它們。
獲取公共微博最多有四個(gè)參數(shù)谁帕,那么我們?cè)俨榭聪芦@取好友微博APIhttp://open.weibo.com/wiki/2/statuses/friends_timeline口芍,發(fā)現(xiàn)其有一個(gè)必選參數(shù)以及七個(gè)可選參數(shù),也就是方法會(huì)有八個(gè)參數(shù)雇卷。那么問(wèn)題來(lái)了鬓椭,如果有更多的可選參數(shù),那方法的參數(shù)是不是也會(huì)特別多关划,而且很多時(shí)候我們只需要其中一兩個(gè)請(qǐng)求參數(shù)小染,卻需要提供所有的參數(shù)值,顯然很繁瑣贮折。為了處理這種情況裤翩,QueryMap就該登場(chǎng)了。
我們可以使用下面的方式定義獲取公共微博API的方法:
@GET("/statuses/public_timeline.json")
Call<Timeline> timelineForPublic(@QueryMap Map<String, String> options);
@QueryMap后面需要緊跟著一個(gè)Map< String, String >類(lèi)型调榄,這樣就可以動(dòng)態(tài)地添加查詢參數(shù)了踊赠。如果說(shuō)只需要accept_token參數(shù),則可以像下面這樣調(diào)用:
Map<String, String> options = new HashMap<>();
options.put("access_token", AccessTokenKeeper.readAccessToken(getContext()).getToken());
call = weiboService.timelineForPublic(options);
這樣每庆,我們只需要傳遞我們需要設(shè)置的參數(shù)就可以了筐带。
給每個(gè)請(qǐng)求添加url參數(shù)
在我們查看微博的各個(gè)API時(shí),會(huì)發(fā)現(xiàn)每個(gè)請(qǐng)求都需要一個(gè)access_token參數(shù)缤灵,于是我們就在所有的方法中都添加了對(duì)應(yīng)的參數(shù)伦籍。那么有沒(méi)有簡(jiǎn)單的方式來(lái)給每個(gè)請(qǐng)求都添加相同的參數(shù),從而不需要每個(gè)請(qǐng)求都做相同處理呢腮出?
強(qiáng)大的Retrofit是支持的帖鸦,但是是通過(guò)OkHttp中的攔截器來(lái)實(shí)現(xiàn)的。我們?cè)?a href="http://www.reibang.com/p/511a6266e656" target="_blank">《Retrofit之初體驗(yàn)》提及過(guò)胚嘲,Retrofit直接依賴OkHttp作儿,使用OkHttp作為底層網(wǎng)絡(luò)客戶端。而使用OkHttp可以添加攔截器馋劈,用來(lái)修改即將發(fā)出去的請(qǐng)求攻锰,這個(gè)可以參見(jiàn)《OkHttp之?dāng)r截器》晾嘶。這樣我們就可以在攔截器中,對(duì)每個(gè)請(qǐng)求添加一個(gè)access_token參數(shù)了:
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對(duì)象口注,然后基于原始的HttpUrl對(duì)象創(chuàng)建一個(gè)新的構(gòu)建器,從而可以使用addQueryPatameter()方法添加額外的查詢參數(shù)君珠,最后將這個(gè)新的HttpUrl對(duì)象通過(guò)Request.Builder方法設(shè)置到Request中寝志。
請(qǐng)求主體
在我們實(shí)際應(yīng)用中,大多數(shù)時(shí)候會(huì)通過(guò)請(qǐng)求主體向服務(wù)器發(fā)送數(shù)據(jù)策添。以我們的慣例材部,都會(huì)以微博API為例,但可惜的是并沒(méi)有找過(guò)微博使用這種方式的API唯竹,而都使用的是表單方式乐导,這個(gè)會(huì)在后面討論。所以個(gè)很常見(jiàn)的例子浸颓,那就是登陸物臂,通常請(qǐng)求參數(shù)如下:
{
"username":"xxx",
"password":"xxx"
}
好的,登錄的方法定義如下:
@POST("login-path")
Call<User> login(@Body LoginParam param);
其中LoginParam.java類(lèi)如下:
public class LoginParam {
private String username;
private String password;
public LoginParam(String username, String password) {
this.username = username;
this.password = password;
}
}
首先产上,我們使用了@Body注解了方法參數(shù)棵磷,而我們?cè)趧?chuàng)建Retrofit.Builder的時(shí)候也為其添加了GsonConverter轉(zhuǎn)換器。
new Retrofit.Builder()
.baseUrl(BASE_URL)
.addConverterFactory(GsonConverterFactory.create());
這樣晋涣,Retrofit會(huì)將LoginParam對(duì)象轉(zhuǎn)換為Json仪媒,并將其作為主體數(shù)據(jù)添加到請(qǐng)求中,支持了使用請(qǐng)求主體向服務(wù)器發(fā)送數(shù)據(jù)谢鹊。
表單
在上面我們使用了用請(qǐng)求主體的方式來(lái)向服務(wù)器發(fā)送數(shù)據(jù)算吩,除了這種方式,還可以通過(guò)表單編碼(form-urlencoded)的形式佃扼。微博API中的寫(xiě)入接口都是采用這種方式向服務(wù)器發(fā)送數(shù)據(jù)的偎巢,我們這里以轉(zhuǎn)發(fā)微博為例,具體API詳見(jiàn)http://open.weibo.com/wiki/2/statuses/repost兼耀。
首先艘狭,定義轉(zhuǎn)發(fā)微博的方法:
@FormUrlEncoded
@POST("statuses/repost.json")
Call<WeiboTimeline> repost(@Field("id") String id);
我們看到了@FormUrlEncoded注解,這個(gè)注解不能使用在GET請(qǐng)求上翠订,因?yàn)樗硪敕?wù)器發(fā)送數(shù)據(jù)巢音。此外,在參數(shù)id上使用@Field注解尽超,表示請(qǐng)求會(huì)發(fā)送此參數(shù)到服務(wù)器官撼,而@Field("id")中的id則定義的是參數(shù)名稱。
與@Query類(lèi)似似谁,當(dāng)你有多個(gè)參數(shù)要發(fā)送時(shí)傲绣,只需要使用@Field注解它們即可掠哥。同樣與@QueryMap對(duì)應(yīng)的有個(gè)@FieldMap注解,具體使用類(lèi)似秃诵。
@Field與@FieldMap都有一個(gè)屬性encoded续搀,表示鍵值對(duì)是否進(jìn)行url編碼,默認(rèn)為false菠净。以@Field為例禁舷,使用如下:
@FormUrlEncoded
@POST("statuses/repost.json")
Call<WeiboTimeline> repost(@Field(value="id", encoded=true) String id);
了解完@Field之后,我們討論下表單編碼與url參數(shù)的區(qū)別:表單編碼使用在POST請(qǐng)求中的毅往,而url參數(shù)是用在GET請(qǐng)求中的牵咙。表單編碼使用請(qǐng)求主體發(fā)送數(shù)據(jù)到服務(wù)器,而不是url參數(shù)攀唯。而url參數(shù)的使用主要是為了從服務(wù)器過(guò)濾或者獲取指定的數(shù)據(jù)洁桌。
Ok,本文就討論到這里侯嘀,感謝大家的閱讀另凌,下文我們將討論Retrofit如何定義請(qǐng)求頭。
如果你對(duì)retrofit感興趣戒幔,同時(shí)你也覺(jué)得我的文章可以給你帶來(lái)那么一丟丟的幫助途茫,敬請(qǐng)關(guān)注,后續(xù)會(huì)繼續(xù)介紹Retrofit的相關(guān)使用溪食。
源碼地址:
https://github.com/FILWAndroid/DevJourney
關(guān)于源碼:
- 不只是本文涉及的代碼囊卜,會(huì)包含很多知識(shí)點(diǎn)的代碼,應(yīng)該都會(huì)在我的簡(jiǎn)書(shū)中進(jìn)行介紹错沃。
- 有可能代碼與本文中所貼出來(lái)的有差異栅组,但應(yīng)該都是我覺(jué)得更好的方式吧。
- 新浪微博相關(guān)的代碼運(yùn)行顯示不出來(lái)結(jié)果枢析,感興趣的可以參考新浪微博SDK玉掸,配置工程。
- 歡迎大家對(duì)我進(jìn)行批評(píng)教育醒叁。