RxHttp 讓你眼前一亮的Http請求框架

一、前言

RxHttp在今年4月份一經推出,就受到了廣大Android 開發者的喜好,截止本文發表在github上已有1100+star,爲此,我本身也建個RxHttp&RxLife 的羣(羣號:378530627)目前羣裏也有將近180號人,裏面有很多小夥伴提了不少有價值的創意,才使得RxHttp一直堅持走到了如今,在此,感謝你們的喜好。java

這期間,一直有人問我,retrofit不香嗎?以前不知道該如何回答這個問題,如今我想說,香!!retrofit無疑是目前綜合得分最高的選手,但它也有它的不足。android

RxHttp相較於retrofit,功能上,二者均能實現,並沒有多大差別,更多的差別體現功能的使用上,也就是易用性,如對文件上傳/下載/進度監聽的操做上,RxHttp用及簡的API,能夠說碾壓retrofit;另外在baseUrl、公共參數/請求頭、請求加解密等功能上的易用性都要優於retrofit;然而這些,我的以爲都不算什麼,我的以爲RxHttp最大的優點在於它近乎爲0的上手成本、極簡的API以及高擴展性,看完這篇文章,相信你會有同感。git

那RxHttp就沒有缺點嗎?有,那就是它的穩定性目前還不如retrofit,畢竟RxHttp剛出道8個月,且所有是我一我的在維護,固然,並非說RxHttp不穩定,RxHttp未開源前,在實際項目已經使用了近2年,接着今年4月份將其開源,至今大大小小已迭代20多個版本,目前用的人也不在少數,能夠說很穩定了。github

二、簡介

RxHttp是基於OkHttp的二次封裝,並與RxJava作到無縫銜接,一條鏈就能發送任意請求。主要優點以下:json

1. 支持Gson、Xml、ProtoBuf、FastJson等第三方數據解析工具api

2. 支持Get、Post、Put、Delete等任意請求方式,可自定義請求方式數組

3. 支持在Activity/Fragment/View/ViewModel/任意類中,自動關閉請求緩存

4. 支持統一加解密,且可對單個請求設置是否加解密網絡

5. 支持添加公共參數/頭部,且可對單個請求設置是否添加公共參數/頭部session

6. 史上最優雅的實現文件上傳/下載及進度的監聽,且支持斷點下載

7. 史上最優雅的對錯誤統一處理,且不打破Lambda表達式

8. 史上最優雅的處理多個BaseUrl及動態BaseUrl

9. 史上最優雅的處理網絡緩存

10. 30秒便可上手,學習成本極低

gradle依賴

implementation 'com.rxjava.rxhttp:rxhttp:1.3.6'
//註解處理器,生成RxHttp類,便可一條鏈發送請求
annotationProcessor 'com.rxjava.rxhttp:rxhttp-compiler:1.3.6'
//管理RxJava及生命週期,Activity/Fragment 銷燬,自動關閉未完成的請求
implementation 'com.rxjava.rxlife:rxlife:1.1.0'

//非必須 根據本身需求選擇Converter  RxHttp默認內置了GsonConverter
implementation 'com.rxjava.rxhttp:converter-jackson:1.3.6'
implementation 'com.rxjava.rxhttp:converter-fastjson:1.3.6'
implementation 'com.rxjava.rxhttp:converter-protobuf:1.3.6'
implementation 'com.rxjava.rxhttp:converter-simplexml:1.3.6'

注:kotlin用戶,請使用kapt替代annotationProcessor

緩存功能,請查看:RxHttp 全網Http緩存最優解

三、使用

3.一、準備工做

RxHttp 要求項目使用Java 8,請在 app 的 build.gradle 文件中添加如下代碼

compileOptions {
    sourceCompatibility JavaVersion.VERSION_1_8
    targetCompatibility JavaVersion.VERSION_1_8
}

此時,再Rebuild一下項目(經過Rebuild生成RxHttp類),就能夠開始RxHttp的入坑之旅

3.二、配置默認的BaseUrl

經過@DefaultDomain註解配置默認域名,以下:

public class Url {
    @DefaultDomain //設置爲默認域名
    public static String baseUrl = "https://www.wanandroid.com/";
}

此步驟是非必須的,這裏先介紹@DefaultDomain註解的用法,更多有關域名的介紹,請查看本文3.6章節----多域名/動態域名

3.三、請求三部曲

先來看看如何發送一個最簡單的請求,以下

RxHttp.get("http://...")  //第一步, 經過get、postXxx、putXxx等方法,肯定請求類型         
    .asString()           //第二步, 經過asXxx系列方法,肯定返回數據類型    
    .subscribe(s -> {     //第三步, 訂閱回調(此步驟同RxJava訂閱觀察者)
        //請求成功                                         
    }, throwable -> {                                  
        //請求失敗                                         
    });

是的,不用懷疑,就是這麼簡單,重要的事情說3遍

任意請求,任意返回數據類型,皆遵循請求三部曲

任意請求,任意返回數據類型,皆遵循請求三部曲

任意請求,任意返回數據類型,皆遵循請求三部曲

到這,你已經掌握了RxHttp的精髓,咱們只需牢記請求三部曲,使用RxHttp就會駕輕就熟。

3.3.一、第一部曲:肯定請求類型

RxHttp內部共提供了14個請求方法,以下:

RxHttp.get(String)              //get請求    參數拼接在url後面
RxHttp.head(String)             //head請求   參數拼接在url後面
RxHttp.postForm(String)         //post請求   參數以{application/x-www-form-urlencoded}形式提交
RxHttp.postJson(String)         //post請求   參數以{application/json; charset=utf-8}形式提交,發送Json對象
RxHttp.postJsonArray(String)    //post請求   參數以{application/json; charset=utf-8}形式提交,發送Json數組
RxHttp.putForm(String)          //put請求    參數以{application/x-www-form-urlencoded}形式提交
RxHttp.putJson(String)          //put請求    參數以{application/json; charset=utf-8}形式提交,發送Json對象
RxHttp.putJsonArray(String)     //put請求    參數以{application/json; charset=utf-8}形式提交,發送Json數組
RxHttp.patchForm(String)        //patch請求  參數以{application/x-www-form-urlencoded}形式提交
RxHttp.patchJson(String)        //patch請求  參數以{application/json; charset=utf-8}形式提交,發送Json對象
RxHttp.patchJsonArray(String)   //patch請求  參數以{application/json; charset=utf-8}形式提交,發送Json數組
RxHttp.deleteForm(String)       //delete請求 參數以{application/x-www-form-urlencoded}形式提交
RxHttp.deleteJson(String)       //delete請求 參數以{application/json; charset=utf-8}形式提交,發送Json對象
RxHttp.deleteJsonArray(String)  //delete請求 參數以{application/json; charset=utf-8}形式提交,發送Json數組

以上14個請求方法你會發現,其實就6個類型,分別對應是Get、Head、Post、Put、Patch、Delete方法,只是其中Post、Put、Patch、Delete各有3個方法有不一樣形式的提交方式,只須要根據本身的需求選擇就好。

