Retrofit2.0+RxAndroid

Retrofit2.0+RxAndroid,最近看瞭很多關於Retrofit和Rxjava的文檔介紹。終於在弄清Rxjava後順利的弄懂瞭Retrofit。

網上有很多人都介紹瞭它們的聯合使用,但是我看過之後理解不是太好。可能我太笨。
不過,今天寫這篇博客的目的就是想來說說它們之間如何使用以及使用的時候遇到的坑。

這兩者的關系並不大,但是聯合在一起使用是非常簡便的。Rxjava的響應式編程加上Retrofit的註解式請求用起來是非常爽的。
並且Retrofit內置的是Okhttp,所以這更加的讓Retrofit變得強大。

如果在看這篇博客的時候你對Java註解、Rxjava還有Okhttp還不夠瞭解,建議先去瞭解這兩個東西。

相信看到這裡,你已經對上面三個知識有瞭瞭解。

那麼接下來切入正題。
先來加入依賴庫:

    compile 'io.reactivex:rxjava:1.0.14'
    compile 'io.reactivex:rxandroid:1.0.1'
    compile 'com.squareup.retrofit:adapter-rxjava:2.0.0-beta2'
    compile 'com.squareup.retrofit:retrofit:2.0.0-beta2'
    compile 'com.squareup.retrofit:converter-gson:2.0.0-beta2'

沒錯,你需要的庫就是這麼多。若要聯合它們三者使用,就必須這麼多。
我來按順序介紹。
1,Rxjava庫
2,RxAndroid庫
3,Retrofit適配Rxjava的庫
4,Retrofit庫
5,Retrofit適配Gson的庫(添加瞭這個庫後不用再添加Gson庫,因為已經內置)

另外還要有Okhttp依賴庫。在Androidsdk中已經內置。似乎Retrofit中也內置瞭Okhttp,所以我項目中沒有加入okhttp依賴庫,但是okhttp依舊可以使用。

這裡需要說明一下,第五個依賴庫根據你的項目需求來添加。
一般的項目來說都是使用json數據的。若你的項目是使用xml或者其他的數據格式,那麼對應的添加。
(以下版本號需要與retrofit版本號保持一致,並且以retrofit官網給出的版本號為準。)

1)Gson: compile 'com.squareup.retrofit2:converter-gson:2.0.1'

2)Jackson: compile 'com.squareup.retrofit2:converter-jackson:2.0.1'

3)Moshi: compile 'com.squareup.retrofit2:converter-moshi:2.0.1'

4)Protobuf: compile 'com.squareup.retrofit2:converter-protobuf:2.0.1'

5)Wire: compile 'com.squareup.retrofit2:converter-wire:2.0.1'

6)Simple XML: compile 'com.squareup.retrofit2:converter-simplexml:2.0.1'

7)Scalars (primitives, boxed, and String): compile 'com.squareup.retrofit2:converter-scalars:2.0.1'

好瞭。依賴庫加完以後。我們就開始請求瞭。

我們先來個測試url

private String ip = "http://gc.ditu.aliyun.com/geocoding?a=上海市&aa=松江區&aaa=車墩鎮";

這個是阿裡雲根據地區名獲取經緯度接口。
返回json數據

{
    "lon":120.58531,
    "level":2,
    "address":"",
    "cityName":"",
    "alevel":4,
    "lat":31.29888
}

我們先來寫好實體類AliAddrsBean和IndexRequestBean

public class AliAddrsBean {
    private double lon;
    private int level;
    private String address;
    private String cityName;
    private int alevel;
    private double lat;
     //get/set方法忽略
    }
public class IndexRequestBean {
    private String a;//一級城市
    private String aa;//二級城市
    private String aaa;//三級城市
    //get/set方法忽略
}

Retrofit的請求管理類RetrofitManage(先寫成單例)

private RetrofitManage() {
    }

    public static RetrofitManage getInstance() {
        return RetrofitManager.retrofitManage;
    }

    private static class RetrofitManager {
        private static final RetrofitManage retrofitManage = new RetrofitManage();
    }

定義一個發送網絡請求的方法sendRequest()

