Android之RxJava詳解

来源:https://www.cnblogs.com/WUXIAOCHANG/archive/2019/04/03/10646434.html
-Advertisement-
Play Games

文章大綱 一、什麼是RxJava二、為什麼要用RxJava三、RxJava使用詳解四、項目源碼下載五、參考文章 一、什麼是RxJava Rx(Reactive Extensions)是一個庫,用來處理事件和非同步任務,在很多語言上都有實現,RxJava是Rx在Java上的實現。簡單來說,RxJava就 ...


文章大綱

一、什麼是RxJava
二、為什麼要用RxJava
三、RxJava使用詳解
四、項目源碼下載
五、參考文章

一、什麼是RxJava

  Rx(Reactive Extensions)是一個庫,用來處理事件和非同步任務,在很多語言上都有實現,RxJava是Rx在Java上的實現。簡單來說,RxJava就是處理非同步的一個庫,最基本是基於觀察者模式來實現的。通過Obserable和Observer的機制,實現所謂響應式的編程體驗。

二、為什麼要用RxJava

  比如說一個龐大的項目,一個事件傳遞的整個過程可能要經歷很多方法,方法套方法,每個方法的位置七零八落,一個個方法跳進去看,跳過去跳過來很容易把腦袋弄暈,不夠直觀。但是Rxjava可以把所有邏輯用鏈式加閉包的方式呈現,做了哪些操作,誰在前誰在後非常直觀,邏輯清晰,維護就會非常輕鬆。就算不是你寫的你也可以很快的瞭解,你可以把它看作一條河流,整個過程就是對裡面的水流做進行加工。懂了這個特性我們才知道在複雜的邏輯中運用Rxjava是多麼的重要。
  假設有這樣一個需求:界面上有一個自定義的視圖 imageCollectorView ,它的作用是顯示多張圖片,並能使用 addImage(Bitmap) 方法來任意增加顯示的圖片。現在需要程式將一個給出的目錄數組 File[] folders 中每個目錄下的 png 圖片都載入出來並顯示在 imageCollectorView 中。需要註意的是,由於讀取圖片的這一過程較為耗時,需要放在後臺執行,而圖片的顯示則必須在 UI 線程執行。常用的實現方式有多種,我這裡貼出其中一種:

new Thread() {
    @Override
    public void run() {
        super.run();
        for (File folder : folders) {
            File[] files = folder.listFiles();
            for (File file : files) {
                if (file.getName().endsWith(".png")) {
                    final Bitmap bitmap = getBitmapFromFile(file);
                    getActivity().runOnUiThread(new Runnable() {
                        @Override
                        public void run() {
                            imageCollectorView.addImage(bitmap);
                        }
                    });
                }
            }
        }
    }
}.start();

而如果使用 RxJava ,實現方式是這樣的:

Observable.from(folders)
    .flatMap(new Func1<File, Observable<File>>() {
        @Override
        public Observable<File> call(File file) {
            return Observable.from(file.listFiles());
        }
    })
    .filter(new Func1<File, Boolean>() {
        @Override
        public Boolean call(File file) {
            return file.getName().endsWith(".png");
        }
    })
    .map(new Func1<File, Bitmap>() {
        @Override
        public Bitmap call(File file) {
            return getBitmapFromFile(file);
        }
    })
    .subscribeOn(Schedulers.io())
    .observeOn(AndroidSchedulers.mainThread())
    .subscribe(new Action1<Bitmap>() {
        @Override
        public void call(Bitmap bitmap) {
            imageCollectorView.addImage(bitmap);
        }
    });

三、RxJava使用詳解