如以上方法還不能知足你的需求,咱們還能夠經過@Param註解自定義請求方法,有關注解的使用,本文後續會詳細介紹。

注:當調用xxxForm方法發送請求時,經過setMultiForm()方法或者調用addFile(String, File)添加文件時,內部會自動將參數以{multipart/form-data}方式提交

添加參數/請求頭

肯定請求方法後,咱們就能夠調用一系列addXxx()方法添加參數/請求頭,以下:

RxHttp.get("/service/...")       //發送get請求
    .add("key", "value")         //添加參數
    .addAll(new HashMap<>())     //經過Map添加多個參數
    .addHeader("deviceType", "android")     //添加請求頭
    ...

任意請求,均可調用以上3個方法添加參數/請求頭,固然,在不一樣的請求方式下,也會有不一樣的addXxx方法供開發者調用。以下:

//postJson請求方法下會有更多addAll等方法可供調用
RxHttp.postJson("/service/...") //發送post Json請求
    .addAll(new JsonObject())   //經過json對象添加多個參數
    .addAll("{\"height\":180,\"weight\":70}") //經過json字符串添加多個參數
    ...

//postForm請求方法下會有一系列addFile方法可供調用
RxHttp.postForm("/service/...")  //發送post表單請求
    .addFile("file", new File("xxx/1.png")) //添加單個文件
    .addFile("fileList", new ArrayList<>()) //添加多個文件
    ...

以上只列出了幾個經常使用的addXxx方法,更多方法請下載源碼體驗。

3.3.二、第二部曲:肯定返回數據類型

添加好參數/請求頭後,正式進入第二部曲,肯定返回數據類型,咱們經過asXxx方法肯定返回類型,好比,咱們要返回一個Student對象,就能夠經過asObject(Class<T>)方法,以下:

RxHttp.postForm("/service/...")  //發送post表單請求
    .add("key", "value")         //添加參數,可調用屢次
    .asObject(Student.class)    //返回Student類型
    .subscribe(student -> {   
        //請求成功,這裏就能拿到 Student對象               
    }, throwable -> {         
        //請求失敗                
    });

若是要返回Student對象列表,則能夠經過asList(Class<T>)方法,以下:

RxHttp.postForm("/service/...")  //發送post表單請求
    .add("key", "value")         //添加參數,可調用屢次
    .asList(Student.class)       //返回List<Student>類型
    .subscribe(students -> {   
        //請求成功,這裏就能拿到 Student對象列表               
    }, throwable -> {         
        //請求失敗                
    });

解析Response<T>類型數據

然而,現實開發中,大多數人的接口,返回的數據結構都相似下面的這個樣子

public class Response<T> {
    private int    code;
    private String msg;
    private T      data;
    //這裏省略get、set方法
}

對於這種數據結構,按傳統的寫法,每次都要對code作判斷,若是有100個請求,就要判斷100次,真的會逼死強迫症患者。

RxHttp對於這種狀況,給出完美的答案,好比Response<T>裏面的T表明一個Student對象,則能夠經過asResponse(Class<T>)方法獲取,以下:

RxHttp.postForm("/service/...")   //發送post表單請求
    .add("key", "value")          //添加參數,可調用屢次
    .asResponse(Student.class)    //返回Student類型
    .subscribe(student -> {   
        //請求成功,這裏能拿到 Student對象               
    }, throwable -> {         
        //請求失敗                
    });

若是Response<T>裏面的T表明一個List<Student>列表對象,則能夠經過asResponseList(Class<T>)方法獲取,以下

RxHttp.postForm("/service/...")   //發送post表單請求
    .add("key", "value")          //添加參數,可調用屢次
    .asResponseList(Student.class)    //返回List<Student>類型
    .subscribe(students -> {   
        //請求成功,這裏能拿到List<Student>列表對象               
    }, throwable -> {         
        //請求失敗                
    });

更多時候,咱們的列表數據是分頁的,相似下面的數據結構

{
    "code": 0,
    "msg": "",
    "data": {
        "totalPage": 0,
        "list": []
    }
}

此時,調用RxHttp的asResponsePageList(Class<T>)方法依然能夠完美解決,以下:

RxHttp.postForm("/service/...")   //發送post表單請求
    .add("key", "value")          //添加參數,可調用屢次
    .asResponsePageList(Student.class)    //返回PageList<Student>類型
    .subscribe(pageList -> {   
        //請求成功,這裏能拿到PageList<Student>列表對象 
       int totalPage = pageList.getTotalPage();   //總頁數
       List<Student> students = pageList.getData();  //單頁列表數據        
    }, throwable -> {         
        //請求失敗                
    });

到這,估計不少人會問我:

  • 你的code在哪裏判斷的?
  • 個人code是100或者其它值才表明正確,怎麼改?
  • 個人Response<T>類裏面的字段名,跟你的都不同,怎麼該?
  • 你這成功的時候直接返回Response<T>裏面的T,那我還要拿到code作其餘的判斷,執行不一樣業務邏輯,怎麼辦?

這裏能夠先告訴你們,asResponse(Class<T>)asResponseList(Class<T>)asResponsePageList(Class<T>)這3個方法並非RxHttp內部提供的,而是經過自定義解析器生成,裏面的code判斷、Response<T>類都是開發者自定義的,如何自定義解析器,請查看本文5.1章節----自定義Parser。

接着回答第4個問題,如何拿到code作其餘的業務邏輯判斷,很簡單,咱們只需用OnError接口處理錯誤回調便可,以下:

RxHttp.postForm("/service/...")   //發送post表單請求
    .add("key", "value")          //添加參數,可調用屢次
    .asResponse(Student.class)    //返回Student類型
    .subscribe(student -> {   
        //請求成功,這裏能拿到 Student對象               
    }, (OnError) error -> {     //注意,這裏要用OnError接口,其中error是一個ErrorInfo對象  
        //失敗回調
        //拿到code字段,此時就能夠對code作判斷,執行不一樣的業務邏輯 
        int code = error.getErrorCode();     
        String errorMsg = error.getErrorMsg()  //拿到msg字段             
    });

注:上面的OnError接口並不是是RxHttp內部提供的,而是自定義的,在Demo裏能夠找到

以上介紹的5個asXxx方法,能夠說基本涵蓋80%以上的業務場景,接下來咱們看看RxHttp都提供了哪些asXxx方法,以下:
RxHttp內部共提供了23asXXX方法,其中:

  • 有7個是返回基本類型的包裝類型,如:asInteger、asBoolean、asLong等等;
  • 還有7個是返回對象類型,如:asString、asBitmap、asList、asMap(3個)以及最經常使用asObject方法;
  • 剩下9個是asParser(Parser<T>)asUpload系列方法及asDownload系列方法。

duang、duang、duang !!! 劃重點,這裏我能夠告訴你們,其實前面的14個方法,最終都是經過asParser(Parser<T>)方法實現的,具體實現過程,這裏先跳過,後續會詳細講解。

