小白進(jìn)階回憶錄:Retrofit2要點(diǎn)梳理

本博客為作者原創(chuàng),如需轉(zhuǎn)載請(qǐng)注明原博客出處:WONDER'TWO

0X00 寫(xiě)在前面


相信做過(guò)Android網(wǎng)絡(luò)請(qǐng)求的同學(xué)都繞不開(kāi)Volley芹橡,Retrofit息裸,OkHttp這幾座大山姐刁,至于他們的前世姻緣以及孰優(yōu)孰劣,不在本博客的討論范圍禀崖。如題礁鲁,這篇博客主要介紹一個(gè)小白(其實(shí)就是我自己)的Retrofit2進(jìn)階之路盐欺,會(huì)結(jié)合一個(gè)開(kāi)發(fā)實(shí)例介紹5節(jié)內(nèi)容:

  • Retrofit2 HTTP請(qǐng)求方法注解的字段說(shuō)明
  • Call<T>響應(yīng)結(jié)果的處理問(wèn)題
  • Retrofit2+RxJava實(shí)現(xiàn)開(kāi)發(fā)效率最大化
  • 自定義OkHttp Interceptor實(shí)現(xiàn)日志輸出,保存和添加Cookie
  • 自定義ResponseConverter仅醇,自定義HTTP請(qǐng)求注解

先來(lái)回顧一下Retrofit2在項(xiàng)目中的完整使用流程:創(chuàng)建Bean類 --> 創(chuàng)建接口形式的http請(qǐng)求方法 --> 通過(guò)Retrofit.builder()創(chuàng)建接口對(duì)象并調(diào)用http方法請(qǐng)求網(wǎng)絡(luò)數(shù)據(jù) --> 在RxJavaObservable(被觀察者)中異步處理請(qǐng)求結(jié)果冗美!

那么Retrofit2 Http 請(qǐng)求方法注解有那么多字段,都代表什么含義呢析二?添加請(qǐng)求頭或者大文件上傳的請(qǐng)求方法該怎么寫(xiě)呢粉洼?這將在第二節(jié)介紹。另外甲抖,Retrofit2基本用法的網(wǎng)絡(luò)響應(yīng)結(jié)果是一個(gè)Call<T> 漆改,那么怎樣在Android中解析Call<T> 呢?將在第二節(jié)介紹准谚。第三節(jié)根據(jù)Retrofit2使用流程介紹了一個(gè)實(shí)踐項(xiàng)目是怎樣使用Retrofit2+RxJava 做網(wǎng)絡(luò)請(qǐng)求。第四節(jié)和四五節(jié)是Retrofit實(shí)現(xiàn)一些復(fù)雜需求的必殺技去扣,介紹了自定義OkHttp Interceptor實(shí)現(xiàn)日志輸出柱衔,保存和添加Cookie;自定義ResponseConverter愉棱,自定義HTTP請(qǐng)求注解等內(nèi)容唆铐。

0X01 Retrofit2 HTTP請(qǐng)求方法注解的字段說(shuō)明


從Retrofit2的官方文檔來(lái)看,Retrofit2 進(jìn)行網(wǎng)絡(luò)請(qǐng)求的URL分為兩部分:BaseURL和relativeURL奔滑。BaseURL需要以/ 結(jié)尾艾岂, 一般不需要變化直接定義即可,當(dāng)然在特殊的情況下朋其,比如后一次網(wǎng)絡(luò)訪問(wèn)URL需要從前一次訪問(wèn)結(jié)果中獲取相關(guān)參數(shù)王浴,那么就需要?jiǎng)討B(tài)的操作URL脆炎,這種用法會(huì)第五節(jié)進(jìn)行介紹;relativeURL與每次請(qǐng)求的參數(shù)相關(guān)氓辣,所以每個(gè)request 方法都需要 http annotation 來(lái)提供請(qǐng)求的relativeURL秒裕,Retrofit2內(nèi)置的注解有五個(gè):GET, POST, PUT, DELETE, and HEAD. 這些注解在使用時(shí)涉及到哪些相關(guān)的字段呢?我從參考文獻(xiàn)的博客中引用了一張圖:

可以看到钞啸,有URL請(qǐng)求參數(shù)几蜻,Query參數(shù)這些簡(jiǎn)單網(wǎng)絡(luò)請(qǐng)求參數(shù);同時(shí)還支持用@Header添加請(qǐng)求頭体斩;POST請(qǐng)求中常用@FormUrlEncoded提交表單梭稚,并用@Field定義表單域;@MultiPart文件上傳并用@Part定義請(qǐng)求體絮吵。來(lái)看一個(gè)具體的例子(摘自Retrofit2官方文檔):

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

Retrofit2把網(wǎng)絡(luò)請(qǐng)求定義成接口的形式哨毁,如上是一個(gè)GET請(qǐng)求,@Path表示一個(gè)占位符源武,@Path中的變量必須與@GET變量中{} 中間的部分一致扼褪。下面是一個(gè)POST請(qǐng)求,@FormUrlEncoded用于提交一個(gè)表單粱栖,@Field定義了表單的name和value话浇。更多詳細(xì)的用法詳見(jiàn)Retrofit2官方文檔API Declaration ,另外Retrofit請(qǐng)求參數(shù)注解字段說(shuō)明 這篇博客介紹的比較詳細(xì)可作參考:

public interface GitHubService {
    @FormUrlEncoded
    @POST("user/edit")
    Call<User> updateUser(@Field("first_name") String first, @Field("last_name") String last);
}

0X02 Call<T> 響應(yīng)結(jié)果的處理


細(xì)心的你有木有發(fā)現(xiàn)闹究,發(fā)現(xiàn)官方文檔中給出的請(qǐng)求方法示例幔崖,返回結(jié)果都是 Call<List<User>> 這種形式?沒(méi)錯(cuò)渣淤!這就是Retrofit2最原始的網(wǎng)絡(luò)請(qǐng)求用法赏寇,官方文檔上介紹的很簡(jiǎn)潔,可以在 call<T> 響應(yīng)對(duì)象上做異步或者同步的操作价认,每個(gè) call<T> 對(duì)象只能用一次嗅定,要想多次使用可以調(diào)用 clone() 方法來(lái)克隆出多個(gè) call 對(duì)象以供更多操作使用。因?yàn)镽etrofit2 是一個(gè)類型安全的Java和Android網(wǎng)絡(luò)請(qǐng)求庫(kù)用踩,所以以上的操作對(duì) Java 網(wǎng)絡(luò)請(qǐng)求也是適用的渠退。

針對(duì)JVM而言,網(wǎng)絡(luò)請(qǐng)求和結(jié)果處理會(huì)放在同一個(gè)線程中執(zhí)行脐彩,那么在Android中碎乃,我們?cè)鯓犹幚碚?qǐng)求結(jié)果對(duì)象 call 呢?官方文檔也給出了答案惠奸,我們都知道Android中網(wǎng)絡(luò)請(qǐng)求這類耗時(shí)操作都是放在工作線程(即worker thread)來(lái)執(zhí)行的梅誓,然后在主線程(也即 UI thread)處理網(wǎng)絡(luò)請(qǐng)求結(jié)果,自然Retrofit2也不例外,由于Retrofit2拋棄了飽受詬病的Apache HttpClient底層只依賴OkHttp3.0梗掰,網(wǎng)絡(luò)訪問(wèn)層的操作都會(huì)交由OkHttp來(lái)完成嵌言,而OkHttp不僅擁有自動(dòng)維護(hù)的socket連接池,減少握手次數(shù)愧怜,而且還擁有隊(duì)列線程池呀页,可以輕松寫(xiě)并發(fā),同時(shí)還支持socket自動(dòng)選擇最好路線拥坛,并支持自動(dòng)重連蓬蝶,OkHttp的優(yōu)點(diǎn)遠(yuǎn)不止于此,所以Retrofit2選擇用OkHttp作為網(wǎng)絡(luò)請(qǐng)求執(zhí)行器是一個(gè)再明智不過(guò)的決定猜惋。

