### 前言
當前的網絡開源庫有許多,如volley,okhttp,retrofit等,這三個庫當前是比較火的,其中,okhttp和retrofit由square團隊開發。關于這三個庫的區別,請移步[stackoverflow](http://stackoverflow.com/questions/16902716/comparison-of-android-networking-libraries-okhttp-retrofit-volley/)或者[知乎](http://www.zhihu.com/question/35189851)查看。開發過程中選擇什么樣的開源庫需要更具我們APP來做出選擇。我們選出stackoverflow中的一段話來看下。

上面說,需要與web service通信的時候,我們使用retrofit。[百度百科 web service介紹](http://baike.baidu.com/link?url=FeGwtLhZW7pjuwc-o8gavLwVe6LRnC4tGK-JAw42uHD6GbwI7ex7NN0Ac3Q17NYnFhDBxHhXMANQp3mjLpCpyq),那么我們見天就來了解下retrofit。
### 什么是retrofit
關于什么是retrofit,[官網文檔](http://square.github.io/retrofit/)上們有一句話。A type-safe HTTP client for Android and Java。額,似乎什么也看出去來,就知道是一個類型安全的http client庫。那么什么是類型安全呢?類型安全代碼指訪問被授權可以訪問的內存位置。例如,類型安全代碼不能從其他對象的私有字段讀取值。它只從定義完善的允許方式訪問類型才能讀取。類型安全的代碼具備定義良好的數據類型。更多內容[百度百科-類型安全](http://baike.baidu.com/link?url=LDmTGCvQfcX8SZB8DZb9GrHK-9uoqf3CadgGjNGjZPVZCIDEVO2uhgDBb4GIB8ktqms3HFLGjO9YVE8H__JHMq),關于這里還引用上面知乎的一句話。

[RESTful-百度百科](http://baike.baidu.com/link?url=07gpSp66HaU7PDIcsUpY7U6wV8ic3zzuL0QrElchvTqHjk4lTILGotJO3BNk_OOc2V-Bn1gjLQ_ay70lg_dHF_)
[rest-百度百科](http://baike.baidu.com/link?url=5FvkIdS0cA_BIKKGhCEb6t8rNQgClAGxx1w6Mwnt4oEUAlvDKbWJmi5GZArR8cUapol07jimkCqF1wQVmYXCYgkOl9DWoWjovULf8SN78AO)
上面這么多抽象的概念太抽象,我們不管他。我們只需要知道retrofit確實是個很好的開源庫就可以了。
### 入門打老虎
開源庫是不錯,但是,你文檔能不能寫的詳細點。入門就是個大老虎。怎么說呢,文檔給出的代碼精辟,但是不能運行。那么,我們來看看具體的,真正的入門步驟。
### 1.在gradle腳本中添加
~~~
compile 'com.squareup.retrofit:retrofit:2.0.0-beta2'
compile 'com.squareup.retrofit:converter-gson:2.0.0-beta2'
~~~

retrofit是中有用的okhttp,添加gson庫是為了將返回數據轉化為實體類。
### 2.將http api轉化為java接口
以下稱這貨為http接口
比如我們想去這個網址上獲取json數據。[https://api.github.com/users/Guolei1130](https://api.github.com/users/Guolei1130)
你們也可以將Guolei1130替換為你們自己的github。
~~~
public interface gitapi {
@GET("/users/{user}")
Call<gitmodel> getFeed(@Path("user") String user);
}
~~~
這里使用注解,@GET表示我們的請求方式是get請求,@GET(“STR”),標明這里的請求地址為BASEURL+STR,{user},這里會在后面被getFeed的user參數替換,最后就拼接成了最終的請求路徑。返回的數據室什么呢。Call< gitmodel>,這里返回的Call是用來讓我們執行請求的。需要注意的是:Call< T>表示返回體中的數據,我們看下上面那個網址的返回數據。

,很明顯是個json對象。
但是如果是返回數據為json數組的話我們就需要注意,這里具體怎么用還要根據返回數據以及我們的model來確定,倘若model對應json中的對象,便用Call< List< gitmodel>>,倘若對應json數組,用Call< gitmodel>。多的不說了,自己體會體會。
### 3.model的編寫
只需要將json對象的鍵值,編寫model對應的成員變量,在生成get set方法就好。
如圖:

### 4.執行請求
~~~
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());
}
});
~~~
這里我們先構造Retrofit對象,由于我們將返回的json數據轉化成了model對象,所以在構造Retrofit對象的時候,通過addConverterFactory來添加轉化器來完成數據轉化。
然后利用http接口的方法生成Call對象,最后用Call對象來執行http請求(異步和同步,后面會說到)。別忘記添加權限。

好,到這里入門老虎就被我們打死了。
### 注解爽歪歪
我們既然知道了retrofit是通過注解將HTTP轉化為java接口,那么我們就需要了解下都有哪些注解,該怎么用。

### 請求方式
GET, POST, PUT, DELETE, 和 HEAD,我們平常開發中經常用的也就get和post。關于HTTP請求方式,這里就不再說了,網上有很多介紹HTTP協議的文章,都特別詳細。
我們知道get請求方式,參數是放在路徑當中的。看下圖的路徑。

,
是不是很長,不過沒關系,用retrofit一樣可以將這么長的串拼接到路徑中,怎么做呢?
這里就用到了@Query(一個鍵值對)和@QueryMap(多對鍵值對)。
~~~
// 假設 baseurl = "http://baidu.com"
@GET("/s")
Call<gitmodel> onekey(@Query("wd") String wdvalue);
~~~
上面代碼拼接出來的 = “[http://baidu.com/s?wd=wdvalue](http://baidu.com/s?wd=wdvalue)“.
上面只是一個參數的時候,很多時候我們有許多參數,這個時候就需要我們使用@QueryMap 了
~~~
Call<gitmodel> manykey(@QueryMap Map<String, String> options);
~~~
上面的即可將多對鍵值對拼接到路徑當中。
### 請求體
我們知道post和get的區別當中有一點就是參數的位置,get放在url路徑當中,post放在請求體當中。
注意:前方高能,請仔細閱讀。前方高能,請仔細閱讀。前方高能,請仔細閱讀。下面我們模擬一個登錄,請求參數為用戶名和密碼,返回參數為我們的用戶名。
#### 1.轉化為接口
~~~
@POST("/index.php")
Call<Des> post(@Body User user);
~~~
#### 2.編寫User和Des,用 來轉化數據
~~~
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.發送請求
~~~
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() );
}
});
~~~
客戶端的代碼很簡單,在這里就不做過多的解釋。重點在于客戶端和服務器之間的交互。媽蛋,坑死我了。官方文檔上有一句話是這樣說的。