3.3.三、第三部曲:訂閱回調

這一步就很簡單了,在第二部曲中,asXxx方法會返回Observable<T>對象,沒錯,就是RxJava內部的Observable<T>對象,此時咱們即可經過subscribe系列方法訂閱回調,以下:

//不處理任何回調
RxHttp.postForm("/service/...")   //發送post表單請求
    .add("key", "value")          //添加參數,可調用屢次
    .asResponseList(Student.class)    //返回List<Student>類型
    .subscribe();    //不訂閱任何回調
    
//僅訂閱成功回調
RxHttp.postForm("/service/...")   //發送post表單請求
    .add("key", "value")          //添加參數,可調用屢次
    .asResponseList(Student.class)    //返回List<Student>類型
    .subscribe(students -> {   
        //請求成功,這裏能拿到List<Student>列表對象               
    });    
    
//訂閱成功與失敗回調
RxHttp.postForm("/service/...")   //發送post表單請求
    .add("key", "value")          //添加參數,可調用屢次
    .asResponseList(Student.class)    //返回List<Student>類型
    .subscribe(students -> {   
        //請求成功,這裏能拿到List<Student>列表對象               
    }, throwable -> {         
        //請求失敗                
    });
    
//等等,省略

另外,咱們還能夠訂閱請求開始/結束的回調,以下:

RxHttp.get("/service/...")
    .asString()
    .observeOn(AndroidSchedulers.mainThread())
    .doOnSubscribe(disposable -> {
        //請求開始,當前在主線程回調
    })
    .doFinally(() -> {
        //請求結束,當前在主線程回調
    })
    .as(RxLife.as(this))  //感知生命週期
    .subscribe(pageList -> {
        //成功回調,當前在主線程回調
    }, (OnError) error -> {
        //失敗回調,當前在主線程回調
    });

到這,請求三部曲介紹完畢,接着,將介紹其它經常使用的功能

3.四、初始化

//設置debug模式,默認爲false,設置爲true後,發請求,過濾"RxHttp"能看到請求日誌
RxHttp.setDebug(boolean debug)
//非必須,只能初始化一次,第二次將拋出異常
RxHttp.init(OkHttpClient okHttpClient)
//或者,調試模式下會有日誌輸出
RxHttp.init(OkHttpClient okHttpClient, boolean debug)

此步驟是非必須的,如須要添加攔截器等其餘業務需求,則可調用init方法進行初始化,不初始化或者傳入null即表明使用默認OkHttpClient對象,建議在Application中初始化,默認的OkHttpClient對象在HttpSender類中能夠找到,以下:

private static OkHttpClient getDefaultOkHttpClient() {                              
    X509TrustManager trustAllCert = new X509TrustManagerImpl();                     
    SSLSocketFactory sslSocketFactory = new SSLSocketFactoryImpl(trustAllCert);     
    return new OkHttpClient.Builder()                                               
        .connectTimeout(10, TimeUnit.SECONDS)                                       
        .readTimeout(10, TimeUnit.SECONDS)                                          
        .writeTimeout(10, TimeUnit.SECONDS)                                         
        .sslSocketFactory(sslSocketFactory, trustAllCert) //添加信任證書                  
        .hostnameVerifier((hostname, session) -> true) //忽略host驗證                   
        .build();                                                                   
}

雖然初始化是非必須的,可是建議你們傳入自定義的OkHttpClient對象,一來,自定義的OkHttpClient能最大化知足自身的業務;二來,隨着RxHttp版本的升級,默認的OkHttpClient可能會發生變化(雖然可能性很小),故建議自定義OkHttpClient對象傳入RxHttp。

3.五、公共參數/請求頭

RxHttp支持爲全部的請求添加公共參數/請求頭,固然,若是你但願某個請求不添加公共參數/請求頭,也是支持的,並且很是簡單。以下:

RxHttp.setOnParamAssembly(new Function() {
    @Override
    public Param apply(Param p) { //此方法在子線程中執行,即請求發起線程
        Method method = p.getMethod();
        if (method.isGet()) {     //可根據請求類型添加不一樣的參數
        } else if (method.isPost()) {
        }
        return p.add("versionName", "1.0.0")//添加公共參數
                .addHeader("deviceType", "android"); //添加公共請求頭
    }
});

咱們須要調用RxHttp.setOnParamAssembly(Function)方法,並傳入一個Function接口對象,每次發起請求,都會回調該接口。

固然,若是但願某個請求不回調該接口,即不添加公共參數/請求頭,則能夠調用setAssemblyEnabled(boolean)方法,並傳入false便可,以下:

RxHttp.get("/service/...")       //get請求 
    .setAssemblyEnabled(false)   //設置是否添加公共參數/頭部,默認爲true    
    .asString()                  //返回字符串數據    
    .subscribe(s -> {            //這裏的s爲String類型
        //請求成功                                         
    }, throwable -> {                                  
        //請求失敗                                         
    });

3.六、多域名/動態域名

3.6.一、多域名

現實開發中,咱們常常會遇到多個域名的狀況,其中1個爲默認域名,其它爲非默認域名,對於這種狀況,RxHttp提供了@DefaultDomain()@Domain()這兩個註解來標明默認域名和非默認域名,以下:

public class Url {
    @DefaultDomain() //設置爲默認域名
    public static String baseUrl = "https://www.wanandroid.com/"
    
    @Domain(name = "BaseUrlBaidu") //非默認域名,並取別名爲BaseUrlBaidu
    public static String baidu = "https://www.baidu.com/";
    
    @Domain(name = "BaseUrlGoogle") //非默認域名,並取別名爲BaseUrlGoogle
    public static String google = "https://www.google.com/";
}

經過@Domain()註解標註非默認域名,就會在RxHttp類中生成setDomainToXxxIfAbsent()方法,其中Xxx就是註解中取的別名。

上面咱們使用了兩個@Domain()註解,此時(須要Rebuild一下項目)就會在RxHttp類中生成setDomainToBaseUrlBaiduIfAbsent()setDomainToBaseUrlGoogleIfAbsent()這兩方法,此時發請求,咱們就可使用指定的域名,以下:

//使用默認域名,則無需添加任何額外代碼
//此時 url = "https://www.wanandroid.com/service/..." 
RxHttp.get("/service/...")
    .asString()  
    .subscribe();
    
//手動輸入域名,此時 url = "https://www.mi.com/service/..."
RxHttp.get("https://www.mi.com/service/...")
    .asString()  
    .subscribe();

//手動輸入域名時,若再次指定域名,則無效
//此時 url = "https://www.mi.com/service/..."
RxHttp.get("https://www.mi.com/service/...")
    .setDomainToBaseUrlBaiduIfAbsent()  //此時指定Baidu域名無效
    .asString()  
    .subscribe();
    
//使用谷歌域名,此時 url = "https://www.google.com/service/..."       
RxHttp.get("/service/...")
    .setDomainToBaseUrlGoogleIfAbsent() //指定使用Google域名
    .asString()  
    .subscribe();

