Android 網(wǎng)絡(luò)請(qǐng)求框架 Retrofit

說一下一年以來使用Retrofit的心得和使用姿勢(shì).

  • Retrofit 是什么:

A type-safe HTTP client for Android and Java

image.png

關(guān)于什么是REST ful架構(gòu), 這里不詳細(xì)說明,其實(shí)我也不會(huì)詳細(xì)說明,簡(jiǎn)單來說就是客戶端通過四個(gè)HTTP 動(dòng)詞獲取資源的過程

  • 稍微說明一下HTTP報(bào)文的請(qǐng)求組成: 不論是請(qǐng)求報(bào)文還是響應(yīng)報(bào)文 headerbody無疑是最重要的兩個(gè)部分.請(qǐng)求報(bào)文的header中會(huì)攜帶 Cookie,Cookie就是服務(wù)端來驗(yàn)證和區(qū)別客戶端的手段,這個(gè)客戶端是否是有效登陸的/是否是 成功注冊(cè)過得等等. 響應(yīng)報(bào)文中也會(huì)攜帶一些Cookie , 一些瀏覽器會(huì)使用一些方法儲(chǔ)存和保護(hù)和使用這些Cookie . 在Retrofit 和 Okhttp中表現(xiàn)為CookieJar .

  • 什么是Okhttp: Okhttp 也是Retrofit的 開發(fā)公司Square的作品.Retrofit2封裝了Okhttp的 一些東西,使得網(wǎng)絡(luò)請(qǐng)求更加的方便快捷.Retrofit 和 Okhttp這兩者是密不可分的.

進(jìn)入正題:

    compile 'com.squareup.retrofit2:retrofit:2.0.2'
    compile 'com.squareup.retrofit2:converter-gson:2.0.2'
    compile 'com.squareup.okhttp3:logging-interceptor:3.3.1'

基本配置.

發(fā)送一個(gè)GET請(qǐng)求總是很簡(jiǎn)單的,我使用官網(wǎng)的POST請(qǐng)求為例說明一下.(我稍微修改了一下)

這個(gè)邏輯是這樣子的,如果向服務(wù)端發(fā)送一個(gè)Task的相關(guān)信息,服務(wù)端就會(huì)返回給你這個(gè)Task的一些相關(guān)的信息.
服務(wù)端返回的是JSON數(shù)據(jù)格式
第一步:
首先寫一個(gè)接口 里面有這樣的一個(gè)函數(shù):

public interface TaskService {  
    @POST("/tasks")
    Call<Task> createTask(@Body Task task);
}

根據(jù)JSON格式 手寫一個(gè)這樣的Java 類 叫做Task

public class Task {  
    private long id;
    private String text;

    public Task(long id, String text) {
        this.id = id;
        this.text = text;
    }
}

因?yàn)槲椰F(xiàn)在要進(jìn)行一個(gè)POST請(qǐng)求,so:

Task task = new Task(1,"this is a simple task");
//使用Retrofit 的實(shí)例創(chuàng)建一個(gè)TaskService 的實(shí)例
TaskService taskService = retroft.createTask(TaskService.class);
Call<Task> call  = taskService.createTask(task);

接著進(jìn)行請(qǐng)求并且獲取請(qǐng)求的結(jié)果:

//進(jìn)行一個(gè)異步請(qǐng)求
call.enqueue(.....);
//或者進(jìn)行一個(gè)同步請(qǐng)求
taskService.createTask(task).execute();

好的,以上就是這樣:

但是

握草這些是什么鬼?看完了這一些代碼片段之后一定有這些疑問:

1) @POST? @Body? 而且 @POST()括號(hào)里面的是什么?
2) JSON就JSON數(shù)據(jù)好了 直接寫了一個(gè)類是什么意思? 我怎么取出數(shù)據(jù)呢?
3) 同步和異步又是什么鬼? 我是否需要 在主線程開一個(gè)線程進(jìn)行網(wǎng)絡(luò)請(qǐng)求? 
4) 如果我需要登錄header,我應(yīng)該放在哪里呢?
5) 這段代碼沒有給出創(chuàng)建一個(gè)Retrofit實(shí)例的過程,Retrofit應(yīng)該如何創(chuàng)建呢?
6) 為什么需要在一個(gè)接口中寫這個(gè)函數(shù)?

