RxHttp 一條鏈發送請求,新一代Http請求神器(一)

簡介

RxHttp是基於OkHttp的二次封裝,並於RxJava做到無縫銜接,一條鏈就能發送一個完整的請求。主要功能如下:

  • 支持Get、Post、Put、Delete等任意請求方式,可自定義請求方式
  • 支持Json、DOM等任意數據解析方式,可自定義數據解析器
  • 支持文件下載/上傳,及進度的監聽,並且支持斷點下載
  • 支持在Activity/Fragment的任意生命週期方法,自動關閉未完成的請求
  • 支持添加公共參數/頭部信息,且可動態更改baseUrl
  • 支持請求串行和並行

gradle依賴

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

RxHttp 源碼
RxLife 源碼

初始化

//設置debug模式,此模式下有日誌打印
HttpSender.setDebug(boolean debug)
//非必須,只能初始化一次,第二次將拋出異常
HttpSender.init(OkHttpClient okHttpClient)
//或者,調試模式下會有日誌輸出
HttpSender.init(OkHttpClient okHttpClient, boolean debug)

此步驟是非必須的,不初始化或者傳入null即代表使用默認OkHttpClient對象。

疑問:標題不是說好的是RxHttp,這麼用HttpSender做一些初始化呢?這裏先賣一個關子,後面會解答

添加公共參數/頭部及重新設置url

相信大多數開發者在開發中,都遇到要爲Http請求添加公共參數/請求頭,甚至要爲不同類型的請求添加不同的公共參數/請求頭,爲此,RxHttp爲大家提供了一個靜態接口回調,如下,每發起一次請求,此接口就會被回調一次,並且此回調在子線程進行(在請求執行線程回調)

HttpSender.setOnParamAssembly(new Function() {
    @Override
    public Param apply(Param p) { 
        if (p instanceof GetRequest) {//根據不同請求添加不同參數
        } else if (p instanceof PostRequest) {
        } else if (p instanceof PutRequest) {
        } else if (p instanceof DeleteRequest) {
        }
        //可以通過 p.getSimpleUrl() 拿到url更改後,重新設置
        //p.setUrl("");
        return p.add("versionName", "1.0.0")//添加公共參數
                .addHeader("deviceType", "android"); //添加公共請求頭
    }
});

然後有些請求我們不希望添加公共參數/請求頭,RxHttp又改如何實現呢?很簡單,發起請求前,設置不添加公共參數,如下:

Param param = Param.get("http://...")
        //設置是否對Param對象修飾,即是否添加公共參數,默認爲true
        .setAssemblyEnabled(false); //設爲false,就不會回調上面的靜態接口

到這,也許你們會有疑問,Param 是什麼東東,下面就爲大家講解。

Param

首先,我們來看看如何發送一個請求

Param param = Param.get("http://...")
        .add("key", "value");
Disposable disposable = HttpSender.from(param)
        .subscribe(s -> { //這裏的s爲String類型,即Http請求的返回結果
           //成功回調
        }, throwable -> {
           //失敗回調
        });

疑問:說好的一條鏈發送請求呢?彆着急,還沒到放大招的時候
到這,我可以告訴大家,Param承擔的是一個請求體的一個角色,我們通過Param可以確定請求方式(如:Get、Post、Put、Delete等請求方式)、添加請求參數、添加請求頭、添加File對象等;然後通過HttpSender,傳入Param對象,將請求發送出去。

HttpSender

到這,有人又有疑問,前面初始化、設置公共參數都用到了HttpSender,這裏發送請求又用到了HttpSender ,那麼它又是承擔怎麼樣的一個角色呢?看名字,我們可以理解爲它就是一個請求發送者,通過一個from操作符,傳入一個Param對象,然後返回一個RxJavaObservable對象,此時,我們就可以使用RxJava強大的操作符去處理相關的邏輯(這就是簡介說的,做到了與RxJava的無縫鏈接),在這,我們只是使用了subscribe操作符去訂閱觀察者。

RxHttp