經過以上案例,能夠知道,RxHttp共有3種指定域名的方式,按優先級排名分別是:手動輸入域名 > 指定非默認域名 > 使用默認域名。

3.6.二、動態域名

現實開發中,也會有動態域名切換的需求,如域名被封、或者須要根據服務端下發的域名去配置,這對於RxHttp來講簡直就是 so easy !!! 咱們只須要對BaseUrl從新賦值,此時發請求便會當即生效,以下:

//此時 url = "https://www.wanandroid.com/service/..."
RxHttp.get("/service/...")
    .asString()  
    .subscribe();
    
Url.baseUrl = "https://www.qq.com"; //動態更改默認域名,改完當即生效,非默認域名同理
//此時 url = "https://www.qq.com/service/..."
RxHttp.get("/service/...")
    .asString()  
    .subscribe();

3.七、關閉請求

咱們知道,在Activity/Fragment中發起請求,若是頁面銷燬時,請求還未結束,就會有內存泄漏的危險,所以,咱們須要在頁面銷燬時,關閉一些還未完成的請求,RxHttp提供了兩種關閉請求的方式,分別是自動+手動。

3.7.一、自動關閉請求

自動關閉請求,須要引入本人開源的另外一個庫RxLife,先來看看如何用:

//如下代碼均在FragmentActivty/Fragment中調用

RxHttp.postForm("/service/...")
    .asString()
    .as(RxLife.as(this)) //頁面銷燬、自動關閉請求
    .subscribe();
    //或者
RxHttp.postForm("/service/...")
    .asString()
    .as(RxLife.asOnMain(this)) //頁面銷燬、自動關閉請求 而且在主線程回調觀察者
    .subscribe();

//kotlin用戶,請使用life或lifeOnMain方法,以下:
RxHttp.postForm("/service/...")
    .asString()
    .life(this) //頁面銷燬、自動關閉請求
    .subscribe();
    //或者
RxHttp.postForm("/service/...")
    .asString()
    .lifeOnMain(this) //頁面銷燬、自動關閉請求 而且在主線程回調觀察者
    .subscribe();

上面的thisLifecycleOwner接口對象,咱們的FragmentActivity/Fragment均實現了這個接口,全部咱們在FragmentActivity/Fragment中能夠直接傳this
RxLife不瞭解的同窗請查看RxLife 史上最優雅的管理RxJava生命週期,這裏不詳細講解。

3.7.二、手動關閉請求

手動關閉請求,咱們只須要在訂閱回調的時候拿到Disposable對象,經過該對象能夠判斷請求是否結束,若是沒有,就能夠關閉請求,以下:

//訂閱回調,能夠拿到Disposable對象
Disposable disposable = RxHttp.get("/service/...")
    .asString()  
    .subscribe(s -> { 
       //成功回調
    }, throwable -> {
       //失敗回調
    });
    
if (!disposable.isDisposed()) {  //判斷請求有沒有結束
    disposable.dispose();       //沒有結束,則關閉請求
}

3.八、文件上傳/下載/進度監聽

RxHttp能夠很是優雅的實現上傳/下載及進度的監聽,是騾子是馬,拉出來溜溜

3.8.1上傳

經過addFile系列方法添加文件,以下:

RxHttp.postForm("/service/...") //發送Form表單形式的Post請求  
    .addFile("file1", new File("xxx/1.png"))  //添加單個文件      
    .addFile("fileList", new ArrayList<>())   //經過List對象,添加多個文件     
    .asString()                                      
    .subscribe(s -> {                              
        //上傳成功                                     
    }, throwable -> {                              
        //上傳失敗                                     
    });

經過asUpload系列方法監聽上傳進度,以下:

RxHttp.postForm("/service/...") //發送Form表單形式的Post請求                                    
    .addFile("file1", new File("xxx/1.png"))                                         
    .addFile("file2", new File("xxx/2.png"))                                         
    .asUpload(progress -> {                                                          
        //上傳進度回調,0-100,僅在進度有更新時纔會回調                                                  
        int currentProgress = progress.getProgress(); //當前進度 0-100                   
        long currentSize = progress.getCurrentSize(); //當前已上傳的字節大小                   
        long totalSize = progress.getTotalSize();     //要上傳的總字節大小                    
    }, AndroidSchedulers.mainThread())   //指定回調(進度/成功/失敗)線程,不指定,默認在請求所在線程回調                                           
    .subscribe(s -> {                                                                
        //上傳成功                                                                       
    }, throwable -> {                                                                
        //上傳失敗                                                                       
    });

能夠看到,跟上傳的代碼相比,咱們僅僅是使用了asUpload(Consumer, Scheduler)方法替換asString()方法,第一個參數是進度監聽接口,每當進度有更新時,都會回調該接口,第二個參數是指定回調的線程,這裏咱們指定了在UI線程中回調。

3.8.二、下載

下載使用asDownload(String)方法,傳入本地路徑便可

//文件存儲路徑
String destPath = getExternalCacheDir() + "/" + System.currentTimeMillis() + ".apk";
RxHttp.get("http://update.9158.com/miaolive/Miaolive.apk")
    .asDownload(destPath) //注意這裏使用asDownload操做符,並傳入本地路徑
    .subscribe(s -> {
        //下載成功,回調文件下載路徑
    }, throwable -> {
        //下載失敗
    });

3.8.三、帶進度下載

帶進度下載使用asDownload(String,Consumer,Scheduler)方法

//文件存儲路徑
String destPath = getExternalCacheDir() + "/" + System.currentTimeMillis() + ".apk";
RxHttp.get("http://update.9158.com/miaolive/Miaolive.apk")
    .asDownload(destPath, progress -> {
        //下載進度回調,0-100,僅在進度有更新時纔會回調,最多回調101次,最後一次回調文件存儲路徑
        int currentProgress = progress.getProgress(); //當前進度 0-100
        long currentSize = progress.getCurrentSize(); //當前已下載的字節大小
        long totalSize = progress.getTotalSize();     //要下載的總字節大小
    }, AndroidSchedulers.mainThread()) //指定主線程回調
    .subscribe(s -> {//s爲String類型,這裏爲文件存儲路徑
        //下載完成,處理相關邏輯
    }, throwable -> {
        //下載失敗,處理相關邏輯
    });

3.8.四、斷點下載

斷點下載相較於下載,僅須要調用setRangeHeader(long startIndex, long endIndex)方法傳入開始及結束位置便可(結束位置不傳默認爲文件末尾),其它沒有任何差異

String destPath = getExternalCacheDir() + "/" + "Miaobo.apk";
long length = new File(destPath).length(); //已下載的文件長度
RxHttp.get("http://update.9158.com/miaolive/Miaolive.apk")
    .setRangeHeader(length)  //設置開始下載位置,結束位置默認爲文件末尾
    .asDownload(destPath)
    .subscribe(s -> { //s爲String類型
        //下載成功,處理相關邏輯
    }, throwable -> {
        //下載失敗,處理相關邏輯
    });

3.8.五、帶進度斷點下載

