?前言
當(dāng)前的網(wǎng)絡(luò)開(kāi)源庫(kù)有許多茂装,如volley,okhttp善延,retrofit等少态,這三個(gè)庫(kù)當(dāng)前是比較火的,其中易遣,okhttp和retrofit由square團(tuán)隊(duì)開(kāi)發(fā)彼妻。關(guān)于這三個(gè)庫(kù)的區(qū)別,請(qǐng)移步?stackoverflow或者?知乎查看。開(kāi)發(fā)過(guò)程中選擇什么樣的開(kāi)源庫(kù)需要更具我們APP來(lái)做出選擇侨歉。我們選出stackoverflow中的一段話來(lái)看下屋摇。
上面說(shuō),需要與web service通信的時(shí)候幽邓,我們使用retrofit摊册。?百度百科 web service介紹,那么我們見(jiàn)天就來(lái)了解下retrofit颊艳。
?什么是retrofit
關(guān)于什么是retrofit,官網(wǎng)文檔上們有一句話忘分。A type-safe HTTP client for Android and Java棋枕。額,似乎什么也看出去來(lái)妒峦,就知道是一個(gè)類型安全的http client庫(kù)重斑。那么什么是類型安全呢?類型安全代碼指訪問(wèn)被授權(quán)可以訪問(wèn)的內(nèi)存位置肯骇。例如窥浪,類型安全代碼不能從其他對(duì)象的私有字段讀取值。它只從定義完善的允許方式訪問(wèn)類型才能讀取笛丙。類型安全的代碼具備定義良好的數(shù)據(jù)類型漾脂。更多內(nèi)容?百度百科-類型安全,關(guān)于這里還引用上面知乎的一句話胚鸯。
RESTful-百度百科
?rest-百度百科
上面這么多抽象的概念太抽象骨稿,我們不管他。我們只需要知道retrofit確實(shí)是個(gè)很好的開(kāi)源庫(kù)就可以了姜钳。
入門打老虎
開(kāi)源庫(kù)是不錯(cuò)坦冠,但是,你文檔能不能寫(xiě)的詳細(xì)點(diǎn)哥桥。入門就是個(gè)大老虎辙浑。怎么說(shuō)呢,文檔給出的代碼精辟拟糕,但是不能運(yùn)行判呕。那么,我們來(lái)看看具體的已卸,真正的入門步驟佛玄。
1.在gradle腳本中添加
compile 'com.squareup.retrofit:retrofit:2.0.0-beta2'
compile 'com.squareup.retrofit:converter-gson:2.0.0-beta2'
retrofit是中有用的okhttp,添加gson庫(kù)是為了將返回?cái)?shù)據(jù)轉(zhuǎn)化為實(shí)體類累澡。
2.將http api轉(zhuǎn)化為java接口
以下稱這貨為http接口
比如我們想去這個(gè)網(wǎng)址上獲取json數(shù)據(jù)梦抢。https://api.github.com/users/Guolei1130
你們也可以將Guolei1130替換為你們自己的github。
public interface gitapi {
@GET("/users/{user}")
Call<gitmodel> getFeed(@Path("user") String user);
}
這里使用注解愧哟,@GET表示我們的請(qǐng)求方式是get請(qǐng)求奥吩,@GET("STR")哼蛆,標(biāo)明這里的請(qǐng)求地址為BASEURL+STR,{user}霞赫,這里會(huì)在后面被getFeed的user參數(shù)替換腮介,最后就拼接成了最終的請(qǐng)求路徑。返回的數(shù)據(jù)室什么呢端衰。Call< gitmodel>叠洗,這里返回的Call是用來(lái)讓我們執(zhí)行請(qǐng)求的。需要注意的是:Call< T>表示返回體中的數(shù)據(jù)旅东,我們看下上面那個(gè)網(wǎng)址的返回?cái)?shù)據(jù)灭抑。
,很明顯是個(gè)json對(duì)象抵代。
但是如果是返回?cái)?shù)據(jù)為json數(shù)組的話我們就需要注意腾节,這里具體怎么用還要根據(jù)返回?cái)?shù)據(jù)以及我們的model來(lái)確定,倘若model對(duì)應(yīng)json中的對(duì)象荤牍,便用Call< List< gitmodel>>,倘若對(duì)應(yīng)json數(shù)組案腺,用Call< gitmodel>。多的不說(shuō)了康吵,自己體會(huì)體會(huì)劈榨。
3.model的編寫(xiě)
只需要將json對(duì)象的鍵值待秃,編寫(xiě)model對(duì)應(yīng)的成員變量齐媒,在生成get set方法就好油狂。
如圖:
4.執(zhí)行請(qǐng)求
Retrofit retrofit= new Retrofit.Builder()
.baseUrl("https://api.github.com")
.addConverterFactory(GsonConverterFactory.create())
.build();
gitapi service = retrofit.create(gitapi.class);
Call<gitmodel> model = service.getFeed("Guolei1130");
model.enqueue(new Callback<gitmodel>() {
@Override
public void onResponse(Response<gitmodel> response, Retrofit retrofit) {
Log.e(TAG, "onResponse: "+response.body().getLogin() );
}
@Override
public void onFailure(Throwable t) {
Log.e(TAG, "onFailure: " + t.getMessage());
}
});
這里我們先構(gòu)造Retrofit對(duì)象神郊,由于我們將返回的json數(shù)據(jù)轉(zhuǎn)化成了model對(duì)象逛万,所以在構(gòu)造Retrofit對(duì)象的時(shí)候畦戒,通過(guò)addConverterFactory來(lái)添加轉(zhuǎn)化器來(lái)完成數(shù)據(jù)轉(zhuǎn)化季俩。
然后利用http接口的方法生成Call對(duì)象昼蛀,最后用Call對(duì)象來(lái)執(zhí)行http請(qǐng)求(異步和同步棕兼,后面會(huì)說(shuō)到)陡舅。別忘記添加權(quán)限。
好伴挚,到這里入門老虎就被我們打死了靶衍。
?注解爽歪歪
我們既然知道了retrofit是通過(guò)注解將HTTP轉(zhuǎn)化為java接口,那么我們就需要了解下都有哪些注解茎芋,該怎么用颅眶。
請(qǐng)求方式
GET, POST, PUT, DELETE, 和 HEAD,我們平常開(kāi)發(fā)中經(jīng)常用的也就get和post田弥。關(guān)于HTTP請(qǐng)求方式涛酗,這里就不再說(shuō)了,網(wǎng)上有很多介紹HTTP協(xié)議的文章,都特別詳細(xì)商叹。
我們知道get請(qǐng)求方式燕刻,參數(shù)是放在路徑當(dāng)中的∑鼠希看下圖的路徑卵洗。
,
是不是很長(zhǎng)弥咪,不過(guò)沒(méi)關(guān)系过蹂,用retrofit一樣可以將這么長(zhǎng)的串拼接到路徑中,怎么做呢聚至?
這里就用到了@Query(一個(gè)鍵值對(duì))和@QueryMap(多對(duì)鍵值對(duì))榴啸。
// 假設(shè) baseurl = "http://baidu.com"
@GET("/s")
Call<gitmodel> onekey(@Query("wd") String wdvalue);
上面代碼拼接出來(lái)的 = "http://baidu.com/s?wd=wdvalue".
上面只是一個(gè)參數(shù)的時(shí)候,很多時(shí)候我們有許多參數(shù)晚岭,這個(gè)時(shí)候就需要我們使用@QueryMap 了
Call<gitmodel> manykey(@QueryMap Map<String, String> options);
上面的即可將多對(duì)鍵值對(duì)拼接到路徑當(dāng)中。
請(qǐng)求體
我們知道post和get的區(qū)別當(dāng)中有一點(diǎn)就是參數(shù)的位置勋功,get放在url路徑當(dāng)中坦报,post放在請(qǐng)求體當(dāng)中。
注意:前方高能狂鞋,請(qǐng)仔細(xì)閱讀片择。前方高能,請(qǐng)仔細(xì)閱讀骚揍。前方高能字管,請(qǐng)仔細(xì)閱讀。下面我們模擬一個(gè)登錄信不,請(qǐng)求參數(shù)為用戶名和密碼嘲叔,返回參數(shù)為我們的用戶名。
1.轉(zhuǎn)化為接口
@POST("/index.php")
Call<Des> post(@Body User user);
2.編寫(xiě)User和Des抽活,用 來(lái)轉(zhuǎn)化數(shù)據(jù)
public class Des {
public String des;
public String getDes() {
return des;
}
public void setDes(String des) {
this.des = des;
}
}
public class User {
public String username;
public String password;
public User(String username,String password){
this.username = username;
this.password = password;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
}
3.發(fā)送請(qǐng)求
Retrofit retrofit= new Retrofit.Builder()
// .baseUrl("https://api.github.com")
.baseUrl("http://192.168.1.214")
.addConverterFactory(GsonConverterFactory.create())
.build();
gitapi service = retrofit.create(gitapi.class);
Call<Des> model = service.post(new User("guolei","123456"));
model.enqueue(new Callback<Des>() {
@Override
public void onResponse(Response<Des> response, Retrofit retrofit) {
Log.e(TAG, "onResponse: "+response.body().getDes());
}
@Override
public void onFailure(Throwable t) {
Log.e(TAG, "onFailure: "+t.getMessage() );
}
});
客戶端的代碼很簡(jiǎn)單硫戈,在這里就不做過(guò)多的解釋。重點(diǎn)在于客戶端和服務(wù)器之間的交互下硕。媽蛋丁逝,坑死我了。官方文檔上有一句話是這樣說(shuō)的梭姓。
意思就是請(qǐng)求體對(duì)象也會(huì)轉(zhuǎn)化為json霜幼,剛開(kāi)始我們并不知道會(huì)轉(zhuǎn)化為json,我們還傻傻的在php代碼中$_POST['username']誉尖,試了幾次沒(méi)效果之后罪既。果斷拿出工具來(lái)分析。
?抓包神器Charles,關(guān)于如何使用我這里不說(shuō)萝衩,我這里只說(shuō)下如何設(shè)置android端代理回挽。長(zhǎng)按鏈接的網(wǎng)絡(luò)-》修改 網(wǎng)絡(luò)-》將代理設(shè)為手動(dòng),輸入IP和端口猩谊。如下圖:
接下來(lái),我們利用抓包工具去看看服務(wù)器接受的數(shù)據(jù)牌捷。如下圖:
果然,是json數(shù)據(jù)暗甥,但是我擦喜滨,我們明顯感覺(jué)這種數(shù)據(jù)我們沒(méi)法通過(guò)$_POST來(lái)接收。怎么辦呢撤防,不著急虽风,咱們可以通過(guò)如下代碼來(lái)接收。php完整代碼如下(ps:只是演示寄月,沒(méi)幾行)
<?php
$var=file_get_contents("php://input");
$obj= json_decode($var,true);
$des = $obj['username'];
$arr = array("des"=>$des);
echo json_encode($arr);
恩辜膝,就這么少,關(guān)于如下安裝php開(kāi)發(fā)環(huán)境就不說(shuō)了漾肮,so easy不是么厂抖。最后,我們來(lái)看下效果圖:
表單編碼和多part
什么叫表單克懊,我想大家都應(yīng)該知道忱辅,表單中有很多元素,我們這里也不例外谭溉。在html代碼中墙懂,我們經(jīng)常通過(guò)form表單來(lái)提交。在上面的請(qǐng)求體中扮念,我們明顯感覺(jué)那玩意十分貌似有點(diǎn)難用垒在。不過(guò)沒(méi)關(guān)系,我們有表單扔亥。
還是和上個(gè)例子一樣场躯。
@FormUrlEncoded
@POST("/index.php")
Call<Des> form(@Field("username") String username,@Field("password") String password);
Call<Des> model = service.form("guolei","123456");
看下輸出結(jié)果;
這個(gè)多part是什么呢,就是將請(qǐng)求提分為多個(gè)部分旅挤,這個(gè)就沒(méi)啥好說(shuō)了的踢关。
請(qǐng)求頭
我們知道http是有請(qǐng)求頭的,有些時(shí)候我們是需要填寫(xiě)或者配置一下請(qǐng)求頭的粘茄,比如說(shuō)签舞,文件上傳秕脓,或者cookie保持。這里的請(qǐng)求頭支持動(dòng)態(tài)配置和靜態(tài)配置儒搭。
1.靜態(tài)配置
通過(guò)@Headers注解吠架。如:下面這段是官方文檔上的。
@Headers({
"Accept: application/vnd.github.v3.full+json",
"User-Agent: Retrofit-Sample-App"
})
2.動(dòng)態(tài)配置
也來(lái)一段官方文檔上面的吧搂鲫。
@GET("/user")
Call<User> getUser(@Header("Authorization") String authorization)
這些都相對(duì)簡(jiǎn)單的傍药,沒(méi)啥好說(shuō)的,事實(shí)上魂仍,在開(kāi)發(fā)過(guò)程中需要我們配置請(qǐng)求頭的地方也不多拐辽。
執(zhí)行方式
這里支持異步和同步。上面的例子中我們都是用的異步擦酌。那么我們看下如何執(zhí)行同步請(qǐng)求俱诸,也很簡(jiǎn)單。
//同步請(qǐng)求
//model.execute().body().getLogin();
混淆代碼
-dontwarn retrofit.**
-keep class retrofit.** { *; }
-keepattributes Signature
-keepattributes Exceptions
?retrofit+rxjava
在拋物線大大的RxJava入門當(dāng)中赊舶,我們知道了retrofit和rxjava可以結(jié)合使用睁搭,那么我們現(xiàn)在便來(lái)看看如何使用。
1.添加
dependencies {
compile fileTree(dir: 'libs', include: ['*.jar'])
testCompile 'junit:junit:4.12'
compile 'com.android.support:appcompat-v7:23.0.1'
compile 'com.squareup.retrofit:retrofit:2.0.0-beta2'
compile 'com.squareup.retrofit:converter-gson:2.0.0-beta2'
compile 'io.reactivex:rxjava:1.0.16'
compile 'io.reactivex:rxandroid:1.0.1'
compile 'com.squareup.retrofit:adapter-rxjava:2.0.0-beta2'
}
2.http api接口
@FormUrlEncoded
@POST("/index.php")
public Observable<Des> rxpost(@Field("username") String username,@Field("password") String password);
3.在retrofit中添加RxJavaCallAdapterFactory
Retrofit retrofit= new Retrofit.Builder()
// .baseUrl("https://api.github.com")
.addConverterFactory(GsonConverterFactory.create())
.addCallAdapterFactory(RxJavaCallAdapterFactory.create())
.baseUrl("http://192.168.1.214")
.build();
4.執(zhí)行
gitapi service = retrofit.create(gitapi.class);
Observable<Des> observable = service.rxpost("quanshijie", "123456");
observable.observeOn(AndroidSchedulers.mainThread())
.subscribeOn(Schedulers.newThread())
.subscribe(new Action1<Des>() {
@Override
public void call(Des des) {
Log.e(TAG, "call: " + des.getDes().toString());
mText.setText(des.getDes().toString());
}
}, new Action1<Throwable>() {
@Override
public void call(Throwable throwable) {
Log.e(TAG, "call: " + throwable.getLocalizedMessage());
}
});
在這里我們要做好線程調(diào)度笼平,現(xiàn)在網(wǎng)絡(luò)上那些坑爹的代碼介袜,少一行代碼,把我坑了好長(zhǎng)時(shí)間出吹。喵了個(gè)咪的。最后看下效果圖辙喂。
?總結(jié)
到這里捶牢,還差我們網(wǎng)絡(luò)請(qǐng)求的其他需求,比如說(shuō)文件上傳下載巍耗,cookie保持秋麸,https協(xié)議支持等。在okhttp中炬太,這些東西都可以灸蟆,retrofit作為okhttp的兄弟,我想也不會(huì)太差亲族,還得繼續(xù)學(xué)習(xí)啊炒考。
不過(guò),在查了一些資料之后霎迫,我們知道斋枢,網(wǎng)絡(luò)庫(kù)的選擇要根據(jù)需求,一個(gè)很大的項(xiàng)目中用一個(gè)網(wǎng)絡(luò)庫(kù)很顯然是不可能的知给。那么瓤帚,okhttp+retrofit+圖片緩存庫(kù)+rxjava會(huì)不會(huì)成為日后開(kāi)發(fā)的主流呢描姚。還是期待吧。
好累啊戈次,到現(xiàn)在飯都沒(méi)吃轩勘。retrofit的坑還是有的。我估計(jì)剩下的三點(diǎn)地方坑更大怯邪,不扯了绊寻,收工。