1. RxJava設計模式

  RxJava 的非同步實現,是通過一種擴展的觀察者模式來實現的。
  觀察者模式面向的需求是:A 對象(觀察者)對 B 對象(被觀察者)的某種變化高度敏感,需要在 B 變化的一瞬間做出反應。舉個例子,新聞里喜聞樂見的警察抓小偷,警察需要在小偷伸手作案的時候實施抓捕。在這個例子里,警察是觀察者,小偷是被觀察者,警察需要時刻盯著小偷的一舉一動,才能保證不會漏過任何瞬間。程式的觀察者模式和這種真正的『觀察』略有不同,觀察者不需要時刻盯著被觀察者(例如 A 不需要每過 2ms 就檢查一次 B 的狀態),而是採用註冊(Register)或者稱為訂閱(Subscribe)的方式,告訴被觀察者:我需要你的某某狀態,你要在它變化的時候通知我。 Android 開發中一個比較典型的例子是點擊監聽器 OnClickListener 。對設置 OnClickListener來說, View 是被觀察者, OnClickListener 是觀察者,二者通過 setOnClickListener() 方法達成訂閱關係。訂閱之後用戶點擊按鈕的瞬間,Android Framework 就會將點擊事件發送給已經註冊的 OnClickListener 。採取這樣被動的觀察方式,既省去了反覆檢索狀態的資源消耗,也能夠得到最高的反饋速度。當然,這也得益於我們可以隨意定製自己程式中的觀察者和被觀察者,而警察叔叔明顯無法要求小偷『你在作案的時候務必通知我』。

OnClickListener 的模式大致如下圖:

  OnClickListener 觀察者模式

RxJava 的觀察者模式
  RxJava 有四個基本概念:Observable (可觀察者,即被觀察者)、 Observer (觀察者)、 subscribe (訂閱)、事件。Observable 和 Observer 通過 subscribe() 方法實現訂閱關係,從而 Observable 可以在需要的時候發出事件來通知 Observer
  與傳統觀察者模式不同, RxJava 的事件回調方法除了普通事件 onNext() (相當於 onClick() / onEvent())之外,還定義了兩個特殊的事件:onCompleted() 和 onError()

  • onCompleted(): 事件隊列完結。RxJava 不僅把每個事件單獨處理,還會把它們看做一個隊列。RxJava 規定,當不會再有新的 onNext() 發出時,需要觸發 onCompleted() 方法作為標誌。
  • onError(): 事件隊列異常。在事件處理過程中出異常時,onError() 會被觸發,同時隊列自動終止,不允許再有事件發出。
  • 在一個正確運行的事件序列中, onCompleted() 和 onError() 有且只有一個,並且是事件序列中的最後一個。需要註意的是,onCompleted() 和 onError() 二者也是互斥的,即在隊列中調用了其中一個,就不應該再調用另一個。

RxJava 的觀察者模式大致如下圖:

  RxJava 的觀察者模式

開始接入RxJava之間,添加依賴

dependencies {
  compile 'io.reactivex:rxandroid:1.2.1'
  compile 'io.reactivex:rxjava:1.1.6'
  }

2. 創建RxJava幾種方式

方式1:簡單創建Rxjava

/**
     * 簡單創建Rxjava
     *
     * Observable是被觀察者,創建後傳入一個OnSubscribe對象,當Observable(觀察者)調用subscribe進行註冊觀察者時,OnSubscribe的call方法會觸發。
     ObservableEmitter: Emitter 是發射器的意思,它可以發出三種類型的事件,與之對應的。
     Observer有三個回調方法:

     onNext:接受到一個事件
     onCompleted:接受完事件後調用,只會調用一次
     onError :發生錯誤時調用,並停止接受事件,調用一次

     註:onCompleted和onError不會同時調用,只會調用其中之一
     */
    public static void createOne() {

        //創建被觀察者
        Observable observable = Observable.create(new Observable.OnSubscribe<String>() {
            @Override
            public void call(Subscriber<? super String> subscriber) {
                subscriber.onNext("Hello");
                subscriber.onNext("吳");
                subscriber.onNext("曉暢");
                subscriber.onCompleted();
            }
        });

        Subscriber<String> subscriber = new Subscriber<String>() {
            @Override
            public void onNext(String s) {

                System.out.println("Item: " + s);
            }

            ////事件隊列完結,RxJava 規定,當不會再有新的 onNext() 發出時,需要觸發 onCompleted() 方法作為標誌。
            @Override
            public void onCompleted() {

                System.out.println("Completed!");
            }

            ////事件隊列異常。在事件處理過程中出異常時,onError() 會被觸發,同時隊列自動終止,不允許再有事件發出。
            @Override
            public void onError(Throwable e) {
                System.out.println("Error!");
            }
        };

        observable.subscribe(subscriber);

    }