帶進度斷點下載相較於帶進度下載僅須要調用setRangeHeader方法傳入開始及結束位置便可(結束位置不傳默認爲文件末尾),其它沒有任何差異

String destPath = getExternalCacheDir() + "/" + "Miaobo.apk";
long length = new File(destPath).length(); //已下載的文件長度
RxHttp.get("http://update.9158.com/miaolive/Miaolive.apk")
    .setRangeHeader(length)  //設置開始下載位置,結束位置默認爲文件末尾
    .asDownload(destPath, progress -> {
        //下載進度回調,0-100,僅在進度有更新時纔會回調
        int currentProgress = progress.getProgress(); //當前進度 0-100
        long currentSize = progress.getCurrentSize(); //當前已下載的字節大小
        long totalSize = progress.getTotalSize();     //要下載的總字節大小
    }, AndroidSchedulers.mainThread()) //指定主線程回調
    .subscribe(s -> { //s爲String類型
        //下載成功,處理相關邏輯
    }, throwable -> {
        //下載失敗,處理相關邏輯
    });

注:上面帶進度斷點下載中,返回的進度會從0開始,若是須要銜接上次下載的進度,則調用asDownload(String,long,Consumer,Scheduler)方法傳入上次已經下載好的長度(第二個參數),以下:

String destPath = getExternalCacheDir() + "/" + "Miaobo.apk";
long length = new File(destPath).length(); //已下載的文件長度
RxHttp.get("http://update.9158.com/miaolive/Miaolive.apk")
    .setRangeHeader(length)  //設置開始下載位置,結束位置默認爲文件末尾
    .asDownload(destPath, length, progress -> {
        //下載進度回調,0-100,僅在進度有更新時纔會回調
        int currentProgress = progress.getProgress(); //當前進度 0-100
        long currentSize = progress.getCurrentSize(); //當前已下載的字節大小
        long totalSize = progress.getTotalSize();     //要下載的總字節大小
    }, AndroidSchedulers.mainThread()) //指定主線程回調
    .subscribe(s -> { //s爲String類型
        //下載成功,處理相關邏輯
    }, throwable -> {
        //下載失敗,處理相關邏輯
    });

3.九、超時設置

3.9.一、設置全局超時

RxHttp內部默認的讀、寫、鏈接超時時間均爲10s,如需修改,請自定義OkHttpClient對象,以下:

//設置讀、寫、鏈接超時時間爲15s
OkHttpClient client = new OkHttpClient.Builder()
    .connectTimeout(15, TimeUnit.SECONDS)
    .readTimeout(15, TimeUnit.SECONDS)
    .writeTimeout(15, TimeUnit.SECONDS)
    .build();
RxHttp.init(client);

3.9.二、爲單個請求設置超時

爲單個請求設置超時,使用的是RxJava的timeout(long timeout, TimeUnit timeUnit)方法,以下:

RxHttp.get("/service/...")
    .asString()
    .timeout(5, TimeUnit.SECONDS)//設置總超時時間爲5s
    .as(RxLife.asOnMain(this))  //感知生命週期,並在主線程回調
    .subscribe(pageList -> {
        //成功回調
    }, (OnError) error -> {
        //失敗回調
    });

注:這裏設置的總超時時間要小於全局讀、寫、鏈接超時時間之和,不然無效

3.十、設置Converter

3.10.一、設置全局Converter

IConverter converter = FastJsonConverter.create();
RxHttp.setConverter(converter)

3.10.二、爲請求設置單獨的Converter

首先須要在任意public類中經過@Converter註解聲明Converter,以下:

public class RxHttpManager {
    @Converter(name = "XmlConverter") //指定Converter名稱
    public static IConverter xmlConverter = XmlConverter.create();
}

而後,rebuild 一下項目,就在自動在RxHttp類中生成setXmlConverter()方法,隨後就能夠調用此方法爲單個請求指定Converter,以下:

RxHttp.get("/service/...")
    .setXmlConverter()   //指定使用XmlConverter,不指定,則使用全局的Converter
    .asObject(NewsDataXml.class)
    .as(RxLife.asOnMain(this))  //感知生命週期,並在主線程回調
    .subscribe(dataXml -> {
        //成功回調
    }, (OnError) error -> {
        //失敗回調
    });

3.十一、請求加解密

3.11.一、加密

請求加密,須要自定義Param,很是簡單,詳情請查看本文5.2章節----自定義Param

3.11.二、解密

有些時候,請求會返回一大串的密文,此時就須要將密文轉化爲明文,直接來看代碼,以下:

//設置數據解密/解碼器                                               
RxHttp.setResultDecoder(new Function<String, String>() {
    //每次請求成功,都會回調這裏,並傳入請求返回的密文   
    @Override                                              
    public String apply(String s) throws Exception {   
        String plaintext = decode(s);   //將密文解密成明文,解密邏輯本身實現
        return plaintext;    //返回明文                                   
    }                                                      
});

很簡單,經過RxHttp.setResultDecoder(Function<String, String>)靜態方法,傳入一個接口對象,此接口會在每次請求成功的時候被回調,並傳入請求返回的密文,只須要將密文解密後返回便可。

然而,有些請求是不需求解密的,此時就能夠調用setDecoderEnabled(boolean)方法,並傳入false便可,以下:

RxHttp.get("/service/...")
    .setDecoderEnabled(false)  //設置本次請求不須要解密,默認爲true
    .asString()
    .subscribe(pageList -> {
        //成功回調
    }, (OnError) error -> {
        //失敗回調
    });

3.十二、指定請求/回調線程

RxHttp默認在Io線程執行請求,也默認在Io線程回調,即默認在同一Io線程執行請求並回調,固然,咱們也能夠指定請求/回調所在線程。

3.12.一、指定請求所在線程

咱們能夠調用一些列subscribeXxx方法指定請求所在線程,以下:

//指定請求所在線程,須要在第二部曲前任意位置調用,第二部曲後調用無效
RxHttp.get("/service/...")
    .subscribeOnCurrent() //指定在當前線程執行請求,即同步執行,
    .asString()  
    .subscribe();

//其它subscribeXxx方法
subscribeOnIo()   //RxHttp默認的請求線程
subscribeOnSingle()
subscribeOnNewThread()
subscribeOnComputation()
subscribeOnTrampoline()
subscribeOn(Scheduler) //自定義請求線程

以上使用的皆是RxJava的線程調度器,不熟悉的請自行查閱相關資料,這裏不作詳細介紹。

3.12.二、指定回調所在線程

指定回調所在線程,依然使用RxJava的線程調度器,以下:

//指定回調所在線程,須要在第二部曲後調用
RxHttp.get("/service/...")
    .asString()  
    .observeOn(AndroidSchedulers.mainThread()) //指定在主線程回調
    .subscribe(s -> { //s爲String類型,主線程回調
        //成功回調
    }, throwable -> {
        //失敗回調
    });

3.1三、 Retrofit用戶

