Android 主流開源框架(四)Retrofit 使用詳解

前言

最近有個想法——就是把 Android 主流開源框架進(jìn)行深入分析换怖,然后寫成一系列文章,包括該框架的詳細(xì)使用與源碼解析蟀瞧。目的是通過鑒賞大神的源碼來了解框架底層的原理沉颂,也就是做到不僅要知其然,還要知其所以然悦污。

這里我說下自己閱讀源碼的經(jīng)驗铸屉,我一般都是按照平時使用某個框架或者某個系統(tǒng)源碼的使用流程入手的,首先要知道怎么使用切端,然后再去深究每一步底層做了什么彻坛,用了哪些好的設(shè)計模式,為什么要這么設(shè)計。

系列文章:

更多干貨請關(guān)注 AndroidNotes

一昌屉、Retrofit 介紹

前面的文章已經(jīng)介紹了 OkHttp 的使用OkHttp 源碼分析钙蒙,不了解的強烈建議先看看這 2 篇文章。這篇介紹的是 Retrofit间驮,它也是 Square 公司開源的網(wǎng)絡(luò)框架躬厌,它的底層就是基于 OkHttp 實現(xiàn)的,不過它比 OkHttp 使用更方便竞帽,也更適合進(jìn)行 RESTful API 格式的請求扛施。

二、Retrofit 的使用

2.1 使用前準(zhǔn)備

(1)加入網(wǎng)絡(luò)權(quán)限 在 AndroidManifest.xml 文件中加入如下:

<uses-permission android:name="android.permission.INTERNET"/>

(2)添加 Retrofit 庫的依賴
因為需要將服務(wù)器返回的 ResponseBody 轉(zhuǎn)換成實體類屹篓,所以需要添加 Gson 庫的依賴作為數(shù)據(jù)解析器疙渣。
最終在當(dāng)前使用的 module 下的 build.gradle 中加入如下依賴:

// Retrofit
implementation 'com.squareup.retrofit2:retrofit:2.5.0'
// Gson
implementation 'com.squareup.retrofit2:converter-gson:2.5.0'

2.2 簡單的 GET 請求