運行結果如下所示:

 

方式2:just(T...): 將傳入的參數依次發送出來

public static void createTwo()
    {

        //相當於
        // 將會依次調用:
        // onNext("Hello");
        // onNext("Hi");
        // onNext("Aloha");
        // onCompleted();
        Observable observable = Observable.just("Hello", "wu", "xiaochang");

        Subscriber<String> subscriber = new Subscriber<String>() {
            @Override
            public void onNext(String s) {

                System.out.println("Item: " + s);
            }

            ////事件隊列完結,RxJava 規定,當不會再有新的 onNext() 發出時,需要觸發 onCompleted() 方法作為標誌。
            @Override
            public void onCompleted() {

                System.out.println("Completed!");
            }

            ////事件隊列異常。在事件處理過程中出異常時,onError() 會被觸發,同時隊列自動終止,不允許再有事件發出。
            @Override
            public void onError(Throwable e) {
                System.out.println("Error!");
            }
        };

        observable.subscribe(subscriber);

    }

運行結果如下所示:

 

方式3:將傳入的數組或 Iterable 拆分成具體對象後,依次發送出來

 public static void createThree()
    {

        String[] words = {"Hello", "wu", "xiaochang"};

        //相當於
        // 將會依次調用:
        // onNext("Hello");
        // onNext("Hi");
        // onNext("Aloha");
        // onCompleted();
        Observable observable = Observable.from(words);

        Subscriber<String> subscriber = new Subscriber<String>() {
            @Override
            public void onNext(String s) {

                System.out.println("Item: " + s);
            }

            ////事件隊列完結,RxJava 規定,當不會再有新的 onNext() 發出時,需要觸發 onCompleted() 方法作為標誌。
            @Override
            public void onCompleted() {

                System.out.println("Completed!");
            }

            ////事件隊列異常。在事件處理過程中出異常時,onError() 會被觸發,同時隊列自動終止,不允許再有事件發出。
            @Override
            public void onError(Throwable e) {
                System.out.println("Error!");
            }
        };

        observable.subscribe(subscriber);
    }

運行結果如下圖所示:

 

方式4:發送多種類型參數

/**
     *發送多種類型參數
     */
    public static void createFour()
    {
        //Just類似於From,但是From會將數組或Iterable的元素具取出然後逐個發射,而Just只是簡單的原樣發射,將數組或Iterable當做單個數據。
        //Just接受一至九個參數,返回一個按參數列表順序發射這些數據的Observable
        Observable justObservable = Observable.just(1, "someThing", false, 3.256f, "NewYork");
        justObservable.subscribe(new Subscriber() {
            @Override
            public void onCompleted() {
                System.out.println("onCompleted!");
            }

            @Override
            public void onError(Throwable e) {
                System.out.println(e.getMessage());
            }

            @Override
            public void onNext(Object o) {

                System.out.println(o);
            }
        });
    }

運行結果如下所示:

 

方式5:自定義Subscriber

   /**
     * 自定義Subscriber
     */
    public static void createFive()
    {

        Observable observable = Observable.just("Hello", "Hi", "Aloha");

        Action1<String> onNextAction = new Action1<String>() {
            // onNext()
            @Override
            public void call(String s) {
                System.out.println(s);
            }
        };
        Action1<Throwable> onErrorAction = new Action1<Throwable>() {
            // onError()
            @Override
            public void call(Throwable throwable) {
                // Error handling
            }
        };
        Action0 onCompletedAction = new Action0() {
            // onCompleted()
            @Override
            public void call() {
                System.out.println("completed");
            }
        };

        // 自動創建 Subscriber ,並使用 onNextAction 來定義 onNext()
                observable.subscribe(onNextAction);
        // 自動創建 Subscriber ,並使用 onNextAction 和 onErrorAction 來定義 onNext() 和 onError()
                observable.subscribe(onNextAction, onErrorAction);
        // 自動創建 Subscriber ,並使用 onNextAction、 onErrorAction 和 onCompletedAction 來定義 onNext()、 onError() 和 onCompleted()
                observable.subscribe(onNextAction, onErrorAction, onCompletedAction);
    }