時常會有童鞋問我,我是Retrofit用戶,喜歡把接口寫在一個類裏,而後能夠直接調用,RxHttp如何實現?其實,這個問題壓根就不是問題,在介紹第二部曲的時候,咱們知道,使用asXxx方法後,就會返回Observable<T>對象,所以,咱們就能夠這樣實現:

public class HttpWrapper {

    public static Observable<List<Student>> getStudent(int page) {
        return RxHttp.get("/service/...")
            .add("page", page)
            .asList(Student.class);
    }
}

//隨後在其它地方就能夠直接調用
HttpWrapper.getStudent(1)
    .as(RxLife.asOnMain(this))  //主線程回調,並在頁面銷燬自動關閉請求(若是還未關閉的話)
    .subscribe(students -> { //學生列表
        //成功回調
    }, throwable -> {
        //失敗回調
    });

很簡單,封裝的時候返回Observable<T>對象便可。

還有的同窗問,咱們獲取列表的接口,頁碼是和url拼接在一塊兒的,Retrofit能夠經過佔位符,那RxHttp又如何實現?簡單,以下:

public class HttpWrapper {

    //單個佔位符
    public static Observable<Student> getStudent(int page) {
        return RxHttp.get("/service/%d/...", page)  //使用標準的佔位符協議
            .asObject(Student.class);
    }
    
    //多個佔位符
    public static Observable<Student> getStudent(int page, int count) {
        return RxHttp.get("/service/%1$d/%2$d/...", page, count)  //使用標準的佔位符協議
            .asObject(Student.class);
    }
}

這一點跟Retrofit不一樣,Retrofit是經過註解指定佔位符的,而RxHttp是使用標準的佔位符,咱們只須要在url中聲明佔位符,隨後在傳入url的後面,帶上對應的參數便可。

四、原理剖析

4.一、工做流程

在RxHttp有4個重要的角色,分別是:

  • Param:RxHttp類中全部添加的參數/請求頭/文件都交由它處理,它最終目的就是爲了構建一個Request對象
  • HttpSender :它負責從Param對象中拿到Request對象,從而執行請求,最終返回Response對象
  • Parser:它負責將HttpSender返回的Response對象,解析成咱們指望的實體類對象,也就是泛型T
  • RxHttp:它像一個管家,指揮前面3個角色作事情,固然,它也有本身的事情要作,好比:請求線程的調度,BaseUrl的處理、容許開發者自定義API等等

爲此,我畫了一個流程圖,能夠直觀的瞭解到RxHttp的大體工做流程
在這裏插入圖片描述

我想應該很好理解,RxHttp要作的事情,就是把添加的參數/請求頭等所有丟給Param處理,本身啥事也不敢;隨後將Param交給HttpSender,讓它去執行請求,執行完畢,返回Response對象;接着又將Response對象丟給Parser去作數據解析工做,並返回實體類對象T;最後,將T經過回調傳給開發者,到此,一個請求就處理完成。

4.二、Param

首先,附上一張Param類的繼承關係圖

下面將從上往下對上圖中的類作個簡單的介紹:

  • IHeaders:接口類,裏面定義了一系列addHeader方法
  • IParam:接口類,裏面定義了add(String,Object)、addAll(Map)等方法,
  • IRequest:接口類,裏面定義了一系列getXxx方法,經過這些方法最終構建一個Request對象
  • Param:接口類,是一個空接口,繼承了前面3個接口,裏面有一系列靜態方法能夠獲取到Param的具體實現類
  • AbstractParam:Param接口的抽象實現類,實現了Param接口的全部方法
  • IFile:接口類,裏面定義了一系列addFile方法
  • IUploadLengthLimit:接口類,裏面就定義了一個checkLength()方法,用於限制文件上傳大小
  • NoBodyParam:Param的具體實現類,Get、Head請求就是經過該類去實現的
  • JsonParam:Param的具體實現類,調用RxHttp.xxxJson(String)請求方法時,內部就是經過該類去實現的
  • JsonArrayParam:Param的具體實現類,調用RxHttp.xxxJsonArray(String)請求方法時,內部就是經過該類去實現的
  • FormParam:Param的具體實現類,同時又實現了IFile、IUploadLengthLimit兩個接口,調用RxHttp.xxxForm(String)請求方法時,內部就是經過該類去實現的

4.三、HttpSender

HttpSender能夠把它理解爲請求發送者,裏面聲明OkHttpClient對象和一系列靜態方法,咱們來簡單看下:

public final class HttpSender {

    private static OkHttpClient mOkHttpClient; //只能初始化一次,第二次將拋出異常
    //處理化OkHttpClient對象
    public static void init(OkHttpClient okHttpClient) {
        if (mOkHttpClient != null)
            throw new IllegalArgumentException("OkHttpClient can only be initialized once");
        mOkHttpClient = okHttpClient;
    }

    //經過Param對象同步執行一個請求
    public static Response execute(@NonNull Param param) throws IOException {
        return newCall(param).execute();
    }

    static Call newCall(Param param) throws IOException {
        return newCall(getOkHttpClient(), param);
    }
    //全部的請求,最終都會調此方法拿到Call對象,而後執行請求
    static Call newCall(OkHttpClient client, Param param) throws IOException {
        param = RxHttpPlugins.onParamAssembly(param);
        if (param instanceof IUploadLengthLimit) {
            ((IUploadLengthLimit) param).checkLength();
        }
        Request request = param.buildRequest();  //經過Param拿到Request對象
        LogUtil.log(request);
        return client.newCall(request);
    }

    //省略了部分方法
}

這裏咱們重點看下newCall(OkHttpClient, Param)方法,該方法第一行就是爲Param添加公共參數;而後判斷Param有沒有實現IUploadLengthLimit接口,有的話,檢查文件上傳大小,超出大小,則拋出IO異常;接着就是經過Param拿到Request對象;最後拿到Call對象,就能夠發送一個請求。

4.四、Parser

先看下Parser繼承結構圖
在這裏插入圖片描述
這裏對上圖中的類作個簡單的介紹

  • Parser:接口類,裏面定義了一個T onParse(Response)方法,輸入Response對象,輸出實體類對象T
  • AbstractParser:抽象類,裏面沒有任何具體實現,主要做用是在構造方法內獲取泛型類型
  • SimpleParser:是一個萬能的解析器,能夠解析任意數據結構,RxHttp內置的大部分asXxx方法,內部就是經過該解析器實現的
  • ListParser:是一個列表解析器,能夠解析任意列表數據,內置asList(Class<T>)方法,就是經過該解析器實現的
  • MapParser:是一個Map解析器,能夠解析任意Map數據類型,內置的asMap系列方法,就是經過該解析器實現的
  • BitmapParser:是一個Bitmap解析器,經過該解析器能夠得到一個Bitmap對象,asBitmap()方法內部就是經過該解析器實現的
  • DownloadParser:文件下載解析器,用於文件下載,內置的一系列asDownload方法就是經過該解析器實現的

五、擴展

5.一、自定義Parser

