RxHttp是基於OkHttp的二次封裝,並於RxJava作到無縫銜接,一條鏈就能發送一個完整的請求。主要功能以下:php
gradle依賴java
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'
//設置debug模式,此模式下有日誌打印 HttpSender.setDebug(boolean debug) //非必須,只能初始化一次,第二次將拋出異常 HttpSender.init(OkHttpClient okHttpClient) //或者,調試模式下會有日誌輸出 HttpSender.init(OkHttpClient okHttpClient, boolean debug)
此步驟是非必須的,不初始化或者傳入null
即表明使用默認OkHttpClient對象。git
疑問:標題不是說好的是RxHttp,這麼用HttpSender作一些初始化呢?這裏先賣一個關子,後面會解答
github
相信大多數開發者在開發中,都遇到要爲Http請求添加公共參數/請求頭,甚至要爲不一樣類型的請求添加不一樣的公共參數/請求頭,爲此,RxHttp爲你們提供了一個靜態接口回調,以下,每發起一次請求,此接口就會被回調一次,而且此回調在子線程進行(在請求執行線程回調)json
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又改如何實現呢?很簡單,發起請求前,設置不添加公共參數,以下:api
Param param = Param.get("http://...") //設置是否對Param對象修飾,便是否添加公共參數,默認爲true .setAssemblyEnabled(false); //設爲false,就不會回調上面的靜態接口
到這,也許大家會有疑問,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
對象,將請求發送出去。app
到這,有人又有疑問,前面初始化、設置公共參數都用到了HttpSender,這裏發送請求又用到了HttpSender ,那麼它又是承擔怎麼樣的一個角色呢?看名字,咱們能夠理解爲它就是一個請求發送者,經過一個from
操做符,傳入一個Param
對象,而後返回一個RxJava
的Observable
對象,此時,咱們就可使用RxJava強大的操做符去處理相關的邏輯(這就是簡介說的,作到了與RxJava的無縫連接),在這,咱們只是使用了subscribe
操做符去訂閱觀察者。
如今,咱們正式放大招,標題說好的一條鏈發送請求,既然吹牛了,就要去實現它。拿上面的例子,看看咱們如何一條鏈實現,上代碼
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 單獨抽取出來,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
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/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 -> { //失敗回調 });
這裏的this
爲LifecycleOwner
對象,它是一個接口,這裏咱們傳入的是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的返回結果。
在上面的案例中,觀察者拿到數據類型都是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系列其它文章