原來公司用的是OKGO來加載網絡,現在全部替換為Retrofit了,用起來挺不適應的,現在我負責的模塊代碼中網絡數據請求都是照葫蘆畫瓢搬過其他人的接口代碼改成自己的。至于為什么按照這種格式寫?這么寫有什么好處?有沒有其他的寫法?懵逼了!因為之前沒接觸過Retrofit這東西,現在想著抽點時間好好研究一下。
Retrofit是什么?
Retrofit其實我們可以理解為OkHttp的加強版,它也是一個網絡加載框架。底層是使用OKHttp封裝的。準確來說,網絡請求的工作本質上是OkHttp完成,而 Retrofit 僅負責網絡請求接口的封裝。它的一個特點是包含了特別多注解,方便簡化你的代碼量。并且還支持很多的開源庫(著名例子:Retrofit + RxJava)。
還想說一點題外話,Retrofit和OkHttp(我們公司用到的OKGO框架也是封裝人家的OkHttp)都是square公司(前一篇我寫的簡書文章Dragger也是他們的,我擦,真是大佬!)
Retrofit的好處?
- 超級解耦
解耦?解什么耦?
我們在請求接口數據的時候,API接口定義和API接口使用總是相互影響,什么傳參、回調等,耦合在一塊。有時候我們會考慮一下怎么封裝我們的代碼讓這兩個東西不那么耦合,這個就是Retrofit的解耦目標,也是它的最大的特點。
Retrofit為了實現解耦,使用了特別多的設計模式,這里附上一片很好的文章,里面講的就是實現原理:
Retrofit分析-漂亮的解耦套路 - 可以配置不同HttpClient來實現網絡請求,如OkHttp、HttpClient...
- 支持同步、異步和RxJava
- 可以配置不同的反序列化工具來解析數據,如json、xml...
- 請求速度快,使用非常方便靈活
Retrofit注解
- 請求方法
注解代碼 | 請求格式 |
---|---|
@GET | GET請求 |
@POST | POST請求 |
@DELETE | DELETE請求 |
@HEAD | HEAD請求 |
@OPTIONS | OPTIONS請求 |
@PATCH | PATCH請求 |
- 請求參數
注解代碼 | 說明 |
---|---|
@Headers | 添加請求頭 |
@Path | 替換路徑 |
@Query | 替代參數值,通常是結合get請求的 |
@FormUrlEncoded | 用表單數據提交 |
@Field | 替換參數值,是結合post請求的 |
下面我們詳細說說這些注解
我們先來看一段Retrofit請求的簡單用法
- 添加依賴
由于Retrofit是基于OkHttp,所以還需要添加OkHttp庫依賴
在build.grale添加如下依賴:
dependencies {
// Okhttp庫
compile 'com.squareup.okhttp3:okhttp:3.1.2'
// Retrofit庫
compile 'com.squareup.retrofit2:retrofit:2.0.2'
}
- 添加網絡權限
<uses-permission android:name="android.permission.INTERNET"/>
- 創建接收服務器返回數據的類
public class News {
// 根據返回數據的格式和數據解析方式(Json、XML等)定義
...
}
- 創建用于描述網絡請求的接口
public interface APi {
// @GET注解的作用:采用Get方法發送網絡請求
// getNews(...) = 接收網絡請求數據的方法
// 其中返回類型為Call<News>,News是接收數據的類(即上面定義的News類)
// 如果想直接獲得Responsebody中的內容,可以定義網絡請求返回值為Call<ResponseBody>
@Headers("apikey:81bf9da930c7f9825a3c3383f1d8d766")
@GET("word/word")
Call<News> getNews(@Query("num") String num,@Query("page")String page);
}
這一塊知識點很多,做好筆記了!
①Retrofit將Http請求抽象成Java接口,并在接口里面采用注解來配置網絡請求參數。用動態代理將該接口的注解“翻譯”成一個Http請求,最后再執行 Http請求
注意: 接口中的每個方法的參數都需要使用注解標注,否則會報錯
②APi接口中的最后一個注釋,Responsebody是Retrofit網絡請求回來的原始數據類,沒經過Gson轉換什么的,如果你不想轉換,比如我就想看看接口返回的json字符串,那就像注釋中說的,把Call的泛型定義為ResponseBody:Call<ResponseBody>
③GET注解
說白了就是我們的GET請求方式。
這里涉及到Retrofit創建的一些東西,Retrofit在創建的時候,有一行代碼:
baseUrl("http://apis.baidu.com/txapi/")
這個http://apis.baidu.com/txapi/是我們要訪問的接口的BaseUrl,而我們現在用GET注解的字符串 "word/word"會追加到BaseUrl中變為:http://apis.baidu.com/txapi/world/world
在我們日常開發中,BaseUrl具體是啥由后端接口童鞋給出,之后接口童鞋們會出各種各種的后綴(比如上面的 "word/word")組成各種各行的接口用來供移動端數據調用,實現各種各樣的功能
④@Query
簡單點來說呢
@Query("num")String num, @Query("page")String page;
就是鍵值對,Retrofit會把這兩個字段一塊拼接到接口中,追加到http://apis.baidu.com/txapi/world/world后面,變為http://apis.baidu.com/txapi/world/world?num=10&page=1,這樣,這個帶著響應頭的接口就是我們最終請求網絡的完整接口。
這里補充一點哈,GET請求方式,如果攜帶的參數不是以
?num=10&page=1
拼接到接口中(就是不帶?分隔符),那就不用Query注解了,而是使用Path注解,像我們項目中的Get請求:
@GET(URL.CLAIM_APPLICATION_BOOKINFO + "{claimId}")
Observable<PublicResponseEntity<ClaimApplicationBookInfo>> getClaimApplicationBookInfo(@Header("Authorization") String authorization, @Path("claimId") String claimId);
上面的GET注解的接口通過{}占位符來標記的claimId,就用@Path注解在傳入claimId的值。
@Query與@Path功能相同,但區別明顯不一樣。像@Query的例子,我如果使用@Path來注解,那么程序就會報錯。這塊要搞清楚!
還有一點哈,有的url既有“{}”占位符,又有“?”后面的鍵值對(key-value),那Retrofit既得使用@Query注解又得使用@Path注解,也就是說,兩者可以同時使用。
⑤@Headers
@Headers("apikey:81bf9da930c7f9825a3c3383f1d8d766")
這個很好理解,這個接口需要添加的header:
apikey:81bf9da930c7f9825a3c3383f1d8d766
@Headers就是把接口的header注解進去。還有很多添加header的方式,比如:
public interface APi {
@GET("word/word")
Call<News> getNews(@Header("apikey")String apikey, @Query("num")String num, @Query("page")String page);
}
這個就是在代碼中動態的添加header,用法如下:
Call<News> news = mApi.getNews("81bf9da930c7f9825a3c3383f1d8d766", "1", "10");
關于header的其他添加方式,大家可以看看下面的文章:
Retrofit之請求頭
這里再補充一點:@Header與@Headers的區別
舉個例子:
//@Header
@GET("user")
Call<User> getUser(@Header("Authorization") String authorization)
//@Headers
@Headers("Authorization:authorization")
@GET("user")
Call<User> getUser()
以上兩個方法的效果是一致的。
區別就在于使用場景和使用方式
使用場景:@Header用于添加不固定的請求頭,@Headers用于添加固定的請求頭
使用范圍:@Header作用于方法的參數;@Headers作用于方法
- 創建Retrofit對象
Retrofit retrofit = new Retrofit.Builder()
//設置數據解析器
.addConverterFactory(GsonConverterFactory.create())
//設置網絡請求的Url地址
.baseUrl("http://apis.baidu.com/txapi/")
.build();
// 創建網絡請求接口的實例
mApi = retrofit.create(APi.class);
這一塊知識點有三個:
①此處特意說明一下這個網絡請求的URL的組成:Retrofit把網絡請求的URL 分成了兩部分設置:
第一部分:在創建Retrofit實例時通過.baseUrl()設置,就是上面的
.baseUrl("http://apis.baidu.com/txapi/")
第二部分:在網絡請求接口的注解設置,就是在上面的APi接口中用GET注解的字符串:
@GET("word/word")
Retrofit的網絡請求的完整Url = 創建Retrofit實例時通過.baseUrl()設置的url
+網絡請求接口的注解設置(下面稱 “path“ )
(第四種類型存疑)
建議采用第三種方式來配置,并盡量使用同一種路徑形式。
②關于數據解析器(Converter)
//設置數據解析器
.addConverterFactory(GsonConverterFactory.create())
這個有啥用?這句話的作用就是使得來自接口的json結果會自動解析成定義好了的字段和類型都相符的json對象接受類。在Retrofit 2.0中,Package 中已經沒有Converter了,所以,你需要自己創建一個Converter, 不然的話Retrofit只能接收字符串結果,你也只能拿到一串字符,剩下的json轉換的活還得你自己來干。所以,如果你想接收json結果并自動轉換成解析好的接收類,必須自己創建Converter對象,然后使用addConverterFactory把它添加進來!
Retrofit支持多種數據解析方式,在使用時注意需要在Gradle添加依賴:
數據解析器 | Gradle依賴 |
---|---|
Gson | com.squareup.retrofit2:converter-gson:2.0.2 |
Jackson | com.squareup.retrofit2:converter-jackson:2.0.2 |
Simple XML | com.squareup.retrofit2:converter-simplexml:2.0.2 |
Protobuf | com.squareup.retrofit2:converter-protobuf:2.0.2 |
Moshi | com.squareup.retrofit2:converter-moshi:2.0.2 |
Wire | com.squareup.retrofit2:converter-wire:2.0.2 |
Scalars | com.squareup.retrofit2:converter-scalars:2.0.2 |
像上面代碼中,就是使用了第一種Gson數據解析器
③再來引入另一個方法:addCallAdapterFactory()
上文代碼中沒有這個方法,但是得知道它的作用,有必要作為補充知識點
看一下我們的接口返回:
Call<News> news = mApi.getNews("1", "10");
返回的Call<News>可以理解成源生的了,默認就這么寫。但像很多很多項目都是結合著RXJava來使用這個Retrofit的,那么這個接口返回就會被定義為(偽代碼):
Observable<News> news = mApi.getNews("1", "10").subscribeOn(...).observeOn(...);
它返回的是一個Observable類型(觀察者模式)。從上面可以看到,Retrofit接口的返回值可以分為兩部分,第一部分是返回值類型:Call或者Observable,另一部分是泛型:News
addCallAdapterFactory()影響的就是第一部分:Call或者Observable。Call類型是Retrofit默認支持的(Retrofit內部有一個DefaultCallAdapterFactory),所以你如果不用RXJava + Retrofit結合使用,那就自動忽略掉這個方法,而如果你想要支持RXJava(就是想把返回值定義為Observable對象),就需要我們自己用addCallAdapterFactory()添加:
addCallAdapterFactory(RxJavaCallAdapterFactory.create())
像我們項目中Retrofit創建的代碼就是:
retrofit = new Retrofit.Builder()
.baseUrl(URL.SERVICE_URL)
.addCallAdapterFactory(RxJava2CallAdapterFactory.create())
.addConverterFactory(GsonConverterFactory.create())
.client(okHttpClient)
.build();
同理,Retrofit不光支持多種數據解析器,也支持多種網絡請求適配器:Guava、Java8、RXJava ,使用時也需要在Gradle添加依賴:
網絡請求適配器 | Gradle依賴 |
---|---|
Guava | com.squareup.retrofit2:adapter-guava:2.0.2 |
Java8 | com.squareup.retrofit2:adapter-java8:2.0.2 |
RXJava | com.squareup.retrofit2:adapter-rxjava:2.0.2 |
像上面的代碼,就是用的RXJava
- 發起網絡請求
//對發送請求進行封裝
Call<News> news = mApi.getNews("1", "10");
//發送網絡請求(異步)
news.enqueue(new Callback<News>() {
//請求成功時回調
@Override
public void onResponse(Call<News> call, Response<News> response) {
//請求處理,輸出結果-response.body().show();
}
@Override
public void onFailure(Call<News> call, Throwable t) {
//請求失敗時候的回調
}
});
上面是一個簡單的GET請求的全過程。
補充一點,Retrofit還有個發起同步網絡請求的方式:
//對發送請求進行封裝
Call<News> news = mApi.getNews("1", "10");
//發送網絡請求(同步)
Response<Reception> response = news.execute();
接下來我們看下POST請求
Retrofit的POST請求
POST請求與GET請求算是我們日常開發中最最常用的兩種網絡訪問方式,Retrofit的POST請求在用法上與GET區別不算大。
拿我早期寫過的一個比較不合格的代碼舉個例子就能看出來(先聲明,這種寫法是不合格的,但是接口能跑通,看下去你就知道了):
①首先都是定義一個API接口:
public interface IServiceApi {
@POST("/claims/preclaims")
Observable<PublicResponseEntity<PreclaimsResponseEntity>> postClaimPreclaims(@Header("Authorization") String authorization, @QueryMap HashMap<String, String> deviceInfo, @Body RequestBody body);
}
拿出筆記啦(因為大部分都和GET請求一樣,所以這里講的就簡單點)
①和GET請求相比,流程的開頭都是創建了一個API的接口,然后用@POST注釋,指定了對應的接口地址,我的返回值需要把獲取到的Json字符串轉成PublicResponseEntity<PreclaimsResponseEntity>,所以方法返回值要寫成Call<PublicResponseEntity<PreclaimsResponseEntity>>
但是我項目中用到的是RxJava + Retrofit,所以把返回值定義為了Observable<PublicResponseEntity<PreclaimsResponseEntity>>
②方法中的第一個參數:我是在代碼中動態的添加了一個header,這沒啥可說的,上面的GET請求中說完了已經,看第二個。
③方法中的第二個參數:通過@QueryMap往接口中注解很多個參數,看到這里很容易聯想到@Query,在上面的GET請求中@Query是一個一個往接口中注入參數的,而@QueryMap從名字也能看出來,如果Query參數比較多,那么可以通過@QueryMap方式將所有的參數集成在一個Map統一傳遞。
③第三個參數:通過@Body注解了一個RequestBody,
好!又出來一個新的注解@Body,它的源碼中對他的注釋大體意思是:使用這個注解可以把參數放到請求體中,適用于 POST/PUT請求,一臉懵逼呀,只知道它適用于對于POST/PUT。
其實,@Body可以注解很多東西的,HashMap、實體類等,例如:
public interface IServiceApi {
@POST("/claims/preclaims")
Observable<Item> postClaimUser(@Body User user);
}
那這么一看,@Body和@QueryMap差別不是很大哈,都可以對很多參數進行封裝傳遞。話是這么說,但是它倆還是有差別的:
@QueryMap注解會把參數拼接到url后面,所以它適用于GET請求;
@Body會把參數放到請求體中,所以適用于POST請求。
如果你的項目是采用POST請求方式,不管是使用實體類還是使用HashMap最好采用@Body注解。雖然你使用QueryMap 可能也不會有什么問題(PS:這種共用的情況只適用于POST請求,GET請求不能使用@Body注解,否則會報錯),就像上面我的不合格代碼一樣,POST請求中一直采用@QueryMap,雖然也能拿到接口數據,但是這么寫是不合格的。
引以為戒吧~~
接下來就是調用了:
一樣的創建Retrofit對象
Retrofit retrofit = new Retrofit.Builder()
.baseUrl(URL.SERVICE_URL)
.addCallAdapterFactory(RxJava2CallAdapterFactory.create())
.addConverterFactory(GsonConverterFactory.create())
.client(okHttpClient)
.build();
// 創建網絡請求接口的實例
IServiceApi mApi = retrofit.create(IServiceApi .class);
一樣的發起網絡請求:
//對發送請求進行封裝
Observable<PublicResponseEntity<PreclaimsResponseEntity>> news = mApi.postClaimPreclaims("你的Header信息", "你要傳到接口中的HashMap參數", "你的實體類");
//發送網絡請求(異步)
news.enqueue(new Callback<News>() {
//請求成功時回調
@Override
public void onResponse(Call<News> call, Response<News> response) {
//請求處理,輸出結果-response.body().show();
}
@Override
public void onFailure(Call<News> call, Throwable t) {
//請求失敗時候的回調
}
});
OK,到這里,你就能成功拿到一次POST請求的數據了。
這一塊我講的比較少,因為我也是在邊寫邊學,不會的不敢寫,先把掌握到的寫下來做個積累,以后慢慢把學到的東西補充進來。
Retrofit下載文件
其實用Retrofit下載文件方式與其他請求幾乎無異,拿我用到下載PDF的程序來舉例子
step1:編寫API,執行下載接口功能
public interface IServiceApi {
····
//PDF文件Retrofit下載
@Streaming
@GET
Observable<ResponseBody> retrofitDownloadFile(@Url String fileUrl);
...
}
上面的代碼有幾個注意的點:
①@Streaming 是注解大文件的,小文件可以忽略不加注釋,但是大文件一定需要注釋,不然會出現OOM。
②fileUrl就是PDF的下載地址,通過參數形式傳進來
③正常來講,API接口的返回類型是Call<ResponseBody>,即:
public interface IServiceApi {
····
//PDF文件Retrofit下載
@Streaming
@GET
Call<ResponseBody> retrofitDownloadFile(@Url String fileUrl);
...
}
但是我項目中是Retrofit結合RXJava來使用的,我把它的返回值類型定義為Observable<ResponseBody>,強烈推薦這種寫法,便利于后續的數據處理
step2:實現一個下載管理工具
它的作用有很多:寫入文件、判斷文件類型、計算文件大小...當然最主要的還是用來把下載下來的文件寫入本地
public class DownLoadManager {
//Log標記
private static final String TAG = "eeeee";
//APK文件類型
private static String APK_CONTENTTYPE = "application/vnd.android.package-archive";
//PNG文件類型
private static String PNG_CONTENTTYPE = "image/png";
//JPG文件類型
private static String JPG_CONTENTTYPE = "image/jpg";
//文件后綴名
private static String fileSuffix="";
/**
* 寫入文件到本地
* @param file
* @param body
* @return
*/
public static boolean writeResponseBodyToDisk(File file, ResponseBody body) {
Log.d(TAG, "contentType:>>>>" + body.contentType().toString());
//下載文件類型判斷,并對fileSuffix賦值
String type = body.contentType().toString();
if (type.equals(APK_CONTENTTYPE)) {
fileSuffix = ".apk";
} else if (type.equals(PNG_CONTENTTYPE)) {
fileSuffix = ".png";
}
// 其他類型同上 需要的判斷自己加入.....
//下面就是一頓寫入,文件寫入的位置是通過參數file來傳遞的
InputStream is = null;
byte[] buf = new byte[2048];
int len = 0;
FileOutputStream fos = null;
try {
is = body.byteStream();
long total = body.contentLength();
fos = new FileOutputStream(file);
long sum = 0;
while ((len = is.read(buf)) != -1) {
fos.write(buf, 0, len);
sum += len;
int progress = (int) (sum * 1.0f / total * 100);
}
fos.flush();
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
} finally {
try {
if (is != null)
is.close();
} catch (IOException e) {
}
try {
if (fos != null)
fos.close();
} catch (IOException e) {
}
}
}
}
上面的代碼沒啥需要注意的點,都挺基本的Android代碼,硬要說的話,就注意一下ResponseBody這個類吧,導包的時候有很多,別導錯了,要導入OKHTTP的包,因為Retrofit的底層就是OKHTTP,當時我在這里手快導錯了,蒙了一下。
step3:接下來就是調用API下載寫入文件了
我項目結構是MVVM,又使用了RXJava和Dagger2,看起來代碼寫的簡單沒多少,但是沒用過的可能看不懂。那這里貼兩份代碼,一份我項目中的代碼(強烈推薦這種寫法,Retrofit結合RXJava來用不僅解決了線程安全問題而且特別簡單),一份是OKHTTP原始的代碼
項目是MVVM的,為下載的Activity創建一個ViewModel來執行下載的耗時操作,并在Activity中用Dagger2來注入該ViewModel對象
public class ElectronicImageSynthesisViewModel {
private IServiceApi mServiceApi;
@Inject
public ElectronicImageSynthesisViewModel(IServiceApi serviceApi){
mServiceApi = serviceApi;
}
//下載PDF文件
public Observable<ResponseBody> retrofitDownloadFile(String fileUrl){
return mServiceApi.retrofitDownloadFile(fileUrl).subscribeOn(Schedulers.io()).observeOn(AndroidSchedulers.mainThread());
}
}
上面代碼中注意一點(RXJava的內容)
要指定下載的線程與數據返回的線程:
subscribeOn(Schedulers.io()):在io線程中下載文件
observeOn(AndroidSchedulers.mainThread()):在UI線程中處理返回結果
public class ElectronicImageSynthesisActivity extends BaseActivity {
...
@Inject
ElectronicImageSynthesisViewModel mElectronicImageSynthesisViewModel;
/**
* pdf下載
*/
private void pdfDownLoad() {
mElectronicImageSynthesisViewModel.retrofitDownloadFile(mPDFDownloadUrl)
.map(new Function<ResponseBody, Boolean>() {
@Override
public Boolean apply(ResponseBody responseBody) throws Exception {
return DownLoadManager.writeResponseBodyToDisk(mPDFSavedFile, responseBody);
}
}).subscribe(new Consumer<Boolean>() {
@Override
public void accept(Boolean aBoolean) throws Exception {
if (aBoolean) {
//這一步就是對你下載下來的文件進行你想要的操作了,我這里是展示PDF
displayFromFile(mPDFSavedFile);
}
}
}, new Consumer<Throwable>() {
@Override
public void accept(Throwable throwable) throws Exception {
//onError
showToast(throwable.getMessage());
}
});
}
...
}
上面代碼是Retrofit和RXJava結合來使用的,代碼看起來沒多少行,很簡潔。最讓我喜歡的是它的鏈式結構,邏輯一目了然,而不是以往那種層層遞進。
pdfDownLoad()的代碼有很多東西需要講,因為它涉及到RXJava了,我們這里重點是Retrofit下載,只挑一些重點來說明一下:
①.首先它調用了ViewModel里面的mElectronicImageSynthesisViewModel(),并給它傳遞了一個PDF的下載鏈接mPDFDownloadUrl
②.mElectronicImageSynthesisViewModel()方法返回的就是攜帶下載數據的Observable<ResponseBody>
③.這個Observable在RXJava中叫做被觀察者,它的泛型就是下載的數據:ResponseBody,現在我們通過retrofitDownloadFile()返回了它,那么我們需要做的就是把它寫入到手機本地
④.writeResponseBodyToDisk()就上場了,把我們定義的想要存儲到手機哪里的文件File給它傳遞進去。我們只需要做的就是根據writeResponseBodyToDisk()返回的boolean值來判斷文件到底寫沒寫入成功:true-寫入成功,false-寫入失敗。
⑤.這里就涉及到一個類型轉換了,我們拿到的是ResponseBody,想要的卻是寫入成功與否的標記,RXJava就給提供了一個操作符:map
⑥.使用map操作符,實現里面的apply方法,在apply里面調用我們的writeResponseBodyToDisk(),把拿到的boolean值返回,ok,轉換完成~
⑦.接下來就是accept中對下載好的文件進行操作了,判斷下boolean,如果true怎么怎么樣,false怎么怎么樣。
⑧.最后一定要寫new Consumer<Throwable>(),因為你網絡請求失敗了,Observable就會發送一個異常,你如果不捕獲它,程序就會崩潰
以上簡單了解~~
如果使用源生的OKHTTP,那就簡單的多,直接調用下載:
OkHttpClient okHttpClient = new OkHttpClient.Builder().build();
Retrofit retrofit = new Retrofit.Builder()
.client(okHttpClient)
.baseUrl(baseUrl)
.build();
IServiceApi apiService = retrofit.create(IServiceApi.class);
Call<ResponseBody> call = apiService.retrofitDownloadFile(mPDFDownloadUrl);
call.enqueue(new Callback<ResponseBody>() {
@Override
public void onResponse(Call<ResponseBody> call, Response<ResponseBody> response) {
if (response.isSuccessful()) {
//下載成功,寫入文件
boolean bl = DownLoadManager.writeResponseBodyToDisk(mPDFSavedFile, response.body());
if (bl) {
//這一步就是對你下載下來的文件進行你想要的操作了,我這里是展示PDF
displayFromFile(mPDFSavedFile);
}
} else {
//下載失敗
}
}
@Override
public void onFailure(Call<ResponseBody> call, Throwable t) {
//下載失敗
}
});
上面的代碼只涉及到Retrofit下載,就沒啥干貨可以講,用法也很簡單。不過大家可以看到兩種寫法的區別,先不說鏈接邏輯和層層遞進邏輯上的區別,光這個代碼量和排版就不討人喜歡。所以還是推薦大家學習一下RXJava,雖然入門挺難,但是用熟了你會發現有很多驚喜,太棒了~
誤區
在這里記錄我遇到的坑或者使用錯誤的地方:
①:一開始我理解的是GET請求一定要用@Path注解,POST請求一定要用@Query注解,但這是錯誤的。@Path、@Query具體怎么用要看具體的Url形式。
特別感謝以下作者,讓我從一個不會Retrofit的小白成長到會使用這個網絡框架的程序猿,這篇文章也是在這幾篇博文基礎上總結了下自己的理解:
Retrofit分析-漂亮的解耦套路
Android retrofit 注解@QueryMap和@Body的區別
這是一份很詳細的 Retrofit 2.0 使用教程(含實例講解)
Retrofit 2.0 超能實踐(四),完成大文件斷點下載