前面第二部曲中,咱們介紹了一系列asXxx方法,經過該系列方法能夠很方便的指定數據返回類型,特別是自定義的asResponse(Class<T>)asResponseList(Class<T>)asResponsePageList(Class<T>)這3個方法,將Reponse<T>類型數據,處理的簡直不要太完美,下面咱們就來看看如何自定義Parser。

源碼永遠是最好的學習方式,在學習自定義Parser前,咱們不妨先看看內置的Parser是如何實現的

SimPleParser

public class SimpleParser<T> extends AbstractParser<T> {

    //省略構造方法
    @Override
    public T onParse(Response response) throws IOException {
        return convert(response, mType);
    }
}

能夠看到,SimpleParser除了構造方法,就剩一個onParser方法,該方法是在Parser接口中定義的,再來看看具體的實現convert(Response, Type),這個方法也是在Parser接口中定義的,而且有默認的實現,以下:

public interface Parser<T> {

    //輸入Response 輸出T
    T onParse(@NonNull Response response) throws IOException;

    //對Http返回的結果,轉換成咱們指望的實體類對象
    default <R> R convert(Response response, Type type) throws IOException {
        ResponseBody body = ExceptionHelper.throwIfFatal(response);  //這裏內部會判斷code<200||code>=300 時,拋出異常
        boolean onResultDecoder = isOnResultDecoder(response); //是否須要對返回的數據進行解密
        LogUtil.log(response, onResultDecoder, null);
        IConverter converter = getConverter(response);        //取出轉換器
        return converter.convert(body, type, onResultDecoder); //對數據進場轉換
    }
    //省略若干方法
}

能夠看到,很是的簡單,輸入Response對象和泛型類型Type,內部就經過IConverter接口轉換爲咱們指望的實體類對象並返回。

到這,我想你們應該就多少有點明白了,自定義Parser,無非就是繼承AbstractParser,而後實現onParser方法便可,那咱們來驗證一下,咱們來看看內置ListParser是否是這樣實現的,以下:

public class ListParser<T> extends AbstractParser<List<T>> {

    //省略構造方法
    @Override
    public List<T> onParse(Response response) throws IOException {
        final Type type = ParameterizedTypeImpl.get(List.class, mType); //拿到泛型類型
        return convert(response, type);
    }
}

能夠看到,跟SimpleParser解析器幾乎是同樣的實現,不一樣是的,這裏將咱們輸入的泛型T與List組拼爲一個新的泛型類型,最終返回List<T>對象。

如今,咱們就能夠來自定義Parser了,先來自定義ResponseParser,用來處理Response<T>數據類型,先看看數據結構:

public class Response<T> {
    private int    code;
    private String msg;
    private T      data;
    //這裏省略get、set方法
}

自定義ResponseParser代碼以下:

//經過@Parser註解,爲解析器取別名爲Response,此時就會在RxHttp類生成asResponse(Class<T>)方法
@Parser(name = "Response") 
public class ResponseParser<T> extends AbstractParser<T> {

    //省略構造方法
    @Override
    public T onParse(okhttp3.Response response) throws IOException {
        final Type type = ParameterizedTypeImpl.get(Response.class, mType); //獲取泛型類型
        Response<T> data = convert(response, type);
        T t = data.getData(); //獲取data字段
        if (data.getCode() != 0 || t == null) {//這裏假設code不等於0,表明數據不正確,拋出異常
            throw new ParseException(String.valueOf(data.getCode()), data.getMsg(), response);
        }
        return t;
    }
}

能夠看到,很是的簡單,首先將咱們輸入泛型和自定義的Response<T>類組拼成新的泛型類型,隨後經過convert(Response, Type)方法獲得Response<T>對象,接着又對code及T作了判斷,若是不正確就拋出異常,最後返回T。

估計這裏有人會問,我怎麼用這個解析器呢?相信很多小夥伴以及發現了,咱們在ResponseParser類名上面用了@Parser註解,只要用了該註解,就會在RxHttp自動生成asXxx(Class<T>)方法,其中Xxx就是咱們在@Parser註解中爲解析器取的別名,這裏取別名爲Response,因此便會在RxHttp類中自動生成asResponse(Class<T>)方法,以下:

public <T> Observable<T> asResponse(Class<T> type) {
    return asParser(new ResponseParser(type));
  }

能夠看到,該方法內部又調用了asParser(Parser<T>)方法,並傳入了ResponseParser,所以,咱們有兩種方式使用自定義的ResponseParser,以下:

//第一種方式,使用@parser註解生成的asResponse方法
RxHttp.postForm("/service/...")   //發送post表單請求
    .add("key", "value")          //添加參數,可調用屢次
    .asResponse(Student.class)    //返回Student類型
    .subscribe(student -> {   
        //請求成功,這裏能拿到 Student對象               
    }, throwable -> {         
        //請求失敗                
    });   

//第二種方式,直接使用asParser(Parser<T>)方法
RxHttp.postForm("/service/...")   //發送post表單請求
    .add("key", "value")          //添加參數,可調用屢次
    .asParser(new ResponseParser<Student>(){})    //返回Student類型
    .subscribe(student -> {   
        //請求成功,這裏能拿到 Student對象               
    }, throwable -> {         
        //請求失敗                
    });

以上兩種方式,除了寫法上的區別,其它都同樣,用哪一種,看我的喜愛,但仍是建議使用第一種方式,不只寫法簡單,也下降了耦合。

這裏最後再貼上ResponseListParser、ResponsePageListParser的源碼,原理都是同樣的,代碼實現也差很少,就再也不詳解
ResponseListParser

@Parser(name = "ResponseList")
public class ResponseListParser<T> extends AbstractParser<List<T>> {

    //省略構造方法
    @Override
    public List<T> onParse(okhttp3.Response response) throws IOException {
        final Type type = ParameterizedTypeImpl.get(Response.class, List.class, mType); //獲取泛型類型
        Response<List<T>> data = convert(response, type);
        List<T> list = data.getData(); //獲取data字段
        if (data.getCode() != 0 || list == null) {  //code不等於0,說明數據不正確,拋出異常
            throw new ParseException(String.valueOf(data.getCode()), data.getMsg(), response);
        }
        return list;
    }
}

ResponsePageListParser

@Parser(name = "ResponsePageList")
public class ResponsePageListParser<T> extends AbstractParser<PageList<T>> {
      
    //省略構造方法
    @Override
    public PageList<T> onParse(okhttp3.Response response) throws IOException {
        final Type type = ParameterizedTypeImpl.get(Response.class, PageList.class, mType); //獲取泛型類型
        Response<PageList<T>> data = convert(response, type);
        PageList<T> pageList = data.getData(); //獲取data字段
        if (data.getCode() != 0 || pageList == null) {  //code不等於0,說明數據不正確,拋出異常
            throw new ParseException(String.valueOf(data.getCode()), data.getMsg(), response);
        }
        return pageList;
    }
}

5.二、自定義Param