來一步步解釋一下(基本操作)

  • 為什么需要在接口中寫一個(gè)函數(shù):
    如果自己寫一個(gè)函數(shù)來進(jìn)行網(wǎng)絡(luò)請(qǐng)求不就增加了難度,所以借助已經(jīng)創(chuàng)建好了的Retrofit的實(shí)例來給出接口的實(shí)例來進(jìn)行網(wǎng)絡(luò)請(qǐng)求是最簡(jiǎn)潔的.
  • 如何創(chuàng)建一個(gè)Retrofit實(shí)例:
//interceptor 是 打印網(wǎng)絡(luò)請(qǐng)求的log 并且設(shè)置log的層級(jí) Level.BODY的層級(jí)是比較全面的 
        HttpLoggingInterceptor interceptor = new HttpLoggingInterceptor();
        interceptor.setLevel(HttpLoggingInterceptor.Level.BODY);
 OkHttpClient client = new OkHttpClient.Builder()
                .readTimeout(25, TimeUnit.SECONDS)
                .connectTimeout(25, TimeUnit.SECONDS)
                .writeTimeout(25,TimeUnit.SECONDS)
                .addInterceptor(interceptor)
              //  .cookieJar(cookieJar)
                .build();
//Retrofit實(shí)例的創(chuàng)建過程使用了建造者模式:
  Retrofit retrofit = new Retrofit.Builder()  
                .addConverterFactory(GsonConverterFactory.create())
                .client(client)
                .baseUrl("https://something.best.com/api/")
                .build();

之前說過Retrofit的基礎(chǔ)是Okhttp所以,構(gòu)建Retrofit的時(shí)候需要傳入一個(gè)OkhttpClient的實(shí)例 在這個(gè)client的實(shí)例中可以設(shè)置interceptorCookieJar

在創(chuàng)建Retrofit的實(shí)例的過程中baseUrl()就是需要請(qǐng)求的url的一部分, Retrofit在請(qǐng)求的過程中會(huì)將@POST括號(hào)中的部分 拼接在baseUrl的后面形成一個(gè)完整的requestUrl.

這里@POST 是Retrofit的特色之一,使用注解來"標(biāo)注"請(qǐng)求的類型: POST GET等 而@Body表示后面的數(shù)據(jù)不是攜帶完整數(shù)據(jù)的一個(gè)類.

接口中的這個(gè)函數(shù)的返回值是一個(gè)被包括在Call<T>(CallAdapter)這個(gè)泛型T所對(duì)應(yīng)的實(shí)際類型,這里就是Task 注意:這里請(qǐng)求時(shí)上傳的值和返回的類型都是一樣的都是Task,在其他情況可能不一樣.

這里需要注意的是callAdapter可能不止一種,還有對(duì)RxJava的支持Observable<T>也是經(jīng)常見到的.

但是返回的數(shù)據(jù)明明是JSON數(shù)據(jù),為什么變成了一個(gè)類 ,這里就需要多謝之前配置的依賴converter-gson提供的一個(gè)類GsonConverter,這個(gè)類將JSON數(shù)據(jù)轉(zhuǎn)化成了一個(gè)類

  {
           "city":"北京",
           "size":16800,
          "population":1600
        }

好比這一段json數(shù)據(jù) 如果你使用GsonFormat這個(gè)Android Studio 插件的話 就會(huì)生成

String city;
int size;
int population;

這三個(gè)量分別對(duì)應(yīng)三個(gè)數(shù)據(jù).
之后可以使用一些getter() setter()來取出數(shù)據(jù).