如果你想異步的執(zhí)行網(wǎng)絡(luò)請(qǐng)求丸氛,最簡(jiǎn)單的就是在Activity或者Fragment中View控件的監(jiān)聽(tīng)器中進(jìn)行網(wǎng)絡(luò)訪問(wèn),并通過(guò)call.enqueue()處理請(qǐng)求結(jié)果著摔,并更新UI缓窜,下面是一個(gè)小demo:

Button button = (Button) findViewById(R.id.button);
button.setOnClickListener(new View.OnClickListener() {
  @Override
  public void onClick(View view) {
    GitHubService gitHubService = GitHubService.retrofit.create(GitHubService.class);
    final Call<List<Contributor>> call =
            gitHubService.repoContributors("square", "retrofit");

    call.enqueue(new Callback<List<Contributor>>() {
        @Override
        public void onResponse(Call<List<Contributor>> call, Response<List<Contributor>> response) {
            final TextView textView = (TextView) findViewById(R.id.textView);
            textView.setText(response.body().toString());
        }
        @Override
        public void onFailure(Call<List<Contributor>> call, Throwable t) {
            final TextView textView = (TextView) findViewById(R.id.textView);
            textView.setText("Something went wrong: " + t.getMessage());
        }
    });
  }
});

如果你需要在工作線程中執(zhí)行網(wǎng)絡(luò)請(qǐng)求,而不是在一個(gè)Activity或者一個(gè)Fragment中去執(zhí)行谍咆,那么也就意味著禾锤,你可以在同一個(gè)線程中同步的去執(zhí)行網(wǎng)絡(luò)請(qǐng)求,使用call.execute()方法來(lái)處理請(qǐng)求結(jié)果即可摹察,代碼如下:

try {
  Response<User> response = call.execute();
} catch (IOException e ){
   // handle error
}

0X03 Retrofit2+RxJava實(shí)現(xiàn)開(kāi)發(fā)效率最大化


Retorfit是支持RxJava恩掷,Guava,Java8 等等一系列擴(kuò)展的供嚎,關(guān)于RxJava這個(gè)網(wǎng)紅我就不做介紹了黄娘,RactiveX項(xiàng)目對(duì) JVM 的擴(kuò)展,你可以把它當(dāng)做一個(gè)超級(jí)強(qiáng)大的異步事件處理庫(kù)克滴,可他的NB之處遠(yuǎn)不止于此逼争,至少做Android的都應(yīng)該聽(tīng)過(guò)他的鼎鼎大名,不熟悉的可以去看看RxJava WikiH芭狻誓焦!而這里Retrofit2+RxJava組合就可以實(shí)現(xiàn)開(kāi)發(fā)效率的大幅提升,至于怎樣提升的望忆?對(duì)比一下你以前寫(xiě)的網(wǎng)絡(luò)請(qǐng)求的代碼量就知道了罩阵!結(jié)合一個(gè)實(shí)踐項(xiàng)目的源碼來(lái)分析,這里是請(qǐng)求果殼網(wǎng)最新的100條文章數(shù)據(jù)启摄,返回結(jié)果為Json,首先build.gradle 添加依賴:

    compile 'io.reactivex:rxandroid:1.1.0' // RxAndroid
    compile 'io.reactivex:rxjava:1.1.0' // 推薦同時(shí)添加RxJava
    compile 'com.squareup.retrofit2:retrofit:2.1.0' // Retrofit網(wǎng)絡(luò)處理
    compile 'com.squareup.retrofit2:adapter-rxjava:2.1.0' // Retrofit的rx解析庫(kù)
    compile 'com.squareup.retrofit2:converter-gson:2.1.0' // Retrofit的gson庫(kù)

    compile 'com.squareup.okhttp3:okhttp:3.2.0' // OkHttp3

第一步幽钢,定義服務(wù)器Json數(shù)據(jù)對(duì)應(yīng)的POJO類歉备,這里我們可以偷一下懶可以直接通過(guò)jsonschema2pojo 這個(gè)網(wǎng)站自動(dòng)生成POJO類,就不用我們手動(dòng)去寫(xiě)了匪燕,然后copy到項(xiàng)目目錄的bean包下蕾羊。接著便是定義HTTP請(qǐng)求方法了喧笔,以接口的形式定義,如下:

// 服務(wù)器數(shù)據(jù)對(duì)應(yīng)的實(shí)體類
public class Guokr {
    // 定義序列化后的名字
    public @SerializedName("ok") Boolean response_ok;
    // 定義序列化后的名字
    public @SerializedName("result") List<GuokrResult> response_results;

    public static class GuokrResult {
        public int id;
        public String title;

        public String headline_img_tb; // 用于文章列表頁(yè)小圖
        public String headline_img; // 用于文章內(nèi)容頁(yè)大圖

        public String link;
        public String author;
        public String summary;
    }
}

// HTTP請(qǐng)求方法
public interface GuokrService {

    @GET("handpick/article.json")
    Observable<Guokr> getGuokrs(@Query("retrieve_type") String type,
                                @Query("category") String category,
                                @Query("limit") int limit,
                                @Query("ad") int ad);

}

其中 Observable<Guokr> 是RxJava中的被觀察者龟再,對(duì)應(yīng)請(qǐng)求結(jié)果Call<T>书闸。只是因?yàn)镽etrofit提供了非常強(qiáng)大的CallAdapterFactory 完美兼容了RxJava 這個(gè)超級(jí)大網(wǎng)紅,才導(dǎo)致我們平忱眨看到的寫(xiě)法是這樣的浆劲。第二步, 需要通過(guò)Retrofit.builder() 創(chuàng)建 GuokrService 接口對(duì)象哀澈,通過(guò)接口對(duì)象執(zhí)行 getGuokrs 方法進(jìn)行網(wǎng)絡(luò)訪問(wèn)牌借,代碼如下:

    // 封裝 GuokrService 請(qǐng)求
    public static GuokrService getGuokrService() {
        if (guokrService == null) {
            Retrofit retrofit = new Retrofit.Builder()
                    .client(mClient)
                    .baseUrl("http://apis.guokr.com/")
                    .addCallAdapterFactory(rxJavaCallAdapterFactory)
                    .addConverterFactory(gsonConverterFactory)
                    .build();
            guokrService = retrofit.create(GuokrService.class);
        }
        return guokrService;
    }