自定義Param,想較於自定義Parser,要更加的簡單,咱們只需根據本身的需求,繼承NoBodyParam、FormParam、JsonParam等,增長或者重寫方法便可,好比咱們有如下3種狀況,須要自定義Param,以下:

  • postForm請求,須要將全部添加的參數,拼接在一塊兒,隨後加密,最後將加密的字符串添加到請求頭中
  • postJson請求,須要將全部的參數,也就是json字符串加密後再發送出去
  • FormParam裏面的API不夠用,我要自定義API

5.2.一、postForm請求加密

這種狀況,咱們須要繼承FormParam,並重寫getRequestBody()方法,以下:

@Param(methodName = "postEncryptForm")
public class PostEncryptFormParam extends FormParam {

    public PostEncryptFormParam(String url) {
        super(url, Method.POST);  //Method.POST表明post請求
    }

    @Override
    public RequestBody getRequestBody() {
        //這裏拿到你添加的全部參數
        List<KeyValuePair> keyValuePairs = getKeyValuePairs();
        String encryptStr = "加密後的字符串";  //根據上面拿到的參數,自行實現加密邏輯
        addHeader("encryptStr", encryptStr);
        return super.getRequestBody();
    }
}

5.2.二、postJson請求加密

這種狀況,咱們須要繼承JsonParam,也重寫getRequestBody()方法,以下:

@Param(methodName = "postEncryptJson")
public class PostEncryptJsonParam extends JsonParam {

    public PostEncryptFormParam(String url) {
        super(url, Method.POST);
    }

    @Override
    public RequestBody getRequestBody() {
        //這裏拿到你添加的全部參數
        Map<String, Object> params = getParams();
        String encryptStr = "加密後的字符串";  //根據上面拿到的參數,自行實現解密邏輯
        return RequestBody.create(MEDIA_TYPE_JSON, encryptStr);  //發送加密後的字符串
    }
}

5.2.三、自定義API

咱們繼承FormParam,並新增兩個test方法`,以下:

@Param(methodName = "postTestForm")
public class PostTestFormParam extends FormParam {

    public PostEncryptFormParam(String url) {
        super(url, Method.POST);
    }
    
    public PostEncryptFormParam test(long a, float b) {
        //這裏的業務邏輯自行實現
        return this;
    }
    
    public PostEncryptFormParam test1(String s, double b) {
        //這裏的業務邏輯自行實現
        return this;
    }
}

5.2.四、使用自定義的Param

一樣的問題,咱們怎麼用這3個自定義的Param呢?我想大多數人在類名前發現類@Param註解,併爲Param取了別名。那這個又有什麼做用呢?
答案揭曉,只要在自定的Param上使用了@Param註解,並取了別名,就會在RxHttp類自動生成一個跟別名同樣的方法,在上面咱們自定義了3個Param,並分別取別名爲postEncryptForm、postEncryptJson、postTestForm,此時就會在RxHttp類中生成postEncryptForm(String)postEncryptJsonString)postTestForm(String)這3個方法,咱們在RxHttp這個類中來看下:

public static RxHttp$PostEncryptFormParam postEncryptForm(String url) {
    return new RxHttp$PostEncryptFormParam(new PostEncryptFormParam(url));
  }
  
  public static RxHttp$PostEncryptJsonParam postEncryptJson(String url) {
    return new RxHttp$PostEncryptJsonParam(new PostEncryptJsonParam(url));
  }

  public static RxHttp$PostTestFormParam postTestForm(String url) {
    return new RxHttp$PostTestFormParam(new PostTestFormParam(url));
  }

發請求時,只須要調用對應的方法就好,如:

//發送加密的postForm請求
RxHttp.postEncryptForm("/service/...")   
    .add("key", "value")          //添加參數,可調用屢次
    .asString()                  //返回String類型
    .subscribe(s-> {   
        //請求成功    
    }, throwable -> {         
        //請求失敗                
    });  
    
//發送加密的postJson請求
RxHttp.postEncryptJson("/service/...")   
    .add("key", "value")          //添加參數,可調用屢次
    .asString()                  //返回String類型
    .subscribe(s-> {   
        //請求成功    
    }, throwable -> {         
        //請求失敗                
    });

那我自定義的API如何調用呢,so easy!!!!,選擇對應的請求方法後,就能夠直接調用,以下:

//發送加密的postJson請求
RxHttp.postTestJson("/service/...")   
    .test(100L, 99.99F)          //調用自定義的API
    .test1("testKey", 88.88D)    //調用自定義的API
    .add("key", "value")         //添加參數,可調用屢次
    .asString()                  //返回String類型
    .subscribe(s-> {   
        //請求成功    
    }, throwable -> {         
        //請求失敗                
    });

5.三、自定義Converter

RxHttp內部默認使用來GsonConverter,而且額外提供了4個Converter,以下:

//非必須 根據本身需求選擇Converter  RxHttp默認內置了GsonConverter
implementation 'com.rxjava.rxhttp:converter-jackson:1.3.6'
implementation 'com.rxjava.rxhttp:converter-fastjson:1.3.6'
implementation 'com.rxjava.rxhttp:converter-protobuf:1.3.6'
implementation 'com.rxjava.rxhttp:converter-simplexml:1.3.6'

5.3.一、自定義TestConverter

即便這樣,RxHttp也沒法保證知足全部的業務需求,爲此,咱們能夠選擇自定義Converter,自定義Converter須要繼承IConverter接口,以下:

public class TestConverter implements IConverter {

    /**
     * 請求成功後會被回調
     * @param body             ResponseBody
     * @param type             泛型類型
     * @param onResultDecoder  是否須要對結果進行解碼/解密
     */
    @Override
    public <T> T convert(ResponseBody body, Type type, boolean onResultDecoder) throws IOException {
        //自行實現相關邏輯
        return null;
    }

    /**
     * json請求前會被回調,須要自行根據泛型T建立RequestBody對象,並返回
     */
    @Override
    public <T> RequestBody convert(T value) throws IOException {
        //自行實現相關邏輯
        return null;
    }
}

以上兩個convert方法根據自身業務需求自行實現,能夠參考RxHttp提供FastJsonConverter、SimpleXmlConverter等Converter

5.3.二、怎麼用Converter

請查看本文3.10章節----設置Converter

六、小技巧

在這教你們一個小技巧,因爲使用RxHttp發送請求都遵循請求三部曲,故咱們能夠在android studio 設置代碼模版,以下在這裏插入圖片描述
如圖設置好後,寫代碼時,輸入rp,就會自動生成模版,以下:
在這裏插入圖片描述

七、小結

到這,RxHttp經常使用功能介紹完畢,你會發現,一切都是那麼的美好,不管你是get、post、加密請求、自定義解析器,仍是文件上傳/下載/進度監聽等等,皆遵循請求三部曲。特別是對Response<T>類型數據處理,能夠說是完美無缺,咱們無需每次都判斷code,直接就能夠拿到T,簡直了。。。

最後,喜歡的,請給本文點個贊,若是能夠,還請給個star,創做不易,感激涕零。🙏🙏🙏

相關文章
相關標籤/搜索