在進(jìn)行數(shù)據(jù)請(qǐng)求的時(shí)候也新建一個(gè)這樣的類的實(shí)例的話,在構(gòu)造函數(shù)通填入數(shù)據(jù)就可以了(有一種 key-value的感覺對(duì)吧!)

但是如果需要的不只是響應(yīng)的body中的內(nèi)容 還需要響應(yīng)報(bào)文中的其他東西,可以使用Call<ResponseBody>這樣不止返回Body 還有其他內(nèi)容,但是這樣的話就沒有通過converter轉(zhuǎn)化為一個(gè)類了; 同理,如果不想管返回類型,可以使用Call<Void>

此外Retrofit還提供了很多其他的注解@Query/ @QueryMap注解可以用于查詢參數(shù)的場(chǎng)景

https://www.google.com/search?q=something&aqs=somethingelse&sourceid=chrome&ie=UTF-8

代碼可以寫成:

@Query("q") String arg1,@Query("ags") String args2, @Query("sourceid") String args3, @Query("id") String args4;

只需要key和查詢參數(shù)的key一一對(duì)應(yīng)就好了
還有@Path注解和其他注解 這里就不一一贅述了

如果使用的是enqueue()函數(shù)的話 進(jìn)行的是一個(gè)異步的操作,可以再回調(diào)函數(shù)中操作UI線程

 Call<ResponseBody> call = mCcnuService.performLibLogin2();
        call.enqueue(new Callback<ResponseBody>() {
            @Override
            public void onResponse(Call<ResponseBody> call, retrofit2.Response<ResponseBody> response) {
                
            }

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

            }
        });

這個(gè)是一個(gè)同步的操作 注意不要阻塞了主線程

  retrofit2.Response<ResponseBody> responseBody2 = mCcnuService.performSystemLogin().execute();

最后說一下在使用過程中經(jīng)常出現(xiàn)的問題和一些常見的使用場(chǎng)景的解決策略:

1) 需要訪問url A 獲取數(shù)據(jù)之后 在訪問 url B 使用url A 里面拿到的數(shù)據(jù)訪問 url C 獲取最后想要的數(shù)據(jù):

推薦使用Retrofit的同步操作,這樣的話比較符合邏輯上面的順次進(jìn)行的概念 如果使用異步操作的話可能會(huì)造成回調(diào)地獄

image.png
  1. 為什么命名onSuccess執(zhí)行了 但是沒有取出想要的數(shù)據(jù)?

這個(gè)情況多半是在Call<T>中 T 類型的變量名 和JSON數(shù)據(jù)中的不一樣. 這很好理解,key-value 如果key錯(cuò)誤了就不能取出正確的數(shù)據(jù)了.

在使用@Header注解的時(shí)候也要注意這個(gè)問題 token/Cookie的key也要和后端提供的一樣.使用postman調(diào)試過,拿到JSON數(shù)據(jù)在使用GsonFormat生成會(huì)大大避免這個(gè)問題

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • Android 自定義View的各種姿勢(shì)1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 178,725評(píng)論 25 709
  • 說到目前最火的網(wǎng)絡(luò)請(qǐng)求庫(kù),那肯定是的非Retrofit莫屬了,如果你還不了解Retrofit如何使用,如果你想讓自...
    koala_閱讀 11,820評(píng)論 8 34
  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見模式的工具(例如配置管理,服務(wù)發(fā)現(xiàn),斷路器,智...
    卡卡羅2017閱讀 136,502評(píng)論 19 139
  • AndroidStudio使用kotlin入門 導(dǎo)讀 1、創(chuàng)建第一個(gè)kotlin項(xiàng)目 2、java代碼自動(dòng)轉(zhuǎn)換成...
    b77535296c81閱讀 24,797評(píng)論 4 9
  • 媒體人在商業(yè)里找到自己的新活法,歡迎你來,我是佳侖。昨天晚上八點(diǎn),我和小蝶做了連線,關(guān)于配音和模仿,我們聊了一個(gè)多...
    佳侖閱讀 731評(píng)論 0 1

友情鏈接更多精彩內(nèi)容