現在,我們正式放大招,標題說好的一條鏈發送請求,既然吹牛了,就要去實現它。拿上面的例子,看看我們如何一條鏈實現,上代碼

  RxHttp.get("http://...")
        .add("key", "value")
        .from()  
        .subscribe(s -> { //這裏的s爲String類型,即Http請求的返回結果
           //成功回調
        }, throwable -> {
           //失敗回調
        });

我們的主角RxHttp終於登場了,可以看到使用RxHttp類我們就實現了一條鏈完成請求的發送,那它又是承擔一個什麼角色呢?我們暫時可以理解爲RxHttp=Param+HttpSender,並且還有自己特殊的使命。至於什麼使用,後面會講解。

我們現在來解疑惑,爲什麼我們的庫叫RxHttp,但是初始化、設置公共參數等卻用HttpSender?因爲RxHttp這個類不在RxHttp庫中,它是通過註解處理器生成的類。前面我們看到gradle依賴時,使用了

annotationProcessor 'com.rxjava.rxhttp:rxhttp-compiler:1.0.2'

該註解處理器的目的就是在項目中生成RxHttp類,那爲何不直接把它寫到庫裏面去呢?前面講過,因爲它有自己的使命,而這個使命,就是我們可以通過註解,在RxHttp中生成自定義的api,我們來看看如何使用註解。

動態設置baseUrl

現實開發中,大部人開發者都會將baseUrl 單獨抽取出來,RxHttp也考慮到了這一點,RxHttp通過@DefaultDomain註解來配置baseUrl,看代碼

public class Url {
    @DefaultDomain() //設置爲默認域名
    public static String baseUrl = "http://ip.taobao.com/";
}

rebuild一下項目,此時我們發送請求就可以直接傳入path路徑,如下:

  RxHttp.get("/service/getIpInfo.php")
        .add("key", "value")
        .from()  
        .subscribe(s -> { //這裏的s爲String類型,即Http請求的返回結果
           //成功回調
        }, throwable -> {
           //失敗回調
        });

RxHttp在發送請求前,會對url做判斷,如果沒有域名,就會自定加上默認的域名,也就是baseUrl。然後,如果我們不想使用默認的域名呢?RxHttp也考慮到來,提供了一個@Domain註解,我們再來看看用法:

public class Url {
    @Domain(name = "Update9158") //設置非默認域名,name 可不傳,不傳默認爲變量的名稱
    public static String update = "http://update.9158.com";

    @DefaultDomain() //設置爲默認域名
    public static String baseUrl = "http://ip.taobao.com/";
}

此時再rebuild一下項目,就會在RxHttp類中生成一個setDomainToUpdate9158IfAbsent()方法,其中的Update9158字符就是name指定的名字,然後發請求就可以這樣:

  RxHttp.get("/service/getIpInfo.php")
        .setDomainToUpdate9158IfAbsent()
        .add("key", "value")
        .from()  
        .subscribe(s -> { //這裏的s爲String類型,即Http請求的返回結果
           //成功回調
        }, throwable -> {
           //失敗回調
        });

此時,RxHttp檢測到url已經配置了域名,就不會再去使用默認的域名。同樣的,setDomainToUpdate9158IfAbsent也會檢測url 有沒有配置域名,如果配置了,也不會使用我們指定的域名。

注意:@Domain註解可以在多個地方使用,而@DefaultDomain()只能在一個地方使用,否則編譯不通過,很好理解,默認域名只可能有一個。兩個註解都要使用在public static修飾的String類型變量上,對final關鍵字沒有要求,可寫可不寫,這就表明,baseUrl 可以動態更改,RxHttp始終會拿到的最新的baseUrl 。怎麼樣,是不是很nice!!
更多註解使用請查看RxHttp 一條鏈發送請求之註解處理器 Generated API(四)

接下來,我們來看看,如何發送Post請求、如何在Activity/Fragment銷燬時,自動關閉爲完成的請求、如何上傳/下載文件及進度的監聽、如何把Http返回的結果自動解析成我們想要的對象。

注:以下講解均使用RxHttp

Post

  RxHttp.postForm("http://...")
        .add("key", "value")
        .from()  
        .subscribe(s -> { //這裏的s爲String類型,即Http請求的返回結果
           //成功回調
        }, throwable -> {
           //失敗回調
        });