public void sendRequest(String url) {      
        //每一個Call實例可以同步(call.excute())或者異步(call.enquene(CallBack callBack))的被執行,
        //每一個實例僅僅能夠被使用一次,但是可以通過clone()函數創建一個新的可用的實例。
        //默認情況下,Retrofit隻能夠反序列化Http體為OkHttp的ResponseBody類型
        //並且隻能夠接受ResponseBody類型的參數作為@body
        Retrofit retrofit = new Retrofit
                .Builder()
                .baseUrl(url)
                .addCallAdapterFactory(RxJavaCallAdapterFactory.create()) // 使用RxJava作為回調適配器
                .addConverterFactory(GsonConverterFactory.create()) // 使用Gson作為數據轉換器
                .build();

Retrofit 實例化 註意事項:
1,每一個Call實例的用法和okhttp的call幾乎一樣。
2,call隻能被使用一次,若再次調用會拋出異常。如果需要多次使用請使用clone
3,默認反序列化OkHttp的ResponseBody類型
4,默認隻能接受ResponseBody類型的參數作為@body
5,2.0以後,get請求和post的請求的區別在call裡面,註解寫@get和寫@post已經沒有區別瞭
sendRequest()方法中已經寫的很清楚瞭,不再多說。

承載一切請求的接口ApiService

這個類我要分開寫,因為內容實在太多。

規則:每一個函數都必須有提供請求方式和相對URL的Http註解

Retrofit提供瞭5種內置的註解:GET、POST、PUT、DELETE和HEAD

註解中指定的資源是相對的URL
註解中指定的資源是相對的URL
註解中指定的資源是相對的URL

為啥說三遍,不解釋。
這是第一個細節也是第一個門檻。
註解裡到底寫的是什麼?

我們先來看看一個簡單的、迷茫的get請求。

@GET("search/repositories")
Call queryRetrofitByGetCall(
                                      @Query("name")String name,
                                      @Query("pwd")String pwd);

url呢?
註解裡寫的啥?
query又是啥?
怎麼調用?
怎麼返回?
這是我在網上看到最多的示例。
我就搞不懂瞭,給一個初學者寫這麼一個東西誰看得懂?

所以,我來一步一步的解釋清楚,寫一個易懂的例子。

先來回顧一下我們的url:http://gc.ditu.aliyun.com/geocoding?a=上海市&aa=松江區&aaa=車墩鎮

參照這個url我們來寫一個get請求:

@GET("http://gc.ditu.aliyun.com/geocoding?a=上海市&aa=松江區&aaa=車墩鎮")
    Call getIndexContent();

這麼看就明白瞭吧,註解中就是一個url而已。
看看怎麼調用的

Retrofit retrofit = new Retrofit
                .Builder()
                .baseUrl(url)         .addCallAdapterFactory(RxJavaCallAdapterFactory.create())              .addConverterFactory(GsonConverterFactory.create()) 
                .build();
        ApiService service = retrofit.create(ApiService.class);

        Call requestInde = service.getIndexContent();
        requestInde.execute();//同步請求,和okhttp的使用方法一樣

這就是最簡單粗暴的調用方法。這裡需要註意的是,我們還需要傳入一個實體類作為返回數據的解析依據。

這裡我們就是傳入的AliAddrsBean。Retrofit已經幫我們把數據解析好瞭。

可以看出來我們在外面調用瞭.baseUrl(url) ,為什麼在註解中還需要寫url呢?
這就關系到很多的細節和坑瞭。

現在我們假定我們的url是這樣的:http://gc.ditu.aliyun.com/geocoding?

如果我們在註解裡面這麼寫:
(以下有坑)

@GET("a=上海市&aa=松江區&aaa=車墩鎮")
    Call getIndexContentOne();

看似是對的,其實在我做demo的時候用okhttp請求成功但是用retrofit請求一直失敗。
究其原因就是:註解中必須要有一部分的url地址,不能光是請求體。

所以,修改代碼:
請求url:http://gc.ditu.aliyun.com/

以下是正解:

@GET("geocoding?a=上海市&aa=松江區&aaa=車墩鎮")
    Call getIndexContentOne();

這麼一寫,果然請求正常瞭。

另外還有一個坑,Retrofit建議url以/結束,註解不要以/開始

我們將get請求匯入接口ApiService中:

基本get請求

    //實際上在get的開始已經有一個rul存在瞭
    //例如,我們的url是“http://gc.ditu.aliyun.com/”
    //那麼get註解前就已經存在瞭這個url,並且使用{}替換符得到的最終url是:
    // http://gc.ditu.aliyun.com/geocoding?a=蘇州市
    //參數不能為null,且不能隻有url的參數,還應該包括地址的字段;正確:geocoding?a=蘇州市;錯誤:a=蘇州市
    @GET("geocoding?a=蘇州市")
    Call getIndexContentOne();

    @GET("http://gc.ditu.aliyun.com/geocoding?a=上海市&aa=松江區&aaa=車墩鎮")
    Call getIndexContent();

{ }取代塊和@Path

    //這裡需要註意的是,{}作為取代塊一定不能取代參數
    //它會報異常:URL query string "a={city}" must not have replace block. For dynamic query parameters use @Query.
    //翻譯:URL查詢字符串“= {城市}”必須沒有取代塊。動態查詢參數使用@Query。
    //所以,{}取代塊隻能替換url而不能替換參數參數應該用@query
    @GET("{parameters}?a=蘇州市")
    Call getIndexContentOne(
            @Path("parameters") String parameters);

所以,取代塊隻能取代url,不能取代參數,且@path的作用就是專職於取代塊
調用的時候把參數傳進來

Call requestInde = service.getIndexContentOne("geocoding");

@Query 鍵值對傳參

看到那麼多參數請求,肯定有簡單的方法,單個參數添加

//看到那麼多參數請求,肯定有簡單的方法,單個參數添加
    @POST("geocoding?")
    Call getIndexContentTow(
            @Query("a") String key1,
            @Query("aa") String key2,
            @Query("aaa") String key3
    );

調用:

Call requestInde = service.getIndexContentTow("蘇州市","蘇州市","蘇州市");

@query的作用就相當於拼接字符串:a=上海市&aa=松江區&aaa=車墩鎮

@QueryMap 參數集合

有時候我們參數很多,一個一個的用@query去傳肯定不方便。

//看到那麼多參數請求,肯定有簡單的方法,多個參數添加使用map
    @GET("geocoding?")
    Call getIndexContentThree(
            @QueryMap Map options
    );

調用:

Map map = new HashMap();
map.put("a", "上海市");
map.put("aa", "黃浦區");
Call requestInde = service.getIndexContentThree(map);

發佈留言

發佈留言必須填寫的電子郵件地址不會公開。 必填欄位標示為 *