本文注目錄:java
- Retrofit入門
- Retrofit註解詳解
- Gson與Converter
- RxJava與CallAdapter
- 自定義Converter
- 自定義CallAdapter
- 其它說明
前言
本文中的Retrofit均指代Retrofit2.0。
本文涉及到的代碼以及測試使用的接口可在Github上找到。
測試接口服務器在 server 項目下,直接運行 RESTServer.main()
便可啓動測試服務器,所面代碼示例均使用該接口(接口地址 http://localhost:4567/ ).
固然你也能夠本身藉助 json-server 或 最新開源的Parse 搭建一個REST API,不過都須要安裝Node.js,有興趣的能夠去試試。node
接口列表:git
前面寫了你應該知道的HTTP基礎知識 介紹了HTTP的相關知識,不知道那些想了解Retrofit的同鞋是否是去看了Retrofit的官方教程,曾經我在你真的會用Gson嗎?Gson使用指南(四) 中說當你瞭解了註解、反射、泛型、HTTP的內容只須要看一篇Retrofit的代碼示例就能夠輕鬆玩轉Retrofit,不知道你玩轉了沒?注:以上的接口的{id}
和{page}
均表明一個純數字,/blog/{id}
能夠用/blog?id=XXX
代替,page同理。github
固然註解、反射、泛型的內容尚未寫,Retrofit的內容卻先來了!畢竟看懂Retrofit也只須要會使就行,你準備好了嗎?json
一、Retrofit入門
Retrofit 其實至關簡單,簡單到源碼只有37個文件,其中22個文件是註解還都和HTTP有關,真正暴露給用戶的類並很少,因此我看了一遍 官方教程 大多數情景就能夠無障礙使用,若是你尚未看過,能夠先去看看,雖然是英文,但代碼纔是最好的教程不是麼?固然本篇文章會介紹得詳細一點,不能寫一篇水文,畢竟我給它命名爲《你真的會用Retrofit2嗎?Retrofit2徹底教程》。數組
1.一、建立Retrofit實例
- Retrofit retrofit = new Retrofit.Builder()
- .baseUrl("http://localhost:4567/")
- .build();
建立Retrofit實例時須要經過Retrofit.Builder
,並調用baseUrl
方法設置URL。
注: Retrofit2 的baseUlr 必須以 /(斜線) 結束,否則會拋出一個IllegalArgumentException
,因此若是你看到別的教程沒有以/ 結束,那麼多半是直接從Retrofit 1.X 照搬過來的。bash
1.二、接口定義
以獲取指定id的Blog爲例:服務器
- public interface BlogService {
- @GET("blog/{id}")
- Call<ResponseBody> getFirstBlog(@Path("id") int id);
- }
注意,這裏是interface
不是class
,因此咱們是沒法直接調用該方法,咱們須要用Retrofit建立一個BlogService
的代理對象。ide
- BlogService service = retrofit.create(BlogService.class);
拿到代理對象以後,就能夠調用該方法啦。svg
1.三、接口調用
- Call<ResponseBody> call = service.getFirstBlog(2);
- call.enqueue(new Callback<ResponseBody>() {
- @Override
- public void onResponse(Call<ResponseBody> call, Response<ResponseBody> response) {
- try {
- System.out.println(response.body().string());
- } catch (IOException e) {
- e.printStackTrace();
- }
- }
-
- @Override
- public void onFailure(Call<ResponseBody> call, Throwable t) {
- t.printStackTrace();
- }
- });
打印結果:
- {"code":200,"msg":"OK","data":{"id":2,"date":"2016-04-15 03:17:50","author":"怪盜kidou","title":"Retrofit2 測試2","content":"這裏是 Retrofit2 Demo 測試服務器2"},"count":0,"page":0}
示例源碼見 Example01.java
二、Retrofit註解詳解
上面提到Retrofit 共22個註解,這節就專門介紹這22個註解,爲幫助你們更好理解我將這22個註解分爲三類,並用表格的形式展示出來,表格上說得並不完整,具體的見源碼上的例子註釋。
第一類:HTTP請求方法
HTTP請求方法註解
以上表格中的除HTTP之外都對應了HTTP標準中的請求方法,而HTTP註解則能夠代替以上方法中的任意一個註解,有3個屬性:method
、path
,hasBody
,下面是用HTTP註解實現上面Example01.java 的例子。
- public interface BlogService {
-
- @HTTP(method = "get", path = "blog/{id}", hasBody = false)
- Call<ResponseBody> getFirstBlog(@Path("id") int id);
- }
示例源碼見 Example02.java
第二類:標記類
標記類註解
示例源碼見 Example03.java
第三類:參數類
參數類註解
注1:{佔位符}和PATH
儘可能只用在URL的path部分,url中的參數使用Query
和QueryMap
代替,保證接口定義的簡潔
注2:Query
、Field
和Part
這三者都支持數組和實現了Iterable
接口的類型,如List
,Set
等,方便向後臺傳遞數組。
- Call<ResponseBody> foo(@Query("ids[]") List<Integer> ids);
Path 示例源碼見 Example01.java
Field、FieldMap、Part和PartMap 示例源碼見 Example03.java
Header和Headers 示例源碼見 Example04.java
Query、QueryMap、Url 示例源碼見 Example05.java
三、Gson與Converter
在默認狀況下Retrofit只支持將HTTP的響應體轉換換爲ResponseBody
,
這也是什麼我在前面的例子接口的返回值都是 Call<ResponseBody>
,
但若是響應體只是支持轉換爲ResponseBody
的話何須要引用泛型呢,
返回值直接用一個Call
就好了嘛,既然支持泛型,那說明泛型參數能夠是其它類型的,
而Converter
就是Retrofit爲咱們提供用於將ResponseBody
轉換爲咱們想要的類型,
有了Converter
以後咱們就能夠寫把咱們的第一個例子的接口寫成這個樣子了:
- public interface BlogService {
- @GET("blog/{id}")
- Call<Result<Blog>> getFirstBlog(
- }
固然只改變泛型的類型是不行的,咱們在建立Retrofit時須要明確告知用於將ResponseBody
轉換咱們泛型中的類型時須要使用的Converter
引入Gson支持:
- compile 'com.squareup.retrofit2:converter-gson:2.0.2'
經過GsonConverterFactory爲Retrofit添加Gson支持:
- Gson gson = new GsonBuilder()
-
- .setDateFormat("yyyy-MM-dd hh:mm:ss")
- .create();
-
- Retrofit retrofit = new Retrofit.Builder()
- .baseUrl("http://localhost:4567/")
-
- .addConverterFactory(GsonConverterFactory.create(gson))
- .build();
示例源碼見 Example06.java
這樣Retrofit就會使用Gson將ResponseBody
轉換咱們想要的類型。
這是時候咱們終於能夠演示如使建立一個Blog了!
- @POST("blog")
- Call<Result<Blog>> createBlog(@Body Blog blog);
被@Body
註解的的Blog將會被Gson轉換成RequestBody發送到服務器。
- BlogService service = retrofit.create(BlogService.class);
- Blog blog = new Blog();
- blog.content = "新建的Blog";
- blog.title = "測試";
- blog.author = "怪盜kidou";
- Call<Result<Blog>> call = service.createBlog(blog);
結果:
- Result{code=200, msg='OK', data=Blog{id=20, date='2016-04-21 05:29:58', author='怪盜kidou', title='測試', content='新建的Blog'}, count=0, page=0}
示例源碼見 Example07.java
若是你對Gson不熟悉能夠參考我寫的《你真的會用Gson嗎?Gson使用指南》 系列。
四、RxJava與CallAdapter
說到Retrofit就不得說到另外一個火到不行的庫RxJava
,網上已經很多文章講如何與Retrofit結合,但這裏仍是會有一個RxJava的例子,不過這裏主要目的是介紹使用CallAdapter
所帶來的效果。
第3節介紹的Converter
是對於Call<T>
中T
的轉換,而CallAdapter
則能夠對Call
轉換,這樣的話Call<T>
中的Call
也是能夠被替換的,而返回值的類型就決定你後續的處理程序邏輯,一樣Retrofit提供了多個CallAdapter
,這裏以RxJava
的爲例,用Observable
代替Call
:
引入RxJava支持:
- compile 'com.squareup.retrofit2:adapter-rxjava:2.0.2'
經過RxJavaCallAdapterFactory爲Retrofit添加RxJava支持:
- Retrofit retrofit = new Retrofit.Builder()
- .baseUrl("http://localhost:4567/")
- .addConverterFactory(GsonConverterFactory.create())
- .addCallAdapterFactory(RxJavaCallAdapterFactory.create())
- .build();
接口設計:
- public interface BlogService {
- @POST("/blog")
- Observable<Result<List<Blog>>> getBlogs();
- }
使用:
- BlogService service = retrofit.create(BlogService.class);
- service.getBlogs(1)
- .subscribeOn(Schedulers.io())
- .subscribe(new Subscriber<Result<List<Blog>>>() {
- @Override
- public void onCompleted() {
- System.out.println("onCompleted");
- }
-
- @Override
- public void onError(Throwable e) {
- System.err.println("onError");
- }
-
- @Override
- public void onNext(Result<List<Blog>> blogsResult) {
- System.out.println(blogsResult);
- }
- });
結果:
- Result{code=200, msg='OK', data=[Blog{id=1, date='2016-04-15 03:17:50', author='怪盜kidou', title='Retrofit2 測試1', content='這裏是 Retrofit2 Demo 測試服務器1'},.....], count=20, page=1}
示例源碼見 Example08.java
「20160608補充」:像上面的這種狀況最後咱們沒法獲取到返回的Header和響應碼的,若是咱們須要這二者,提供兩種方案:
一、用Observable<Response<T>>``Observable<T>
,這裏的Response
指retrofit2.Response
二、用Observable<Result<T>>
代替Observable<T>
,這裏的Result
是指retrofit2.adapter.rxjava.Result
,這個Result中包含了Response
的實例
五、自定義Converter
本節的內容是教你們實如今一簡易的Converter,這裏以返回格式爲Call<String>
爲例。
在此以前先了解一下Converter接口及其做用:
- public interface Converter<F, T> {
-
- T convert(F value) throws IOException;
-
-
- abstract class Factory {
-
-
- public Converter<ResponseBody, ?> responseBodyConverter(Type type, Annotation[] annotations,
- Retrofit retrofit) {
- return null;
- }
-
-
-
- public Converter<?, RequestBody> requestBodyConverter(Type type,
- Annotation[] parameterAnnotations, Annotation[] methodAnnotations, Retrofit retrofit) {
- return null;
- }
-
-
-
- public Converter<?, String> stringConverter(Type type, Annotation[] annotations,
- Retrofit retrofit) {
- return null;
- }
-
- }
- }
咱們要想從Call<ResponseBody>
轉換爲 Call<String>
那麼對應的F和T則分別對應ResponseBody
和String
,咱們定義一個StringConverter
並實現Converter接口。
- public static class StringConverter implements Converter<ResponseBody, String> {
-
- public static final StringConverter INSTANCE = new StringConverter();
-
- @Override
- public String convert(ResponseBody value) throws IOException {
- return value.string();
- }
- }
咱們須要一個Fractory
來向Retrofit註冊StringConverter
- public static class StringConverterFactory extends Converter.Factory {
-
- public static final StringConverterFactory INSTANCE = new StringConverterFactory();
-
- public static StringConverterFactory create() {
- return INSTANCE;
- }
-
-
- @Override
- public Converter<ResponseBody, ?> responseBodyConverter(Type type, Annotation[] annotations, Retrofit retrofit) {
- if (type == String.class) {
- return StringConverter.INSTANCE;
- }
-
- return null;
- }
- }
使用Retrofit.Builder.addConverterFactory
向Retrofit註冊咱們StringConverterFactory
:
- Retrofit retrofit = new Retrofit.Builder()
- .baseUrl("http://localhost:4567/")
-
- .addConverterFactory(StringConverterFactory.create())
- .addConverterFactory(GsonConverterFactory.create())
- .build();
注:addConverterFactory
是有前後順序的,若是有多個ConverterFactory都支持同一種類型,那麼就是隻有第一個纔會被使用,而GsonConverterFactory
是不判斷是否支持的,因此這裏交換了順序還會有一個異常拋出,緣由是類型不匹配。
只要返回值類型的泛型參數就會由咱們的StringConverter
處理,不論是Call<String>
仍是Observable<String>
有沒有很簡單?若是你有其它的需求處理的就本身實現吧。
示例源碼見 Example09.java
六、自定義CallAdapter
本節將介紹如何自定一個CallAdapter
,並驗證是否全部的String都會使用咱們第5節中自定義的Converter。
先看一下CallAdapter接口定義及各方法的做用:
- public interface CallAdapter<T> {
-
-
-
-
- Type responseType();
-
- <R> T adapt(Call<R> call);
-
-
- abstract class Factory {
-
-
-
- public abstract CallAdapter<?> get(Type returnType, Annotation[] annotations,
- Retrofit retrofit);
-
-
- protected static Type getParameterUpperBound(int index, ParameterizedType type) {
- return Utils.getParameterUpperBound(index, type);
- }
-
-
-
- protected static Class<?> getRawType(Type type) {
- return Utils.getRawType(type);
- }
- }
- }
瞭解了CallAdapter
的結構和其做用以後,咱們就能夠開始自定義咱們的CallAdapter
了,本節以CustomCall<String>
爲例。
在此咱們須要定義一個CustomCall
,不過這裏的CustomCall
做爲演示只是對Call
的一個包裝,並無實際的用途。
- public static class CustomCall<R> {
-
- public final Call<R> call;
-
- public CustomCall(Call<R> call) {
- this.call = call;
- }
-
- public R get() throws IOException {
- return call.execute().body();
- }
- }
有了CustomCall
,咱們還須要一個CustomCallAdapter
來實現 Call<T>
到CustomCall<T>
的轉換,這裏須要注意的是最後的泛型,是咱們要返回的類型。
- public static class CustomCallAdapter implements CallAdapter<CustomCall<?>> {
-
- private final Type responseType;
-
-
- CustomCallAdapter(Type responseType) {
- this.responseType = responseType;
- }
-
- @Override
- public Type responseType() {
- return responseType;
- }
-
- @Override
- public <R> CustomCall<R> adapt(Call<R> call) {
-
- return new CustomCall<>(call);
- }
- }
提供一個CustomCallAdapterFactory
用於向Retrofit提供CustomCallAdapter
:
- public static class CustomCallAdapterFactory extends CallAdapter.Factory {
- public static final CustomCallAdapterFactory INSTANCE = new CustomCallAdapterFactory();
-
- @Override
- public CallAdapter<?> get(Type returnType, Annotation[] annotations, Retrofit retrofit) {
-
- Class<?> rawType = getRawType(returnType);
-
- if (rawType == CustomCall.class && returnType instanceof ParameterizedType) {
- Type callReturnType = getParameterUpperBound(0, (ParameterizedType) returnType);
- return new CustomCallAdapter(callReturnType);
- }
- return null;
- }
- }
使用addCallAdapterFactory
向Retrofit註冊CustomCallAdapterFactory
- Retrofit retrofit = new Retrofit.Builder()
- .baseUrl("http://localhost:4567/")
- .addConverterFactory(Example09.StringConverterFactory.create())
- .addConverterFactory(GsonConverterFactory.create())
- .addCallAdapterFactory(CustomCallAdapterFactory.INSTANCE)
- .build();
注: addCallAdapterFactory
與addConverterFactory
同理,也有前後順序。
示例源碼見 Example10.java
七、其它說明
7.1 Retrofit.Builder
前面用到了 Retrofit.Builder
中的baseUrl
、addCallAdapterFactory
、addConverterFactory
、build
方法,還有callbackExecutor
、callFactory
、client
、validateEagerly
這四個方法沒有用到,這裏簡單的介紹一下。
7.2 Retrofit的Url組合規則
從上面不能難看出如下規則:
- 若是你在註解中提供的url是完整的url,則url將做爲請求的url。
- 若是你在註解中提供的url是不完整的url,且不以 / 開頭,則請求的url爲baseUrl+註解中提供的值
- 若是你在註解中提供的url是不完整的url,且以 / 開頭,則請求的url爲baseUrl的主機部分+註解中提供的值
7.3 Retrofit提供的Converter
7.4 Retrofit提供的CallAdapter:
7.5 關於源碼
看到這兒可能有小夥伴要問爲何源碼沒有把類拆分到單獨的文件,命名也不能體現其用途,這裏主要是由於方便你們看源碼,而不是將注意力放在反覆跳轉上,另外一方面也是由於同一個例子中不可避免的使用其它小節要介紹的內容,因此就直接用了ExampleXX
的形式,不過在項目中千萬不要使用這種方式,必定要好好命名,作到見名知意。
結語
其它本博客的內容早就已經完成好了,但因爲當時HTTP、反射、註解的博客一篇也沒有寫,因此一直沒有發,期間也有很多的博主寫了Retrofit2的博文,不過呢沒有自定義相關的內容也沒有對各個註解進行詳解,因此我仍是決定發出來幫助一下那此對Retrofit2無從下手同鞋。
原文連接:http://www.jianshu.com/p/308f3c54abdd