可以看到,跟上面的Get請求只有一點不同,Get是RxHttp.get,而Post是RxHttp.postForm,除此之外,沒有任何區別,我們在看來來,RxHttp都有哪些靜態方法供我們選擇請求方式
在這裏插入圖片描述可以看到,默認提供了10個靜態方法供我們選擇具體的請求方式,有Get、Post、Put等,而Post等又分爲postForm和postJson,這個好理解,前者是發送表單形式的post請求,後者是發送json字符串的post請求。

現實中,這些默認的請求方式顯然不能滿足我們的需求,如:我要發送加密的post請求,這個時候該怎麼辦呢?此時就需要我們自定義請求方式。自定義請求方式請查看RxHttp 一條鏈發送請求之強大的Param類(三)

Activity 銷燬,自動關閉未完成的請求

上面的案例中,在Activity/Fragment銷燬時,如果請求還未完成,就會造成Activity/Fragment 無法回收,導致內存泄漏。這是非常嚴重的問題,那麼RxHttp是如何解決的呢?此時,就要引入我自己寫的另一個庫RxLife,直接看看如何使用

  RxHttp.postForm("http://...")
        .add("key", "value")
        .from()
        .as(RxLife.as(this)) //訂閱觀察者前,加上這句話即可
        .subscribe(s -> {
           //成功回調
        }, throwable -> {
           //失敗回調
        });
  //或者
  RxHttp.postForm("http://...")
        .add("key", "value")
        .from()
        .as(RxLife.asOnMain(this)) //asOnMain 可以在主線程回調觀察者
        .subscribe(s -> {
           //成功回調
        }, throwable -> {
           //失敗回調
        });

這裏的thisLifecycleOwner對象,它是一個接口,這裏我們傳入的是Activity,因爲Activity實現了LifecycleOwner接口。當Activity/Fragment銷燬時,會將RxJava的管道中斷,管道中斷時,又會將未完成的請求自動關閉。
RxLife不瞭解的同學請查看Android RxLife 一款輕量級別的RxJava生命週期管理庫 (一),這裏不詳細講解。在下面的講解中,我們均會使用RxLife

文件上傳/下載及進度監聽

使用RxHttp,可以很優雅的實現文件上傳/下載及進度的監聽,如何優雅?直接上代碼

文件上傳

  RxHttp.postForm("http://...") //發送Form表單形式的Post請求
        .add("key", "value")
        .add("file1", new File("xxx/1.png")) //添加file對象
        .add("file2", new File("xxx/2.png"))
        .from() //from操作符,是異步操作
        .as(RxLife.asOnMain(this))  //感知生命週期,並在主線程回調
        .subscribe(s -> { 
            //成功回調
        }, throwable -> {
            //失敗回調
        });

可以看到,文件上傳跟普通的post請求其實沒啥區別,無非就是在post請求的基礎上,調用add方法添加要上傳的文件對象。

文件下載

  //文件存儲路徑
  String destPath = getExternalCacheDir() + "/" + System.currentTimeMillis() + ".apk";
  RxHttp.get("http://update.9158.com/miaolive/Miaolive.apk")
        .download(destPath) //注意這裏使用download操作符,並傳入本地路徑
        .as(RxLife.asOnMain(this))  //感知生命週期,並在主線程回調
        .subscribe(s -> {
            //下載成功,回調文件下載路徑
        }, throwable -> {
            //下載失敗
        });

下載跟普通請求不同的是,下載使用的是download操作符,其它都一樣。

文件下載進度監聽