意思就是請求體對象也會轉化為json,剛開始我們并不知道會轉化為json,我們還傻傻的在php代碼中$_POST[‘username’],試了幾次沒效果之后。果斷拿出工具來分析。
[抓包神器Charles](http://pan.baidu.com/s/1c08brfq),關于如何使用我這里不說,我這里只說下如何設置android端代理。長按鏈接的網絡-》修改 網絡-》將代理設為手動,輸入IP和端口。如下圖:

。
接下來,我們利用抓包工具去看看服務器接受的數據。如下圖:

,
果然,是json數據,但是我擦,我們明顯感覺這種數據我們沒法通過$_POST來接收。怎么辦呢,不著急,咱們可以通過如下代碼來接收。php完整代碼如下(ps:只是演示,沒幾行)
~~~
<?php
$var=file_get_contents("php://input");
$obj= json_decode($var,true);
$des = $obj['username'];
$arr = array("des"=>$des);
echo json_encode($arr);
~~~
恩,就這么少,關于如下安裝php開發環境就不說了,so easy不是么。最后,我們來看下效果圖:

### 表單編碼和多part
什么叫表單,我想大家都應該知道,表單中有很多元素,我們這里也不例外。在html代碼中,我們經常通過form表單來提交。在上面的請求體中,我們明顯感覺那玩意十分貌似有點難用。不過沒關系,我們有表單。
還是和上個例子一樣。
~~~
@FormUrlEncoded
@POST("/index.php")
Call<Des> form(@Field("username") String username,@Field("password") String password);
~~~
~~~
Call<Des> model = service.form("guolei","123456");
~~~
看下輸出結果;

這個多part是什么呢,就是將請求提分為多個部分,這個就沒啥好說了的。
### 請求頭
我們知道http是有請求頭的,有些時候我們是需要填寫或者配置一下請求頭的,比如說,文件上傳,或者cookie保持。這里的請求頭支持動態配置和靜態配置。
#### 1.靜態配置
通過@Headers注解。如:下面這段是官方文檔上的。
~~~
@Headers({
"Accept: application/vnd.github.v3.full+json",
"User-Agent: Retrofit-Sample-App"
})
~~~
#### 2.動態配置
也來一段官方文檔上面的吧。
~~~
@GET("/user")
Call<User> getUser(@Header("Authorization") String authorization)
~~~
這些都相對簡單的,沒啥好說的,事實上,在開發過程中需要我們配置請求頭的地方也不多。
### 執行方式
這里支持異步和同步。上面的例子中我們都是用的異步。那么我們看下如何執行同步請求,也很簡單。
~~~
//同步請求
//model.execute().body().getLogin();
~~~
### 混淆代碼
~~~
-dontwarn retrofit.**
-keep class retrofit.** { *; }
-keepattributes Signature
-keepattributes Exceptions
~~~
### retrofit+rxjava
在拋物線大大的RxJava入門當中,我們知道了retrofit和rxjava可以結合使用,那么我們現在便來看看如何使用。
### 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.執行
~~~
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());
}
});
~~~
在這里我們要做好線程調度,現在網絡上那些坑爹的代碼,少一行代碼,把我坑了好長時間。喵了個咪的。最后看下效果圖。

### 總結
到這里,還差我們網絡請求的其他需求,比如說文件上傳下載,cookie保持,https協議支持等。在okhttp中,這些東西都可以,retrofit作為okhttp的兄弟,我想也不會太差,還得繼續學習啊。
不過,在查了一些資料之后,我們知道,網絡庫的選擇要根據需求,一個很大的項目中用一個網絡庫很顯然是不可能的。那么,okhttp+retrofit+圖片緩存庫+rxjava會不會成為日后開發的主流呢。還是期待吧。
好累啊,到現在飯都沒吃。retrofit的坑還是有的。我估計剩下的三點地方坑更大,不扯了,收工。
- 前言
- Android四大圖片緩存框架之-Fresco(一)
- Android四大圖片緩存框架之-Fresco之initialize(二)
- Android 四大緩存框架之-Universal-Image-Loader
- Android四大圖片緩存框架之-Picasso和Glide
- Android ORM數據庫框架之-greenDao(一)
- Android ORM數據庫框架之-greenDao(二)
- Android ORM數據庫框架之-greenDao(三)
- Android ORM數據庫框架之-greenDao(四)
- Android 網絡開源庫之-retrofit
- RxJava的簡單學習(學習自扔物線)
- Android ORM框架之-ActiveAndroid的簡單分析