// 默認(rèn)加載最新的100條數(shù)據(jù)
subscription = RetrofitClient.getGuokrService().getGuokrs("by_since", "all", 100, 1)
        .subscribeOn(Schedulers.io())
        .observeOn(AndroidSchedulers.mainThread())
        .subscribe(new Observer<Guokr>() {
            @Override
            public void onCompleted() {
                Log.e(TAG, "--------completed-------");
            }

            @Override
            public void onError(Throwable e) {
                Log.e(TAG, "--------error-------");
                Log.e(TAG, e.getMessage());
            }

            @Override
            public void onNext(Guokr guokr) {
                if (guokr.response_ok) {
                    List<Guokr.GuokrResult> guokrResults = guokr.response_results;
                    List<GuokrItem> guokrItems = new ArrayList<>(guokrResults.size());
                    for (Guokr.GuokrResult result : guokrResults) {
                        GuokrItem item = new GuokrItem();
                        item.headline_img_tb = result.headline_img_tb;
                        item.title = result.title;
                        item.id = result.id;
                        item.headline_img = result.headline_img;
                        item.summary = result.summary;
                        guokrItems.add(item);
                    }
                    mAdapter.addAll(guokrItems);
                    mAdapter.notifyDataSetChanged();
                });

注意到封裝 GuokrService 請(qǐng)求:

  1. addCallAdapterFactory(rxJavaCallAdapterFactory) 方法指定使用RxJava 作為CallAdapter 割按,需要傳入一個(gè)RxJavaCallAdapterFactory對(duì)象:CallAdapter.Factory rxJavaCallAdapterFactory = RxJavaCallAdapterFactory.create()膨报;
  2. addConverterFactory(gsonConverterFactory) 方法指定 Gson 作為解析Json數(shù)據(jù)的ConverterConverter.Factory gsonConverterFactory = GsonConverterFactory.create()
  3. client(mClient)方法指定網(wǎng)絡(luò)執(zhí)行器為OkHttp 如下創(chuàng)建一個(gè)默認(rèn)的OkHttp對(duì)象傳入即可:OkHttpClient mClient = new OkHttpClient()适荣;

而加載網(wǎng)絡(luò)數(shù)據(jù)這個(gè)鏈?zhǔn)秸{(diào)用就是RxJava最大的特色现柠,用在這里邏輯就是,被觀察者Observable<Guokr>訂閱觀察者Observer<Guokr>弛矛,當(dāng)服務(wù)器一有response够吩,觀察者就會(huì)立即處理response result。因?yàn)?code>RxJava最大的亮點(diǎn)就是異步汪诉,可以很方便的切換當(dāng)前任務(wù)所在的線程废恋,并能對(duì)事件流進(jìn)行各種Map變換,比如壓合扒寄、轉(zhuǎn)換鱼鼓、緩存等操作。這里是最基本的用法该编,被觀察者直接把事件流訂閱到觀察者迄本,中間沒(méi)有做轉(zhuǎn)換處理。

到此網(wǎng)絡(luò)訪問(wèn)就完成了课竣,是不是很簡(jiǎn)潔嘉赎?簡(jiǎn)潔就對(duì)了,那是因?yàn)樘鄸|西Retrofit2和RxJava甚至是OkHttp都幫我們做好了于樟!再回顧一下整個(gè)網(wǎng)絡(luò)訪問(wèn)流程:創(chuàng)建Bean類 --> 創(chuàng)建接口形式的http 請(qǐng)求方法 --> 通過(guò)Retrofit.builder() 創(chuàng)建接口對(duì)象并調(diào)用http 方法請(qǐng)求網(wǎng)絡(luò)數(shù)據(jù) --> 在RxJavaObservable 中異步處理請(qǐng)求結(jié)果公条!

0X04 自定義OkHttp Interceptor實(shí)現(xiàn)日志輸出,保存和添加Cookie


在Retrofit2做網(wǎng)絡(luò)請(qǐng)求的第二步迂曲,我們需要通過(guò)Retrofit.builder()方法來(lái)創(chuàng)建Retrofit對(duì)象靶橱,其中client(mClient)這個(gè)方法指定一個(gè)OkHttpClient客戶端作為請(qǐng)求的執(zhí)行器,需要傳入一個(gè)OkHttpClient對(duì)象作為參數(shù),那么在這里关霸,我們就可以進(jìn)行一些OkHttp相關(guān)的操作传黄,比如自定義Interceptor,通過(guò)自定義Interceptor可以實(shí)現(xiàn)網(wǎng)絡(luò)請(qǐng)求日志的分級(jí)輸出队寇,可以實(shí)現(xiàn)保存和添加Cookie這些功能膘掰,當(dāng)然,這些功能的實(shí)現(xiàn)都是基于OkHttp佳遣,所以要對(duì)OkHttp有一定的了解才能靈活運(yùn)用识埋。

Retrofit使用指南-->OkHttp配合Retrofit使用 這篇博客在OkHttp配合Retrofit使用這一節(jié),關(guān)于OkHttpClient添加HttpLoggingInterceptor 進(jìn)行日志輸出苍日,以及如何設(shè)置SslSocketFactory做了詳細(xì)的說(shuō)明惭聂,有興趣的同學(xué)可以參考!值得注意的是相恃,如果后一次請(qǐng)求的URL辜纲,需要從前一次請(qǐng)求結(jié)果數(shù)據(jù)中獲取,這時(shí)候就需要?jiǎng)討B(tài)的改變BaseURL拦耐,也可通過(guò)自定義Interceptor 來(lái)實(shí)現(xiàn)這一需求耕腾,在BaseURL改變時(shí),只需要setHost()就可以讓下次請(qǐng)求的BaseURL改變杀糯,代碼如下:

public class DynamicBaseUrlInterceptor implements Interceptor {
    private volatile String host;