//文件存儲路徑
  String destPath = getExternalCacheDir() + "/" + System.currentTimeMillis() + ".apk";
  RxHttp.get("http://update.9158.com/miaolive/Miaolive.apk")
        .downloadProgress(destPath) //注:如果需要監聽下載進度,使用downloadProgress操作符
        .observeOn(AndroidSchedulers.mainThread())
        .doOnNext(progress -> {
            //下載進度回調,0-100,僅在進度有更新時纔會回調,最多回調101次,最後一次回調文件存儲路徑
            int currentProgress = progress.getProgress(); //當前進度 0-100
            long currentSize = progress.getCurrentSize(); //當前已下載的字節大小
            long totalSize = progress.getTotalSize();     //要下載的總字節大小
            String filePath = progress.getResult(); //文件存儲路徑,最後一次回調纔有內容
        })
        .filter(Progress::isCompleted)//下載完成,才繼續往下走
        .map(Progress::getResult) //到這,說明下載完成,返回下載目標路徑
        .as(RxLife.as(this)) //感知生命週期
        .subscribe(s -> {//s爲String類型,這裏爲文件存儲路徑
            //下載完成,處理相關邏輯
        }, throwable -> {
            //下載失敗,處理相關邏輯
        });

下載進度的監聽我們稍微看一下 ,首先一點,下載使用download操作符,而下載進度監聽使用downloadProgress操作符,隨後,我們使用了doOnNext操作符處理進度回調,注意這裏是僅當有進度更新時,纔會回調,其中的progress變量是一個Progress類型的對象,我們貼上源碼:

public class Progress<T> {
    private int  progress; //當前進度 0-100
    private long currentSize;//當前已完成的字節大小
    private long totalSize; //總字節大小
    private T mResult; //http返回結果,上傳/下載完成時調用
    //省略get/set方法
}

由於進度回調會執行101次(上面註釋有解釋),而最下面觀察者其實是不需要關心這麼多事件的,只需要關心最後下載完成的事件,所以使用了filter操作符過濾事件,只要還未下載完成,就將事件過濾調,不讓往下走。最終下載完成後,拿到本地下載路徑。

文件上傳進度監聽

  RxHttp.postForm("http://www.......") //發送Form表單形式的Post請求
        .add("file1", new File("xxx/1.png"))
        .add("file2", new File("xxx/2.png"))
        .add("key1", "value1")//添加參數,非必須
        .add("key2", "value2")//添加參數,非必須
        .addHeader("versionCode", "100") //添加請求頭,非必須
        .uploadProgress() //注:如果需要監聽上傳進度,使用uploadProgress操作符
        .observeOn(AndroidSchedulers.mainThread()) //主線程回調
        .doOnNext(progress -> {
            //上傳進度回調,0-100,僅在進度有更新時纔會回調,最多回調101次,最後一次回調Http執行結果
            int currentProgress = progress.getProgress(); //當前進度 0-100
            long currentSize = progress.getCurrentSize(); //當前已上傳的字節大小
            long totalSize = progress.getTotalSize();     //要上傳的總字節大小
            String result = progress.getResult(); //Http執行結果,最後一次回調纔有內容
        })
        .filter(Progress::isCompleted)//過濾事件,上傳完成,才繼續往下走
        .map(Progress::getResult) //到這,說明上傳完成,拿到Http返回結果並繼續往下走
        .as(RxLife.as(this))  //感知生命週期
        .subscribe(s -> { //s爲String類型,由SimpleParser類裏面的泛型決定的
            //上傳成功,處理相關邏輯
        }, throwable -> {
            //上傳失敗,處理相關邏輯
        });

上傳進度監聽使用downloadProgress操作符,剩下的操作跟下載進度監聽的操作都一樣,通過doOnNext監聽上傳進度,然後過濾事件,最終拿到Http的返回結果。

數據解析器Parser

在上面的案例中,觀察者拿到數據類型都是String類型,然後現實開發中,我們經常需要對數據解析成我們想要的對象,RxHttp考慮到了這一點,現在我們就來看看如何的到我們想要的對象

我們拿淘寶獲取IP的接口作爲測試接口http://ip.taobao.com/service/getIpInfo.php?ip=63.223.108.42
對應的數據結構如下

public class Response {
    private int     code;
    private Address data;
    //省略set、get方法

    class Address {
        //爲簡單起見,省略了部分字段
        private String country; //國家
        private String region; //地區
        private String city; //城市
        //省略set、get方法
    }
}