這里使用 postman 提供的 GET 接口進(jìn)行演示。(Postman Echo

(1)創(chuàng)建一個實體類堆巧,用于接收服務(wù)器返回的數(shù)據(jù):

public class PostmanGetBean {
    private String url;
    // 其余字段省略昌阿,具體看 demo。
}

(2)創(chuàng)建一個接口恳邀,用于定義網(wǎng)絡(luò)請求:

public interface PostmanService {
    @GET("get")
    Call<PostmanGetBean> testGet();
}

可以看到懦冰,這里有一個 testGet() 方法,方法上面的注解 @GET 表示 GET 請求谣沸,注解里面的 “get” 會與后面的 baseUrl 拼接成完整的路徑刷钢。例如 baseUrl 為 https://postman-echo.com/,則完整的路徑為 https://postman-echo.com/get乳附。這里建議 baseUrl 以 /(斜線)結(jié)尾内地,注解中的 path 統(tǒng)一不要以 /(斜線)開頭,因為這種方式看起來比較直觀赋除。
(3)創(chuàng)建 Retrofit 的實例:

Retrofit retrofit = new Retrofit.Builder()
        .baseUrl("https://postman-echo.com/")// baseUrl
        .addConverterFactory(GsonConverterFactory.create())// 解析json數(shù)據(jù)
        .build();

(4)創(chuàng)建網(wǎng)絡(luò)請求接口的實例阱缓,并調(diào)用接口中的方法獲取 Call 對象:

PostmanService service = retrofit.create(PostmanService.class);
Call<PostmanGetBean> call = service.testGet();

(5)進(jìn)行網(wǎng)絡(luò)請求

call.enqueue(new Callback<PostmanGetBean>() {
    @Override
    public void onResponse(Call<PostmanGetBean> call, Response<PostmanGetBean> response) {
        System.out.println(response.body().getUrl());
    }

    @Override
    public void onFailure(Call<PostmanGetBean> call, Throwable t) {

    }
});

打印結(jié)果:

https://postman-echo.com/get

示例源碼:RetrofitActivity-testGetRequest

三、Retrofit 注解說明

Retrofit 中使用了大量的注解举农,這里將這些注解分成 3 類荆针。

3.1 第一類:網(wǎng)絡(luò)請求方法

分別是 @GET、@POST颁糟、@PUT航背、@DELETE、@PATH棱貌、@HEAD玖媚、@OPTIONS 和 @HTTP,前 7 個分別對應(yīng) HTTP 中的網(wǎng)絡(luò)請求方法婚脱,都接收一個字符串與 baseUrl 組成完整的 URL今魔,也可以不指定勺像,通過 @HTTP 注解設(shè)置。最后一個 @HTTP 注解可以用來替換前面 7 個注解错森,以及其他擴(kuò)展功能咏删。
這里主要講下 @HTTP 注解,其他注解與 @GET 注解類似问词。

@HTTP 注解示例:
@HTTP 注解有 3 個屬性:method督函、path 與 hasBody,上面說了這個注解可以用來替換前面 7 個注解激挪,所以就替換一下前面講到 GET 請求中的 @GET 注解吧辰狡。

這里只需要修改接口即可,其他不變:

public interface PostmanService {
    @HTTP(method = "GET", path = "get", hasBody = false)
    Call<PostmanGetBean> testHTTP();
}

運行結(jié)果:
與 @GET 注解示例一樣垄分。
示例源碼:RetrofitActivity-testHTTP

3.2 第二類:標(biāo)記

3.2.1 @FormUrlEncoded 注解

簡介: 表示請求體是一個 Form 表單宛篇。
示例:
這里使用 postman 提供的 POST 接口進(jìn)行演示。

單個鍵值對傳:
(1)創(chuàng)建實體類:

public class PostmanPostBean {
    // 字段與重寫 toString() 方法省略薄湿,具體看 demo
}

(2)創(chuàng)建接口:

public interface PostmanService {
    @POST("post")
    @FormUrlEncoded
    Call<PostmanPostBean> testFormUrlEncoded1(@Field("username") String name, @Field("password") String password);
}

可以看到叫倍,這里使用了 @Field 注解,它屬于第三類注解豺瘤,用來向 Post 表單傳入鍵值對吆倦,其中 username 表示鍵,name 表示值坐求。

(3)發(fā)起請求:

Retrofit retrofit = new Retrofit.Builder()
        .baseUrl("https://postman-echo.com/")
        .addConverterFactory(GsonConverterFactory.create())
        .build();

PostmanService service = retrofit.create(PostmanService.class);
Call<PostmanPostBean> call = service.testFormUrlEncoded1("wildma", "123456");
call.enqueue(new Callback<PostmanPostBean>() {
    @Override
    public void onResponse(Call<PostmanPostBean> call, Response<PostmanPostBean> response) {
        System.out.println(response.body().getForm().toString());
    }

    @Override
    public void onFailure(Call<PostmanPostBean> call, Throwable t) {

    }
});

運行結(jié)果:

FormEntity{username='wildma', password='123456'}

示例源碼:RetrofitActivity-testFormUrlEncoded1

傳入一個 Map 集合:
向 Post 表單傳入鍵值對除了上面一個個傳蚕泽,還可以使用注解 @FieldMap 傳一個 Map 集合,如下:

(1)創(chuàng)建接口:

public interface PostmanService {
    @POST("post")
    @FormUrlEncoded
    Call<PostmanPostBean> testFormUrlEncoded2(@FieldMap Map<String, Object> map);
}

(3)發(fā)起請求:

// 省略創(chuàng)建 Retrofit 的實例代碼
Map<String, Object> map = new HashMap<>();
map.put("username", "wildma");
map.put("password", "123456");
Call<PostmanPostBean> call = service.testFormUrlEncoded2(map);
// 省略網(wǎng)絡(luò)請求代碼

示例源碼:RetrofitActivity-testFormUrlEncoded2

3.2.2 @Multipart 注解

簡介: 表示請求體是一個支持文件上傳的 Form 表單桥嗤。
示例:
這里使用 YESAPI 提供的圖片上傳接口進(jìn)行演示须妻。

單文件上傳:
(1)創(chuàng)建實體類:

public class UploadImgBean {
    // 字段與重寫 toString() 方法省略,具體看 demo
}

(2)創(chuàng)建接口:

public interface FileUploadService {
    @POST("?service=App.CDN.UploadImg")
    @Multipart
    Call<UploadImgBean> testFileUpload1(@Part MultipartBody.Part file, @Part("app_key") RequestBody appKey);
}

可以看到泛领,這里使用了 @Part 注解荒吏,它屬于第三類注解,用于表單字段渊鞋,適用于有文件上傳的情況绰更。這里使用了@Part 的兩種類型,MultipartBody.Part 表示上傳一個文件篓像,RequestBody 表示傳一個鍵值對动知,其中 app_key 表示鍵皿伺,appKey 表示值员辩。

(3)發(fā)起請求:

Retrofit retrofit = new Retrofit.Builder()
        .baseUrl("http://hn216.api.yesapi.cn/")
        .addConverterFactory(GsonConverterFactory.create())
        .build();

RequestBody appKey = RequestBody.create(null, "替換成你在 YESAPI 上獲取的 appKey");
// test.png 為 SD 卡跟目錄下的文件,需要提前放好
File file = new File(Environment.getExternalStorageDirectory(), "test.png");
RequestBody requestBody = RequestBody.create(MediaType.parse("image/png"), file);
// 構(gòu)建 MultipartBody.Part鸵鸥,其中 file 為服務(wù)器約定好的 key奠滑,test.png 為文件名稱
MultipartBody.Part filePart = MultipartBody.Part.createFormData("file", "test.png", requestBody);

FileUploadService service = retrofit.create(FileUploadService.class);
Call<UploadImgBean> call = service.testFileUpload1(filePart, appKey);
call.enqueue(new Callback<UploadImgBean>() {
    @Override
    public void onResponse(Call<UploadImgBean> call, Response<UploadImgBean> response) {
        System.out.println(response.body().toString());
    }

    @Override
    public void onFailure(Call<UploadImgBean> call, Throwable t) {
    }
});

運行結(jié)果:

UploadImgBean{ret=200, data=DataEntity{err_code=0, err_msg='', url='http://cd7.yesapi.net/xxx.png'}, msg='當(dāng)前小白接口:App.CDN.UploadImg'}

示例源碼:RetrofitActivity-testFileUpload1

多文件上傳:
如果想上傳多個文件丹皱,則可以使用注解 @PartMap 傳一個鍵值對為 <String, RequestBody> 的 Map 集合,如下:

(1)創(chuàng)建接口:

public interface FileUploadService {
    @POST("?service=App.CDN.UploadImg")
    @Multipart
    Call<UploadImgBean> testFileUpload2(@PartMap Map<String, RequestBody> map);
}

(1)發(fā)起請求:

Retrofit retrofit = new Retrofit.Builder()
        .baseUrl("http://hn216.api.yesapi.cn/")
        .addConverterFactory(GsonConverterFactory.create())
        .build();

RequestBody appKey = RequestBody.create(null, "替換成你在 YESAPI 上獲取的 appKey");
// test.png 為 SD 卡跟目錄下的文件宋税,需要提前放好
File file = new File(Environment.getExternalStorageDirectory(), "test.png");
RequestBody requestBody = RequestBody.create(MediaType.parse("image/png"), file);
Map<String, RequestBody> requestBodyMap = new HashMap<>();
requestBodyMap.put("app_key", appKey);
// 加入一個文件摊崭,其中 file 為服務(wù)器約定好的 key,test.png 為文件名稱
requestBodyMap.put("file\"; filename=\"test.png", requestBody);
// 有更多文件杰赛,則繼續(xù) put()...

FileUploadService service = retrofit.create(FileUploadService.class);
Call<UploadImgBean> call = service.testFileUpload2(requestBodyMap);
call.enqueue(new Callback<UploadImgBean>() {
    @Override
    public void onResponse(Call<UploadImgBean> call, Response<UploadImgBean> response) {
        System.out.println(response.body().toString());
    }

    @Override
    public void onFailure(Call<UploadImgBean> call, Throwable t) {
    }
});

示例源碼:RetrofitActivity-testFileUpload2

3.2.3 @Streaming 注解

簡介: 表示響應(yīng)體的數(shù)據(jù)用流的形式返回呢簸,如果沒有使用該注解,默認(rèn)會把數(shù)據(jù)全部載入內(nèi)存乏屯,之后獲取數(shù)據(jù)就從內(nèi)存中讀取根时,所以該注解一般用在返回數(shù)據(jù)比較大的時候,例如下載大文件辰晕。
示例:
這里使用下載我的博客頭像( https://wildma.github.io/medias/avatars/avatar.jpg ) 進(jìn)行演示蛤迎。

(1)下載文件不需要創(chuàng)建一個實體類,直接用 ResponseBody 來接收服務(wù)器返回的數(shù)據(jù)含友。(后面的示例為了方便演示替裆,也不再解析成實體類,直接用 ResponseBody 來接收服務(wù)器返回的原始數(shù)據(jù))

(2)創(chuàng)建接口:

public interface FileDownloadService {
    @Streaming
    @GET("medias/avatars/avatar.jpg")
    Call<ResponseBody> testFileDownload();
}

這里使用了 @Streaming 注解用來表示響應(yīng)體的數(shù)據(jù)用流的形式返回窘问。

(3)發(fā)起請求:

Retrofit retrofit = new Retrofit.Builder()
        .baseUrl("https://wildma.github.io/")
        .addConverterFactory(GsonConverterFactory.create())
        .build();

FileDownloadService service = retrofit.create(FileDownloadService.class);
Call<ResponseBody> call = service.testFileDownload();
call.enqueue(new Callback<ResponseBody>() {
    @Override
    public void onResponse(Call<ResponseBody> call, Response<ResponseBody> response) {
        InputStream is = response.body().byteStream();
        // 保存文件...
    }

    @Override
    public void onFailure(Call<ResponseBody> call, Throwable t) {

    }
});

示例源碼:RetrofitActivity-testFileDownload

3.3 第三類:網(wǎng)絡(luò)請求參數(shù)

3.3.1 @Header辆童、@Headers 與 @HeaderMap 注解

簡介: @Header 與 @HeaderMap 用于添加不固定值的請求頭,@Headers 用于添加固定值的請求頭惠赫。@Header 與 @HeaderMap 是作為請求方法的參數(shù)傳入胸遇,@Headers 則直接添加到請求方法上。
示例:

// @Header
@GET("headers")
Call<ResponseBody> testHeader(@Header("token") String token);

// @Headers
@Headers("token: 123")
@GET("headers")
Call<ResponseBody> testHeaders();

// @Headers 多個請求頭
@Headers({"token: 123", "sign: 456"})
@GET("headers")
Call<ResponseBody> testHeaders2();

// @HeaderMap
@GET("headers")
Call<ResponseBody> testHeaderMap(@HeaderMap Map<String, String> map);

示例源碼:RetrofitActivity-testHeader()汉形、testHeaders()纸镊、testHeaders2()

3.3.2 @Body 注解

簡介: @Body 用于非表單請求體。很多時候后臺要求前端傳一個 json 字符串的請求體概疆,這時候我們可以使用 @Body 注解來輕松實現(xiàn)逗威,因為該注解可以直接傳一個實體類,發(fā)起請求的過程中會把該實體類轉(zhuǎn)換成 json 字符串的請求體傳給后臺岔冀。
示例:
(1)創(chuàng)建接口:

public interface PostmanService {
    @POST("post")
    Call<ResponseBody> testBody(@Body TestBodyBean testBodyBean);
}

(2)發(fā)起請求:

// 省略創(chuàng)建 Retrofit 的實例代碼
TestBodyBean bean = new TestBodyBean();
bean.setUsername("wildma");
bean.setPassword("123456");

PostmanService service = retrofit.create(PostmanService.class);
Call<ResponseBody> call = service.testBody(bean);
// 省略網(wǎng)絡(luò)請求代碼

示例源碼:RetrofitActivity-testBody()

3.3.3 @Field 與 @FieldMap 注解

簡介: 用于向 Post 表單傳入鍵值對凯旭。
示例:
具體使用前面講 @FormUrlEncoded 注解的時候已經(jīng)講過了。
示例源碼:RetrofitActivity-testFormUrlEncoded1()使套、testFormUrlEncoded2()

3.3.4 @Part 與 @PartMap 注解

簡介: 用于表單字段罐呼,適用于有文件上傳的情況。
示例:
具體使用前面講 @Multipart 注解的時候已經(jīng)講過了侦高。
示例源碼:RetrofitActivity-testFileUpload1()嫉柴、testFileUpload2()

3.3.5 @Query 與 @QueryMap 注解

簡介: 用于表單字段,功能與 @Field奉呛、@FiledMap 一樣计螺,區(qū)別在于 @Query夯尽、@QueryMap 的數(shù)據(jù)體現(xiàn)在 URL 上,而 @Field登馒、@FiledMap 的數(shù)據(jù)體現(xiàn)在請求體上瓮顽,但生成的數(shù)據(jù)是一樣的畏吓。
示例:
(1)創(chuàng)建接口:

public interface PostmanService {
    @GET("get")
    Call<ResponseBody> testQuery(@Query("username") String username);
}

(2)發(fā)起請求:

Retrofit retrofit = new Retrofit.Builder()
        .baseUrl("https://postman-echo.com/")
        .addConverterFactory(GsonConverterFactory.create())
        .build();

PostmanService service = retrofit.create(PostmanService.class);
Call<ResponseBody> call = service.testQuery("wildma");
// 省略網(wǎng)絡(luò)請求代碼

上面的 baseUrl 為 https://postman-echo.com/,@GET 注解中的部分 URL 為 “get”,最終完整 URL 如果沒用 @Query 注解應(yīng)該是 https://postman-echo.com/get谍倦,用了注解就變成 https://postman-echo.com/get?username=wildma 了弟疆。

@QueryMap 注解則對應(yīng) Map 集合钧排,接口如下:

public interface PostmanService {
    @GET("get")
    Call<ResponseBody> testQueryMap(@QueryMap Map<String, String> params);
}

發(fā)起請求的代碼就不貼出來了忆植,傳一個對應(yīng)的 Map 集合進(jìn)來即可。

示例源碼:RetrofitActivity-testQuery()法褥、testQueryMap()

3.3.6 @QueryName 注解

簡介: 用于沒有值的查詢參數(shù)茫叭,該注解實際項目中很少用到,功能與 @Query半等、@QueryMap 類似揍愁,參數(shù)都拼接在 URL 上,但是 @Query杀饵、@QueryMap 在 URL 上是以鍵值對拼接的莽囤,而 @QueryName 只是拼接鍵,沒有值切距。
示例:
(1)創(chuàng)建接口:

public interface PostmanService {
    @GET("get")
    Call<ResponseBody> testQueryName(@QueryName String... filters);
}

注解后面可跟 String filter朽缎,也可跟 String... filters,其中后者是可變長參數(shù)谜悟,可以傳多個參數(shù)也可不傳參數(shù)话肖。
(2)發(fā)起請求:

Retrofit retrofit = new Retrofit.Builder()
        .baseUrl("https://postman-echo.com/")
        .addConverterFactory(GsonConverterFactory.create())
        .build();

PostmanService service = retrofit.create(PostmanService.class);
Call<ResponseBody> call = service.testQueryName("wildma","tom");
// 省略網(wǎng)絡(luò)請求代碼

上面最終拼接的 URL 為 https://postman-echo.com/get?wildma&tom

示例源碼:RetrofitActivity-testQueryName()

3.3.7 @Path 注解

簡介: @Path 用于設(shè)置 URL 地址的缺省值葡幸。
示例:
這里使用官方提供的 API最筒,即獲取指定用戶的倉庫列表進(jìn)行演示。
(1)創(chuàng)建接口:

public interface GitHubService {
    @GET("users/{user}/repos")
    Call<List<RepoBean>> listRepos(@Path("user") String user);
}

(2)發(fā)起請求:

Retrofit retrofit = new Retrofit.Builder()
        .baseUrl("https://api.github.com/")
        .addConverterFactory(GsonConverterFactory.create())
        .build();

GitHubService service = retrofit.create(GitHubService.class);
Call<ResponseBody> call = service.testPath("wildma");
// 省略網(wǎng)絡(luò)請求代碼

可以看到蔚叨,@GET 注解里面的 “users/{user}/repos” 中有一個 “{user}”床蜘,這個就是 URL 地址的缺省值, listRepos() 方法中的 @Path("user") String user 表示傳入的 urse 就是用來替換上面的 {user} 的蔑水。所以最終完整的 URL 為 https://api.github.com/users/wildma/repos邢锯。

示例源碼:RetrofitActivity-testPath()

3.3.8 @Url 注解

簡介: @Url 用于動態(tài)設(shè)置一個完整的 URL。
示例:
(1)創(chuàng)建接口:

public interface PostmanService {
    @GET()
    Call<ResponseBody> testUrl(@Url String url);
}

(2)發(fā)起請求:

Retrofit retrofit = new Retrofit.Builder()
        .baseUrl("https://api.github.com/")
        .addConverterFactory(GsonConverterFactory.create())
        .build();

PostmanService service = retrofit.create(PostmanService.class);
Call<ResponseBody> call = service.testUrl("https://postman-echo.com/get");
// 省略網(wǎng)絡(luò)請求代碼

可以看到搀别,baseUrl() 與 testUrl() 都設(shè)置了一個 URL丹擎,但由于 @Url 注解標(biāo)識的 URL 是動態(tài)設(shè)置的,所以最終以 testUrl() 中設(shè)置的為準(zhǔn)领曼,也就是最終使用的是 https://postman-echo.com/get鸥鹉。

示例源碼:RetrofitActivity-testUrl()

四蛮穿、設(shè)置自定義的 OkHttpClient

在創(chuàng)建 Retrofit 的實例的時候可以通過 client() 方法設(shè)置自定義的 OkHttpClient庶骄,自定義 OkHttpClient 可以設(shè)置統(tǒng)一的 header毁渗,添加 log 攔截器、Cookie 等单刁。這里就講下怎么設(shè)置統(tǒng)一的 header 吧灸异!
(1)創(chuàng)建 OkHttpClient 的時候通過添加攔截器,然后在攔截器的 intercept() 方法中設(shè)置統(tǒng)一的 header:

OkHttpClient okHttpClient = new OkHttpClient.Builder().addInterceptor(new Interceptor() {
    @Override
    public okhttp3.Response intercept(Chain chain) throws IOException {
        Request originalRequest = chain.request();
        Request request = originalRequest.newBuilder()
                .header("token", "123")
                .header("sign", "456")
                .build();
        return chain.proceed(request);
    }
}).build();

(2)通過 client() 方法設(shè)置自定義的 OkHttpClient:

Retrofit retrofit = new Retrofit.Builder()
        .client(okHttpClient)// 設(shè)置自定義的 OkHttpClient
        .baseUrl("https://postman-echo.com/")
        .addConverterFactory(GsonConverterFactory.create())
        .build();

示例源碼:RetrofitActivity-testCustomOkHttpClient

五羔飞、關(guān)于 Converter

Retrofit 默認(rèn)用 ResponseBody 來接收服務(wù)器返回的數(shù)據(jù)肺樟,如果想要轉(zhuǎn)換成對應(yīng)的實體類,那么在創(chuàng)建 Retrofit 的實例的時候可以通過 addConverterFactory() 方法設(shè)置一個數(shù)據(jù)解析器逻淌,數(shù)據(jù)解析器有多種選擇么伯,Retrofit 文檔中就提供了很多種:

  • Gson: com.squareup.retrofit2:converter-gson
  • Jackson: com.squareup.retrofit2:converter-jackson
  • Moshi: com.squareup.retrofit2:converter-moshi
  • Protobuf: com.squareup.retrofit2:converter-protobuf
  • Wire: com.squareup.retrofit2:converter-wire
  • Simple XML: com.squareup.retrofit2:converter-simplexml
  • Scalars (primitives, boxed, and String): com.squareup.retrofit2:converter-scalars

除了文檔提供的這幾種,其實還有一種常用的:
fastjson:'org.ligboy.retrofit2:converter-fastjson-android

這里使用 Gson 進(jìn)行演示卡儒。
(1)創(chuàng)建接口:

public interface PostmanService {
    @GET("get")
    Call<PostmanGetBean> testGet();
}

這里直接用實體類 PostmanGetBean 替換 ResponseBody田柔。
(2)發(fā)起請求:

Retrofit retrofit = new Retrofit.Builder()
        .baseUrl("https://postman-echo.com/")
        .addConverterFactory(GsonConverterFactory.create())// 添加 Gson 解析器
        .build();
// 省略網(wǎng)絡(luò)請求代碼

這里添加了 Gson 作為數(shù)據(jù)解析器。

示例源碼:RetrofitActivity-testGet

六骨望、關(guān)于 CallAdapter

前面創(chuàng)建接口的時候硬爆,發(fā)現(xiàn)接口中的方法返回類型都是 Call,如果想要返回其他類型擎鸠,那么在創(chuàng)建 Retrofit 的實例的時候可以通過 addCallAdapterFactory() 方法設(shè)置一個 CallAdapter缀磕,Retrofit 提供了如下 CallAdapter:

  • guava:com.squareup.retrofit2:adapter-guava
  • Java8:com.squareup.retrofit2:adapter-java8:2.0.2
  • rxjava:com.squareup.retrofit2:adapter-rxjava

這里使用 RxJava 進(jìn)行演示。
(1)添加相關(guān)依賴:

// 支持 rxjava2
implementation 'com.squareup.retrofit2:adapter-rxjava2:2.5.0'
// rxjava2
compile 'io.reactivex.rxjava2:rxjava:2.2.13'
compile 'io.reactivex.rxjava2:rxandroid:2.1.1'

(2)創(chuàng)建接口:

@GET("get")
Observable<ResponseBody> testCallAdapter();

這里使用 Observable 替換 Call劣光。
(3)發(fā)起請求:

Retrofit retrofit = new Retrofit.Builder()
        .baseUrl("https://postman-echo.com/")
        .addConverterFactory(GsonConverterFactory.create())
        .addCallAdapterFactory(RxJava2CallAdapterFactory.create())// 設(shè)置 RxJava 作為當(dāng)前的 CallAdapter
        .build();

PostmanService service = retrofit.create(PostmanService.class);
Observable<ResponseBody> observable = service.testCallAdapter();
observable.subscribeOn(Schedulers.io())               // 在 IO 線程進(jìn)行網(wǎng)絡(luò)請求
        .observeOn(AndroidSchedulers.mainThread())  // 在主線程處理請求結(jié)果
        .subscribe(new Observer<ResponseBody>() {
            @Override
            public void onSubscribe(Disposable d) {
            }

            @Override
            public void onNext(ResponseBody responseBody) {
                try {
                    System.out.println(responseBody.string());
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }

            @Override
            public void onError(Throwable e) {
            }

            @Override
            public void onComplete() {
            }
        });

這里設(shè)置 RxJava 作為當(dāng)前的 CallAdapter袜蚕,并且調(diào)用 Observable 的相關(guān)方法進(jìn)行網(wǎng)絡(luò)請求與請求結(jié)果的處理。
示例源碼:RetrofitActivity-testCallAdapter

七绢涡、源碼

Retrofit 的使用 demo

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末廷没,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子垂寥,更是在濱河造成了極大的恐慌颠黎,老刑警劉巖,帶你破解...
    沈念sama閱讀 218,941評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件滞项,死亡現(xiàn)場離奇詭異狭归,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)文判,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,397評論 3 395
  • 文/潘曉璐 我一進(jìn)店門过椎,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人戏仓,你說我怎么就攤上這事疚宇⊥鍪螅” “怎么了?”我有些...
    開封第一講書人閱讀 165,345評論 0 356
  • 文/不壞的土叔 我叫張陵敷待,是天一觀的道長间涵。 經(jīng)常有香客問我,道長榜揖,這世上最難降的妖魔是什么勾哩? 我笑而不...
    開封第一講書人閱讀 58,851評論 1 295
  • 正文 為了忘掉前任,我火速辦了婚禮举哟,結(jié)果婚禮上思劳,老公的妹妹穿的比我還像新娘。我一直安慰自己妨猩,他們只是感情好潜叛,可當(dāng)我...
    茶點故事閱讀 67,868評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著壶硅,像睡著了一般威兜。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上森瘪,一...
    開封第一講書人閱讀 51,688評論 1 305
  • 那天牡属,我揣著相機(jī)與錄音,去河邊找鬼扼睬。 笑死逮栅,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的窗宇。 我是一名探鬼主播措伐,決...
    沈念sama閱讀 40,414評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼军俊!你這毒婦竟也來了侥加?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,319評論 0 276
  • 序言:老撾萬榮一對情侶失蹤粪躬,失蹤者是張志新(化名)和其女友劉穎担败,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體镰官,經(jīng)...
    沈念sama閱讀 45,775評論 1 315
  • 正文 獨居荒郊野嶺守林人離奇死亡提前,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,945評論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了泳唠。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片狈网。...
    茶點故事閱讀 40,096評論 1 350
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出拓哺,到底是詐尸還是另有隱情勇垛,我是刑警寧澤,帶...
    沈念sama閱讀 35,789評論 5 346
  • 正文 年R本政府宣布士鸥,位于F島的核電站闲孤,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏础淤。R本人自食惡果不足惜崭放,卻給世界環(huán)境...
    茶點故事閱讀 41,437評論 3 331
  • 文/蒙蒙 一哨苛、第九天 我趴在偏房一處隱蔽的房頂上張望鸽凶。 院中可真熱鬧,春花似錦建峭、人聲如沸玻侥。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,993評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽凑兰。三九已至,卻和暖如春边锁,著一層夾襖步出監(jiān)牢的瞬間姑食,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,107評論 1 271
  • 我被黑心中介騙來泰國打工茅坛, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留音半,地道東北人。 一個月前我還...
    沈念sama閱讀 48,308評論 3 372
  • 正文 我出身青樓贡蓖,卻偏偏與公主長得像曹鸠,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子斥铺,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 45,037評論 2 355

推薦閱讀更多精彩內(nèi)容