    public void setHost(String host) {
        this.host = host;
    }

    @Override
    public Response intercept(Chain chain) throws IOException {
        Request originalRequest = chain.request();
        if (!TextUtils.isEmpty(host)) {
            HttpUrl newUrl = originalRequest.url().newBuilder()
                    .host(host)
                    .build();
            originalRequest = originalRequest.newBuilder()
                    .url(newUrl)
                    .build();
        }

        return chain.proceed(originalRequest);
    }
}

那么怎樣在通過(guò)OkHttp保存和添加Cookie呢扫俺?其實(shí)實(shí)現(xiàn)原理和上面添加日志攔截器差不多,只是添加的Intercepter不同而已固翰,其實(shí)就是自定義了一個(gè)Interceptor接口實(shí)現(xiàn)類狼纬,接收和保存返回結(jié)果中的Cookie,或者添加Cookie骂际,最后疗琉,在創(chuàng)建OkHttp實(shí)例的時(shí)候,傳入以上Interceptor實(shí)現(xiàn)類的對(duì)象即可歉铝。Retrofit使用OkHttp保存和添加cookie這篇博客講的很好盈简,可以作為參考!

簡(jiǎn)而言之太示,以上這Retorfit2些高級(jí)運(yùn)用都是基于定制化OkHttp來(lái)實(shí)現(xiàn)的柠贤,如果想玩得很溜就必須對(duì)OkHttp了解一二,推薦看這篇博客OkHttp3源碼分析綜述类缤!最起碼需要弄清楚OkHttpClient自定義Interceptor這一塊內(nèi)容臼勉,推薦看OkHttp Github Wiki --> Interceptors

0X05 自定義ResponseConverter餐弱,自定義HTTP請(qǐng)求注解


默認(rèn)情況下坚俗,Retrofit會(huì)把HTTP響應(yīng)體反序列化到OkHttp的ResponseBody中镜盯,加入Converter可以將返回的數(shù)據(jù)直接格式化成你需要的樣子岸裙,Retrofit提供了如下6個(gè)Converter可以直接使用猖败,使用前需要加上相應(yīng)的Gradle依賴:

  • 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

在前面Retrofit2+RxJava實(shí)例中,我們指定GsonConverterFactory作為解析Json數(shù)據(jù)的Converter降允,當(dāng)面對(duì)更復(fù)雜的需求時(shí)恩闻,仍然可以通過(guò)繼承Converter.Factory 來(lái)自定義Converter,只需要重寫(xiě)以下這兩個(gè)方法即可:

  @Override
  public Converter<ResponseBody, ?> responseBodyConverter(Type type, Annotation[] annotations,
      Retrofit retrofit) {
        //your own implements
  }

  @Override
  public Converter<?, RequestBody> requestBodyConverter(Type type,
     Annotation[] parameterAnnotations, Annotation[] methodAnnotations, Retrofit retrofit) {
       //your own implements
  }

我們不妨來(lái)看看GsonConverterFactory 源碼剧董,果然GsonConverterFactory 也是繼承Converter.Factory 來(lái)實(shí)現(xiàn)的幢尚,重寫(xiě)了responseBodyConverterrequestBodyConverter 這兩個(gè)方法,代碼只有70多行還是很簡(jiǎn)潔的翅楼,如下:

public final class GsonConverterFactory extends Converter.Factory {
  /**
   * Create an instance using a default {@link Gson} instance for conversion. Encoding to JSON and
   * decoding from JSON (when no charset is specified by a header) will use UTF-8.
   */
  public static GsonConverterFactory create() {
    return create(new Gson());
  }

  /**
   * Create an instance using {@code gson} for conversion. Encoding to JSON and
   * decoding from JSON (when no charset is specified by a header) will use UTF-8.
   */
  public static GsonConverterFactory create(Gson gson) {
    return new GsonConverterFactory(gson);
  }

  private final Gson gson;

  private GsonConverterFactory(Gson gson) {
    if (gson == null) throw new NullPointerException("gson == null");
    this.gson = gson;
  }