開始發送請求

  RxHttp.get("http://ip.taobao.com/service/getIpInfo.php") //Get請求
        .add("ip", "63.223.108.42")//添加參數
        .addHeader("accept", "*/*") //添加請求頭
        .addHeader("connection", "Keep-Alive")
        .addHeader("user-agent", "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1;SV1)")
        .fromSimpleParser(Response.class)  //這裏返回Observable<Response> 對象
        .as(RxLife.asOnMain(this))  //感知生命週期,並在主線程回調
        .subscribe(response -> {
            //成功回調
        }, throwable -> {
            //失敗回調
        });

可以看到,這裏我們沒有用from操作符,而是用了fromSimpleParser操作符,並且傳入Response.class,最後觀察者拿到的response變量就是Response類型的對象。怎麼樣,是不是很簡單。RxHttp爲我們提供了一系列的fromXXX方法,我們來看一下:
在這裏插入圖片描述
我們可以看到,一些基本類型的封裝對象RxHttp都爲我們封裝好了,還有一個fromListParser方法,此方法是用來解析集合對象的,一些常見的數據結構,RxHttp都爲我們考慮到了,並封裝好了,然後,一些不常見的數據呢?眼尖的你也許發現了,上圖中還有一個<T> Observable<T> from(Parser<T> parser)方法,它允許我們傳入一個自定義的解析器,更多解析器的介紹,請查看RxHttp 之強大的數據解析功能(二)

最後,附上RxHttp一些常用的用法,如下:

  RxHttp.postForm("/service/getIpInfo.php")       //發送Form表單形式的Post請求
        .setDomainToUpdate9158IfAbsent()  //手動設置域名,此方法是通過@Domain註解生成的
        .tag("RxHttp.get")          //爲單個請求設置tag
        .setUrl("http://...")       //重新設置url
        .setAssemblyEnabled(false)  //設置是否添加公共參數,默認爲true
        .cacheControl(CacheControl.FORCE_NETWORK)  //緩存控制
        .setParam(Param.postForm("http://..."))    //重新設置一個Param對象
        .add(new HashMap<>())   //通過Map添加參數
        .add("int", 1)          //添加int類型參數
        .add("float", 1.28838F) //添加float類型參數
        .add("double", 1.28838) //添加double類型參數
        .add("key1", "value1")  //添加String類型參數
        .add("key2", "value2", false) //根據最後的boolean字段判斷是否添加參數 
        .add("file1", new File("xxx/1.png"))            //添加文件對象
        .addHeader("headerKey1", "headerValue1")        //添加頭部信息
        .addHeader("headerKey2", "headerValue2", false)//根據最後的boolean字段判斷是否添加頭部信息 
        .fromSimpleParser(String.class)  //這裏返回Observable<T> 對象  fromXXX都是異步操作符
        //感知生命週期,並在主線程回調,當Activity/Fragment銷燬時,自動關閉未完成的請求
        .as(RxLife.asOnMain(this))  
        .subscribe(s -> {    //訂閱觀察者
            //成功回調
        }, throwable -> {
            //失敗回調
        });

小結

到這,RxHttp的基本用法我們就講解完畢了,可以看到,使用RxHttp類一條鏈就能完成一個完整的Http請求,簡單一點,就是請求三部曲:

  • 首先,確定請求方式並添加相關參數
  • 然後,確定解析器,指定要解析成的類型
  • 最後,訂閱觀察者,開始發送請求

以上所有的案例都離不開這3個步驟。最後,你會發現,RxHttp除了提供的一系列強大的功能外,在寫法上,不管什麼請求,都極其的相似,只要通過RxHttp類,就能一條鏈,完成所有的請求,極大的降低了學習成本。

注:要想在項目中生成RxHttp類,至少需要使用一次註解類,否則檢測不到註解,就無法生成。

如果你覺得RxHttp+RxLife好用,請記得給我star
如果有好的idea,請留言或者聯繫我。

更過詳情請查看RxHttp系列其它文章

RxHttp 一條鏈發送請求之強大的數據解析功能(二)

RxHttp 一條鏈發送請求之強大的Param類(三)

RxHttp 一條鏈發送請求之註解處理器 Generated API(四)

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章