運行結果如下圖所示:

 

3. 創建觀察者方法

創建方式如下:

        Observer<String> observer = new Observer<String>() {
            @Override
            public void onNext(String s) {
                Log.d(tag, "Item: " + s);
            }

            @Override
            public void onCompleted() {
                Log.d(tag, "Completed!");
            }

            @Override
            public void onError(Throwable e) {
                Log.d(tag, "Error!");
            }
        };

        //創建方式2
        Subscriber<String> subscriber = new Subscriber<String>() {
            @Override
            public void onNext(String s) {
                Log.d("MainActivity", "Item: " + s);
            }

            @Override
            public void onCompleted() {
                Log.d("MainActivity", "Completed!");
            }

            @Override
            public void onError(Throwable e) {
                Log.d("MainActivity", "Error!");
            }
        };

  實質上,在 RxJava 的 subscribe 過程中,Observer 也總是會先被轉換成一個 Subscriber 再使用。所以如果你只想使用基本功能,選擇 Observer 和 Subscriber 是完全一樣的。它們的區別對於使用者來說主要有兩點:
  onStart(): 這是 Subscriber 增加的方法。它會在 subscribe 剛開始,而事件還未發送之前被調用,可以用於做一些準備工作,例如數據的清零或重置。這是一個可選方法,預設情況下它的實現為空。需要註意的是,如果對準備工作的線程有要求(例如彈出一個顯示進度的對話框,這必須在主線程執行), onStart() 就不適用了,因為它總是在 subscribe 所發生的線程被調用,而不能指定線程。要在指定的線程來做準備工作,可以使用 doOnSubscribe() 方法,具體可以在後面的文中看到。
  unsubscribe(): 這是 Subscriber 所實現的另一個介面 Subscription 的方法,用於取消訂閱。在這個方法被調用後,Subscriber 將不再接收事件。一般在這個方法調用前,可以使用 isUnsubscribed() 先判斷一下狀態。 unsubscribe() 這個方法很重要,因為在 subscribe() 之後, Observable 會持有 Subscriber 的引用,這個引用如果不能及時被釋放,將有記憶體泄露的風險。所以最好保持一個原則:要在不再使用的時候儘快在合適的地方(例如 onPause() onStop() 等方法中)調用 unsubscribe() 來解除引用關係,以避免記憶體泄露的發生。

3. 線程Scheduler (調度器)

  在不指定線程的情況下, RxJava 遵循的是線程不變的原則,即:在哪個線程調用 subscribe(),就在哪個線程生產事件;在哪個線程生產事件,就在哪個線程消費事件。如果需要切換線程,就需要用到 Scheduler (調度器)。
在RxJava 中,Scheduler ——調度器,相當於線程式控制制器,RxJava 通過它來指定每一段代碼應該運行在什麼樣的線程。RxJava 已經內置了幾個 Scheduler ,它們已經適合大多數的使用場景:
  Schedulers.immediate(): 直接在當前線程運行,相當於不指定線程。這是預設的 Scheduler。
  Schedulers.newThread(): 總是啟用新線程,併在新線程執行操作。
  Schedulers.io(): I/O 操作(讀寫文件、讀寫資料庫、網路信息交互等)所使用的 Scheduler。行為模式和 newThread() 差不多,區別在於 io() 的內部實現是是用一個無數量上限的線程池,可以重用空閑的線程,因此多數情況下 io() 比 newThread() 更有效率。不要把計算工作放在 io() 中,可以避免創建不必要的線程。
  Schedulers.computation(): 計算所使用的 Scheduler。這個計算指的是 CPU 密集型計算,即不會被 I/O 等操作限制性能的操作,例如圖形的計算。這個 Scheduler 使用的固定的線程池,大小為 CPU 核數。不要把 I/O 操作放在 computation() 中,否則 I/O 操作的等待時間會浪費 CPU。