  @Override
  public Converter<ResponseBody, ?> responseBodyConverter(Type type, Annotation[] annotations,
      Retrofit retrofit) {
    TypeAdapter<?> adapter = gson.getAdapter(TypeToken.get(type));
    return new GsonResponseBodyConverter<>(gson, adapter);
  }

  @Override
  public Converter<?, RequestBody> requestBodyConverter(Type type,
      Annotation[] parameterAnnotations, Annotation[] methodAnnotations, Retrofit retrofit) {
    TypeAdapter<?> adapter = gson.getAdapter(TypeToken.get(type));
    return new GsonRequestBodyConverter<>(gson, adapter);
  }
}

這里需要詳細(xì)解釋一下TypeAdapter<?> adapter = gson.getAdapter(TypeToken.get(type)) 中的TypeAdapter<?> 尉剩,TypeAdapte是Gson提供的自定義Json解析器,Type就是HTTP請(qǐng)求接口GuokrServicegetGuokrs()方法返回值的泛型類型毅臊,如果返回值類型是Call<T>理茎,那么這里的Type就是泛型類型 T ,如果返回值類型是Observable<List<Guokr>> 管嬉,那么Type就是List<Guokr>皂林;關(guān)于Gson的詳細(xì)用法可以參考:你真的會(huì)用Gson嗎?Gson使用指南(四)

我們看到responseBodyConverter 方法返回的是一個(gè)GsonResponseBodyConverter 對(duì)象蚯撩,跟進(jìn)去看一下GsonResponseBodyConverter 源碼础倍,也很簡(jiǎn)單,源碼 如下:

final class GsonResponseBodyConverter<T> implements Converter<ResponseBody, T> {
  private final Gson gson;
  private final TypeAdapter<T> adapter;

  GsonResponseBodyConverter(Gson gson, TypeAdapter<T> adapter) {
    this.gson = gson;
    this.adapter = adapter;
  }

  @Override public T convert(ResponseBody value) throws IOException {
    JsonReader jsonReader = gson.newJsonReader(value.charStream());
    try {
      return adapter.read(jsonReader);
    } finally {
      value.close();
    }
  }
}

我們看到GsonResponseBodyConverter<T> 實(shí)現(xiàn)了Converter<ResponseBody, T>胎挎,重寫(xiě)了convert(ResponseBody value) 方法沟启,這就給我們提供了一個(gè)思路:自定義Converter關(guān)鍵一步就是要實(shí)現(xiàn)Converter<ResponseBody, T> 接口并且重寫(xiě)convert(ResponseBody value) 方法,具體重寫(xiě)的代碼我就不貼出來(lái)了犹菇,可以參考如何使用Retrofit請(qǐng)求非Restful API 這篇博客自定義Converter的做法德迹!

另外,如果需求更復(fù)雜项栏,需要我們自定義HTTP請(qǐng)求方法的注解浦辨,又該怎么做呢?我們還注意到GsonConverterFactory 類的重寫(xiě)方法responseBodyConverter(Type type, Annotation[] annotations, Retrofit retrofit) 中的Annotation[] methodAnnotations 這個(gè)參數(shù)沼沈,對(duì)的流酬,或許你已經(jīng)猜到了,這就是我們?cè)贖TTP請(qǐng)求接口方法中定義的注解列另,先看 @GET 注解的源碼芽腾,如下:

/** Make a GET request. */
@Documented
@Target(METHOD)
@Retention(RUNTIME)
public @interface GET {
  String value() default "";
}

那我們自定義注解的思路也就有了,模仿上面 @GET 注解寫(xiě)一個(gè) @WONDERTWO 注解即可页衙。這里我點(diǎn)到即止摊滔,主要是提供一種思路阴绢,具體實(shí)現(xiàn)仍然可以參考上面提到的 如何使用Retrofit請(qǐng)求非Restful API 這篇博客自定義HTTP請(qǐng)求注解的做法!

0X06 寫(xiě)在后面


