Markdown版本筆記 | 我的GitHub首頁 | 我的博客 | 我的微信 | 我的郵箱 |
---|---|---|---|---|
MyAndroidBlogs | baiqiantao | baiqiantao | bqt20094 | baiqiantao@sina.com |
目錄
Retrofit
入門
Retrofit注解
Gson與Converter
RxJava與CallAdapter
自定義Converter
自定義CallAdapter
其它說明
Retrofit.Builder的其他方法
Retrofit的Url組合規則
Retrofit提供的Converter
Retrofit提供的CallAdapter
如何運行項目
Retrofit
入門
Retrofit 源碼只有37個文件,其中22個文件是注解,還都和HTTP有關,真正暴露給用戶的類並不多,看一遍 官方教程 ,大多數情景就可以無障礙使用。
創建Retrofit實例
Retrofit retrofit = new Retrofit.Builder()
.baseUrl("http://localhost:4567/")
.build();
Retrofit2 的baseUlr 必須以 /
結束,不然會拋出一個IllegalArgumentException。
接口定義
public interface BlogService {
@GET("blog/{id}")/*這里的{id} 表示是一個變量*/
Call<ResponseBody> getBlog(@Path("id") int id);
}
注意,這里是interface不是class,所以我們是無法直接調用該方法,我們需要用Retrofit創建一個BlogService的代理對象
。
BlogService service = retrofit.create(BlogService.class);
接口調用
拿到代理對象之后,就可以調用該方法啦
Call<ResponseBody> call = service.getBlog(2);
//用法和OkHttp的call如出一轍,不同的是,Android系統中回調方法執行在主線程
call.enqueue(new Callback<ResponseBody>() {
@Override
public void onResponse(Call<ResponseBody> call, Response<ResponseBody> response) {
try {
System.out.println(response.body().string());
System.out.println(response.code() + " " + response.isSuccessful());//200 true
} catch (IOException e) {
e.printStackTrace();
}
}
@Override
public void onFailure(Call<ResponseBody> call, Throwable t) {
t.printStackTrace();
}
});
Retrofit注解
Retrofit 共22個注解,為幫助大家更好理解,我將這22個注解分為三類,並用表格的形式展現出來。
8個HTTP請求方法注解
以上表格中的除HTTP以外都對應了HTTP標准中的請求方法,而HTTP注解則可以代替以上方法中的任意一個注解。
有3個屬性:
- method 表示請求的方法,區分大小寫
- path 表示路徑
- hasBody 表示是否有請求體,默認為false,此時可以不寫此參數
public interface BlogService {
@HTTP(method = "GET", path = "blog/{id}", hasBody = false)
Call<ResponseBody> getBlog(@Path("id") int id);
}
注:method 的值 retrofit 不會做處理,所以要自行保證其准確性,包括大小寫,示例源碼使用小寫也可以是因為示例源碼中的
服務器不區分大小寫
。
3個標記類注解
11個參數類注解
注1:{占位符}
和PATH盡量只用在URL的path部分,url中的參數使用Query和QueryMap代替,保證接口定義的簡潔
注2:Query、Field和Part這三者都支持數組和實現了Iterable接口的類型,如List,Set等,方便向后台傳遞數組。
Gson與Converter
在默認情況下Retrofit只支持將HTTP的響應體轉換換為ResponseBody
,但如果響應體只是支持轉換為ResponseBody的話何必要引入泛型呢,返回值直接用一個Call就行了嘛。既然支持泛型,那說明泛型參數可以是其它類型的。
Converter
就是Retrofit為我們提供用於將ResponseBody轉換為我們想要的類型,例如上面例子的接口可以寫成這個樣子:
public interface BlogService {
@GET("blog/{id}")
Call<Result<Blog>> getBlog(@Path("id") int id);
}
當然只改變泛型的類型是不行的,我們在創建Retrofit時需要明確告知用於將ResponseBody轉換我們泛型中的類型時需要使用的Converter。
首先引入Gson支持:
compile 'com.squareup.retrofit2:converter-gson:2.1.0'
通過GsonConverterFactory
為Retrofit添加Gson支持:
Gson gson = new GsonBuilder()//配置你的Gson
.setDateFormat("yyyy-MM-dd hh:mm:ss")
.create();
Retrofit retrofit = new Retrofit.Builder()
.baseUrl("http://localhost:4567/")
.addConverterFactory(GsonConverterFactory.create(gson))//可以接收自定義的Gson,也可以不傳
.build();
這樣Retrofit就會使用Gson將ResponseBody轉換成我們想要的類型。
Gson 反序列化案例
Call<Result<Blog>> call = retrofit.create(BlogService.class).getBlog(2);
call.enqueue(new Callback<Result<Blog>>() {
@Override
public void onResponse(Call<Result<Blog>> call, Response<Result<Blog>> response) {
Result<Blog> result = response.body();//已經轉換為想要的類型了
System.out.println(result);
}
@Override
public void onFailure(Call<Result<Blog>> call, Throwable t) {
t.printStackTrace();
}
});
Gson 序列化案例
下面演示如使創建一個Blog!
@POST("blog")
Call<Result<Blog>> createBlog(@Body Blog blog);
被@Body
注解的的Blog將會被Gson轉換成RequestBody發送到服務器。
Blog blog = new Blog("author:包青天", "title:測試", "content:新建的Blog");
Call<Result<Blog>> call = retrofit.create(BlogService.class).createBlog(blog);
RxJava與CallAdapter
上一節介紹的Converter
是對於Call<T>
中T的轉換,而CallAdapter
則可以對Call
轉換,這樣的話返回值的類型就決定你后續的處理程序邏輯,同樣Retrofit提供了多個CallAdapter,這里以RxJava的為例,用Observable代替Call。
引入RxJava支持:
compile 'com.squareup.retrofit2:adapter-rxjava:2.1.0'
//compile 'com.squareup.retrofit2:adapter-rxjava2:2.3.0'//針對rxjava2.x(adapter-rxjava2的版本要 >= 2.2.0)
通過RxJavaCallAdapterFactory為Retrofit添加RxJava支持:
Retrofit retrofit = new Retrofit.Builder()
.baseUrl("http://localhost:4567/")
.addConverterFactory(GsonConverterFactory.create(gson))
.addCallAdapterFactory(RxJavaCallAdapterFactory.create())
//.addCallAdapterFactory(RxJava2CallAdapterFactory.create())//針對rxjava2.x
.build();
接口設計:
public interface BlogService {
@GET("/blog")
Observable<Result<List<Blog>>> getBlogs(@Query("page") int page);
//如果需要Header的值,可以把返回值替換為 Observable<Response<Result<List<Blog>>>>
}
使用:
retrofit.create(BlogService.class)
.getBlogs(1)
.subscribeOn(Schedulers.io())
.subscribe(blogsResult -> System.out.println(blogsResult)); //Result<List<Blog>>
像上面的這種情況最后我們無法獲取到返回的Header和響應碼的,如果我們需要這兩者,提供兩種方案:
- 用
Observable<retrofit2.Response<T>>
代替Observable<T>
- 用
Observable<retrofit2.adapter.rxjava.Result<T>>
代替Observable<T>
,這個Result中包含了Response的實例
自定義Converter
本節的內容是教大家實現一簡易的Converter,這里以返回格式為Call<String>
為例。
在此之前先了解一下Converter接口及其作用:
public interface Converter<F, T> {
// 實現從 F(rom) 到 T(o)的轉換
T convert(F value) throws IOException;
// 用於向Retrofit提供相應Converter的工廠
abstract class Factory {
// 這里創建從ResponseBody其它類型的Converter,如果不能處理返回null
// 主要用於對響應體的處理
public Converter<ResponseBody, ?> responseBodyConverter(Type type, Annotation[] annotations, Retrofit retrofit) {
return null;
}
// 在這里創建從自定類型到ResponseBody的Converter,不能處理就返回null
// 主要用於對Part、PartMap、Body注解的處理
public Converter<?, RequestBody> requestBodyConverter(Type type,
Annotation[] parameterAnnotations, Annotation[] methodAnnotations, Retrofit retrofit) {
return null;
}
// 這里用於對Field、FieldMap、Header、Path、Query、QueryMap注解的處理
// Retrfofit對於上面的幾個注解默認使用的是調用toString方法
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;
}
// 我們只關實現從ResponseBody 到 String 的轉換,所以其它方法可不覆蓋
@Override
public Converter<ResponseBody, ?> responseBodyConverter(Type type, Annotation[] annotations, Retrofit retrofit) {
if (type == String.class) return StringConverter.INSTANCE;
return null;//其它類型我們不處理,返回null就行
}
}
使用Retrofit.Builder.addConverterFactory
向Retrofit注冊我們StringConverterFactory:
Retrofit retrofit = new Retrofit.Builder()
.baseUrl("http://localhost:4567/")
.addConverterFactory(StringConverterFactory.create())// 如是有Gson這類的Converter一定要放在其它前面
.addConverterFactory(GsonConverterFactory.create())
.build();
注:addConverterFactory是有先后順序的,如果有多個ConverterFactory都支持同一種類型,那么就是只有第一個才會被使用
,而GsonConverterFactory是不判斷是否支持的,所以這里交換順序會有異常拋出,原因是類型不匹配。
只要返回值類型的泛型參數就會由我們的StringConverter處理,不管是Call
自定義CallAdapter
本節將介紹如何自定一個CallAdapter,並驗證是否所有的String都會使用我們上一節中自定義的Converter。
先看一下CallAdapter接口定義及各方法的作用:
public interface CallAdapter<T> {
// 直正數據的類型 如Call<T> 中的 T
// 這個 T 會作為Converter.Factory.responseBodyConverter 的第一個參數
// 可以參照上面的自定義Converter
Type responseType();
<R> T adapt(Call<R> call);
// 用於向Retrofit提供CallAdapter的工廠類
abstract class Factory {
// 在這個方法中判斷是否是我們支持的類型,returnType 即Call<Requestbody>和`Observable<Requestbody>`
// RxJavaCallAdapterFactory 就是判斷returnType是不是Observable<?> 類型
// 不支持時返回null
public abstract CallAdapter<?> get(Type returnType, Annotation[] annotations, Retrofit retrofit);
// 用於獲取泛型的參數 如 Call<Requestbody> 中 Requestbody
protected static Type getParameterUpperBound(int index, ParameterizedType type) {
return Utils.getParameterUpperBound(index, type);
}
// 用於獲取泛型的原始類型 如 Call<Requestbody> 中的 Call
// 上面的get方法需要使用該方法。
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;
// 下面的 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);// 由 CustomCall 決定如何使用
}
}
提供一個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);
// 返回值必須是CustomCall並且帶有泛型
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同理,也有先后順序。
其它說明
Retrofit.Builder的其他方法
Retrofit.Builder中的所有方法:
baseUrl(String baseUrl) 和 baseUrl(okhttp3.HttpUrl baseUrl)
addConverterFactory(retrofit2.Converter.Factory factory)//對Call<T>中T的進行類型轉換
addCallAdapterFactory(retrofit2.CallAdapter.Factory factory)//對Call進行轉換
callbackExecutor(java.util.concurrent.Executor executor)
callFactory(okhttp3.Call.Factory factory)
client(OkHttpClient client)
validateEagerly(boolean validateEagerly)
callbackExecutor(Executor)
:指定Call.enqueue時使用的Executor,所以該設置只對返回值為Call的方法有效callFactory(Factory)
:設置一個自定義的okhttp3.Call.Factory
,那什么是Factory呢?OkHttpClient就實現了此接口。如果你需要對okhttpclient進行詳細的設置,需要構建OkHttpClient對象,然后通過callFactory方法傳入,否則new一個默認的OkHttpClient。下面的client方法最終也是調用了該方法,所有兩者不能共用client(OkHttpClient)
:設置自定義的OkHttpClient,以前的Retrofit版本中,不同的Retrofit對象共同OkHttpClient,在2.0后,各對象各自持有不同的OkHttpClient實例,所以當你需要共用OkHttpClient或需要自定義時則可以使用該方法,如:處理Cookie、使用stetho調式等validateEagerly(boolean)
:是否在調用create(Class)時檢測接口定義是否正確,而不是在調用方法才檢測。適合在開發、測試時使用
很多時候,比如你使用Retrofit需要統一的log管理,給每個請求添加統一的header等,這些都應該通過OkHttpClient去操作。你可以單獨寫一個OkHttpClient的單例生成類,在這個里面完成你所需的所有的配置,然后通過Retrofit.Builder的callFactory方法設置給retrofit。
Retrofit的Url組合規則
從上面不能難看出以下規則:
- 如果你在注解中提供的url是
完整的url
,則url將作為請求的url。 - 如果你在注解中提供的url是不完整的url,且不以
/
開頭,則請求的url為baseUrl+注解中提供的值
- 如果你在注解中提供的url是不完整的url,且以
/
開頭,則請求的url為baseUrl的主機部分+注解中提供的值
Retrofit提供的Converter
Converter | Gradle依賴 |
---|---|
Gson | com.squareup.retrofit2:converter-gson:2.0.2 |
Jackson | com.squareup.retrofit2:converter-jackson:2.0.2 |
Moshi | com.squareup.retrofit2:converter-moshi:2.0.2 |
Protobuf | com.squareup.retrofit2:converter-protobuf:2.0.2 |
Wire | com.squareup.retrofit2:converter-wire:2.0.2 |
Simple XML | com.squareup.retrofit2:converter-simplexml:2.0.2 |
Scalars | com.squareup.retrofit2:converter-scalars:2.0.2 |
Retrofit提供的CallAdapter
CallAdapter | 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 |
如何運行項目
測試接口服務器在 server 項目下,直接運行 RESTServer.main() 即可啟動測試服務器
接口地址http://localhost:4567/
當然你也可以自己借助 json-server 或 最新開源的 Parse 搭建一個REST API,不過都需要安裝Node.js。
接口列表:
地址 | 請求方法 | 參數 | 說明 |
---|---|---|---|
/blog | GET | page={page},sort=asc或desc | 分頁獲取Blog列表,每頁10條 |
/blog/{id} | GET | id | 獲取指定ID的Blog |
/blog | POST | {"author":"","title":"","content":""} | 創建一個新Blog |
/blog/{id} | PUT | {"author":"","title":"","content":""} 中至少一個 | 修改Blog |
/blog/{id} | DELETE | id | 刪除一個Blog |
/form | POST | 任意,最終以Json Object形式返回 | 用於測試Form表單,支持文件上傳 |
/headers | GET | showAll=true或false,默認false | 返回自定義請求頭,all=true是顯示全部 |
2017-9-8
附件列表