另外, Android 還有一個專用的 AndroidSchedulers.mainThread(),它指定的操作將在 Android 主線程運行。
  有了這幾個 Scheduler ,就可以使用 subscribeOn() 和 observeOn() 兩個方法來對線程進行控制了。 * subscribeOn(): 指定 subscribe() 所發生的線程,即 Observable.OnSubscribe 被激活時所處的線程。或者叫做事件產生的線程。 * observeOn(): 指定 Subscriber 所運行在的線程。或者叫做事件消費的線程。

public class RxJavaScheduler {

    public static void showScheduler()
    {
        Observable.just(1, 2, 3, 4)
//                .subscribeOn(Schedulers.io()) // 指定 subscribe() 發生在 IO 線程

//                .observeOn(AndroidSchedulers.mainThread()) // 指定 Subscriber 的回調發生在主線程

                .subscribe(new Action1<Integer>() {
                    @Override
                    public void call(Integer number) {

                        System.out.println("number:" + number);
                    }
                });

    }


    public static void main(String[] args) {

        showScheduler();
    }
    

}

四、項目源碼下載

鏈接:https://pan.baidu.com/s/1Na7DH_N2rf-pXEadmQxgUQ
密碼:xvr2

五、參考文章

    1. http://gank.io/post/560e15be2dca930e00da1083
    2. https://blog.csdn.net/xu_song/article/details/78686439
    3. https://www.jianshu.com/p/387e4af55031

您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • [20190402]Library Cache mutex.txt1.環境:SCOTT@book> @ ver1PORT_STRING VERSION BANNER x86_64/Linux 2.4.xx 11.2.0.4.0 Oracle Database 11g Enterprise Editi ...
  • 本篇文章並非原創,只是看到其中內容講的非常好,搬過來,還望海涵。 原文鏈接地址:http://wpceo.com/user-database-table-design/ 說起用戶表,大概是每個應用/網站立項動工(碼農們)考慮的第一件事情。用戶表結構的設計,算是整個後臺架構的基石。如果基石不穩,待到後 ...
  • 索引模板就是將已經創建好的某個索引參數設置(settings)和索引映射(mapping)保存下來作為模板, 在創建新索引時, 指定使用某個模板就可以直接使用已經定義好的設置和映射. ...
  • Numpy 基礎操作¶ 以numpy的基本數據例子來學習numpy基本數據處理方法 主要內容有: 創建數組 數組維度轉換 數據選區和切片 數組數據計算 隨機數 數據合併 數據統計計算 In [1]: import numpy as np 創建一維數組¶ In [2]: data = np.arang ...
  • [20190402]關於semtimedop函數調用2.txt--//前幾天做了sql語句在mutexes上的探究.今天看看_mutex_wait_time設置很大的情況下是否semtimedop會喚醒.1.環境:SYS@book> @ hide mutexNAME DESCRIPTION DEFA ...
  • [20190402]對比_mutex_wait_scheme不同模式cpu消耗.txt--//前幾天做了sql語句在mutexes上的探究.今天對比不同_mutex_wait_scheme模式cpu消耗.1.環境:SYS@book> @ hide mutexNAME DESCRIPTION DEFA ...
  • 快捷鍵 ctrl+l 清屏 ctrl +a 回到行首 ctrl + e 回到行末 資料庫操作 進入資料庫 方式1 mysql -u用戶名 -p 密碼 直接輸入密碼,缺點,會暴露自己的密碼哦😝 方式2 mysql -u用戶名 -p 回車後輸入密碼 主要內容:查詢 1、查詢當前所有的資料庫 show ...
  • 文章大綱 一、OkHttp簡介二、OkHttp簡單使用三、OkHttp封裝四、項目源碼下載 一、OkHttp簡介 1. 什麼是OkHttp 一般在Java平臺上,我們會使用Apache HttpClient作為Http客戶端,用於發送 HTTP 請求,並對響應進行處理。比如可以使用http客戶端與第 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...