有一個(gè)結(jié)論說(shuō)的是在網(wǎng)絡(luò)上艰躺,只有 1% 的用戶貢獻(xiàn)了內(nèi)容呻袭,10% 的用戶比較活躍,會(huì)評(píng)論和點(diǎn)贊腺兴,剩下的都是網(wǎng)絡(luò)透明人左电,他們只是默默地在看,既不貢獻(xiàn)內(nèi)容页响,也不點(diǎn)贊篓足。這篇文章希望能讓你成為網(wǎng)絡(luò)上貢獻(xiàn)內(nèi)容的 TOP 1%。如果暫時(shí)做不到闰蚕,那就先點(diǎn)個(gè)贊吧栈拖,成為活躍的 10%。

參考文獻(xiàn)

  1. Retrofit官方文檔
  2. Getting started with Retrofit 2
  3. Consuming APIs with Retrofit
  4. Retrofit 2.0: The biggest update yet on the best HTTP Client Library for Android
  5. Retrofit使用指南
  6. Square全家桶正傳——Retrofit使用及配合RxJava實(shí)現(xiàn)最大效率開(kāi)發(fā)
  7. Retrofit使用OkHttp保存和添加cookie
  8. 如何使用Retrofit請(qǐng)求非Restful API
  9. Retrofit請(qǐng)求參數(shù)注解字段說(shuō)明
  10. OkHttp-->Interceptors
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末没陡,一起剝皮案震驚了整個(gè)濱河市涩哟,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌诗鸭,老刑警劉巖染簇,帶你破解...
    沈念sama閱讀 218,858評(píng)論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異强岸,居然都是意外死亡锻弓,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,372評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門(mén)蝌箍,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)青灼,“玉大人,你說(shuō)我怎么就攤上這事妓盲≡硬Γ” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 165,282評(píng)論 0 356
  • 文/不壞的土叔 我叫張陵悯衬,是天一觀的道長(zhǎng)弹沽。 經(jīng)常有香客問(wèn)我,道長(zhǎng)筋粗,這世上最難降的妖魔是什么策橘? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,842評(píng)論 1 295
  • 正文 為了忘掉前任,我火速辦了婚禮娜亿,結(jié)果婚禮上丽已,老公的妹妹穿的比我還像新娘。我一直安慰自己买决,他們只是感情好沛婴,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,857評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布吼畏。 她就那樣靜靜地躺著,像睡著了一般嘁灯。 火紅的嫁衣襯著肌膚如雪泻蚊。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 51,679評(píng)論 1 305
  • 那天旁仿,我揣著相機(jī)與錄音藕夫,去河邊找鬼。 笑死枯冈,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的办悟。 我是一名探鬼主播尘奏,決...
    沈念sama閱讀 40,406評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼病蛉!你這毒婦竟也來(lái)了炫加?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 39,311評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤铺然,失蹤者是張志新(化名)和其女友劉穎俗孝,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體魄健,經(jīng)...
    沈念sama閱讀 45,767評(píng)論 1 315
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡赋铝,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,945評(píng)論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了沽瘦。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片革骨。...
    茶點(diǎn)故事閱讀 40,090評(píng)論 1 350
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖析恋,靈堂內(nèi)的尸體忽然破棺而出良哲,到底是詐尸還是另有隱情,我是刑警寧澤助隧,帶...
    沈念sama閱讀 35,785評(píng)論 5 346
  • 正文 年R本政府宣布筑凫,位于F島的核電站,受9級(jí)特大地震影響并村,放射性物質(zhì)發(fā)生泄漏巍实。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,420評(píng)論 3 331
  • 文/蒙蒙 一橘霎、第九天 我趴在偏房一處隱蔽的房頂上張望蔫浆。 院中可真熱鬧,春花似錦姐叁、人聲如沸瓦盛。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,988評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)原环。三九已至挠唆,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間嘱吗,已是汗流浹背玄组。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,101評(píng)論 1 271
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留谒麦,地道東北人俄讹。 一個(gè)月前我還...
    沈念sama閱讀 48,298評(píng)論 3 372
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像绕德,于是被迫代替她去往敵國(guó)和親患膛。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,033評(píng)論 2 355

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