1. 程式人生 > >RxJava2.x的整合及用法詳解

RxJava2.x的整合及用法詳解

目錄

主頁

中文資料

Rxjava是什麼

首先要了解什麼是觀察者

Android 開發中一個比較典型的例子是點選監聽器 OnClickListener 。對設定 OnClickListener 來說, View 是被觀察者, OnClickListener 是觀察者,二者通過 setOnClickListener() 方法達成訂閱關係。訂閱之後使用者點選按鈕的瞬間,Android Framework 就會將點選事件傳送給已經註冊的 OnClickListener 。採取這樣被動的觀察方式,既省去了反覆檢索狀態的資源消耗,也能夠得到最高的反饋速度。
這裡寫圖片描述


如圖所示,通過 setOnClickListener() 方法,Button 持有 OnClickListener 的引用(這一過程沒有在圖上畫出);當用戶點選時,Button 自動呼叫 OnClickListener 的 onClick() 方法。另外,如果把這張圖中的概念抽象出來(Button -> 被觀察者、OnClickListener -> 觀察者、setOnClickListener() -> 訂閱,onClick() -> 事件),就由專用的觀察者模式(例如只用於監聽控制元件點選)轉變成了通用的觀察者模式。如下圖:
這裡寫圖片描述

用途

  • 非同步操作
  • 在邏輯複雜的情況下, 仍然可以讓程式碼邏輯保持簡潔

配置 新增依賴即可

com.android.tools.build:gradle 3.0 以上版本依賴在gradle 中的宣告寫法
    implementation 'io.reactivex.rxjava2:rxandroid:2.0.2'
    implementation 'io.reactivex.rxjava2:rxjava:2.x.y'com.android.tools.build:gradle 3.0 以下版本依賴在gradle 中的宣告寫法
    compile 'io.reactivex.rxjava2:rxandroid:2.0.2'
compile 'io.reactivex.rxjava2:rxjava:2.x.y'

基本概念

1. 被觀察者. Observable

    - 作用:決定什麼時候出發時間以及出發怎樣的事件
    - 建立方法:

        Observable.just (T t) //引數為單個的
        Observable.flapMap() //引數為多個的
        Observable.from (T [] t) //引數為陣列
        Observable.from (Iterable <? extends T>) //引數為Iterable (ArrayList的一種實現)

2. 觀察者. Observer

    - 作用:當事件觸發的時候將有怎樣的行為
    - 實現類:

        Observer  \ Subscriber

3. 訂閱. Subscribe

    - 作用:使被觀察者與觀察者建立聯絡
    -方法:       

           observable.subscribe(observer);
           observable.subscribe(subscriber);

4. 事件.

    onNext():普通事件
    onCompleted():時間佇列完結
    onError():時間佇列異常
    注意:onCompleted()onError()是互斥的,呼叫了其中一個另一個就不會被觸發

5. Scheduler.

     - 作用:控制執行緒,指定某一段程式碼在哪個執行緒執行
     - 內建的Scheduler

        Schedulers.newThread(); //總是啟用新執行緒,並在新的執行緒中執行操作
        Schedulers.io(); //I/O操作(讀寫檔案、讀寫資料庫、網路請求)所使用的執行緒。行為模式和newThread差不多,區別在於IO的內部實現的是一個無數量上線的執行緒池,可以重用空閒執行緒,因此多數情況下IO()比newThread()更有效率,不要把計算工作放在IO中,可以避免不必要的建立執行緒
        Schedulers.computation(); //計算使用的Schedulers,這個計算指的是CPU的密集型計算,既不會被I/O 等操作限制性能操作,例如圖形的計算,這個Scheduler使用的固定的執行緒池,大小為CPU的核數。不要把IO操作放在computation()中,否則IO操作等待時間會浪費CPU.
        Schedulers.trampoline(); //排程器會將任務按新增順序依次放入當前執行緒中等待執行(當前執行緒就是Schedulers.trampoline排程器建立Worker物件後,Worker物件呼叫scedule方法所在的執行緒)。執行緒一次只執行一個任務,其餘任務排隊等待,一個任務都執行完成後再開始下一個任務的執行。 
        Schedulers.single(); //擁有一個執行緒單例,所有的任務都在這一個執行緒中執行,當此執行緒中有任務執行時,其他任務將會按照先進先出的順序依次執行。
        Scheduler.from(@NonNull Executor executor); //指定一個執行緒排程器,由此排程器來控制任務的執行策略。
        AndroidSchedulers.mainThread(); //在Android UI執行緒中執行任務,為Android開發定製。
        Observable.subscribeOn(); //指subscribe() 所發生的執行緒,即Observable.onSubscribe被啟用時所處的執行緒。或者叫做事件產生的執行緒。
            //若多次設定,則只有一次起作用。
        Observable.observeOn(); //指Subscriber所執行在的執行緒,或者叫做事件的消費執行緒。(可以理解為現在要去渲染頁面了,需要到主執行緒中去)
            //若多次設定,每次均起作用。

- 案例1


    基本概念1-4應用
    遍歷陣列,找出陣列中b開頭的元素,將其轉換為大寫
    sample1:fori實現
    public class MainActivity extends AppCompatActivity {

        public static final String TAG = MainActivity.class.getSimpleName();
        String[] arr = {"shanghai", "beijing", "chengdu"};
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);

            for (int i = 0; i < arr.length ; i++) {
                if (arr[i].startsWith("b")){
                    arr[i] = arr[i].toUpperCase();
                    Log.i(TAG,arr[i]);
                }
            }

        }
    }
    輸出結果:06-30 10:48:02.487 747-747/com.kwunyamshan.rxjava I/MainActivity: BEIJING


    sample2:RxJava實現
    public class MainActivity extends AppCompatActivity {

        public static final String TAG = MainActivity.class.getSimpleName();
        String[] arr = {"shanghai", "beijing", "chengdu"};

        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
            //導包不要導錯 import io.reactivex.Observable;
            Observable<String> observable = Observable.fromArray(arr);
            //新增一個過濾器 , 把字母b開頭的元素過濾出來
            Observable<String> filterObservable = observable.filter(new Predicate<String>() {
                @Override
                public boolean test(String s) throws Exception {
                    return s.startsWith("b");
                }
            });
            //接收結果
            Observer<String> observer = new Observer<String>() {
                @Override
                public void onSubscribe(Disposable d) {}

                //真正處理事件的方法
                @Override
                public void onNext(String s) {
                    s = s.toUpperCase();
                    Log.i(TAG, s);
                }

                @Override
                public void onError(Throwable e) {}

                @Override
                public void onComplete() {}
            };

            //關聯觀察者與被觀察者
            filterObservable.subscribe(observer);
    }
    輸出結果:06-30 10:49:00.371 3261-3261/com.kwunyamshan.rxjava I/MainActivity: BEIJING


    sample3:RxJava實現(優化)
    看起來sample1中的程式碼比sample2中的程式碼還要簡潔,脾氣暴躁的大哥就要來幹我了,別急一步一步來你就明
        白了,下面是對RXJava進行了優化

    public class MainActivity extends AppCompatActivity {

        public static final String TAG = MainActivity.class.getSimpleName();
        String[] arr = {"shanghai", "beijing", "chengdu"};

        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
            Observable.fromArray(arr)
                    ////filter過濾以後返回的型別也是Observable, 所以變數可以刪去,鏈式程式設計
                    //Observable<String> filterObservable = observable.filter(new Predicate<String>() {
                    .filter(new Predicate<String>() {
                        @Override
                        public boolean test(String s) throws Exception {
                            return s.startsWith("b");
                        }
                    })
                    //這裡如果需要做一件事則不需要new Observer,
                    .subscribe(new Consumer<String>() {
                        @Override
                        public void accept(String s) throws Exception {
                            s = s.toUpperCase();
                            Log.i(TAG, s);
                        }
                    });
    }
    輸出結果:06-30 10:51:00.789 3261-3261/com.kwunyamshan.rxjava I/MainActivity: BEIJING
    你會發現這樣寫邏輯條理會變得特別清楚(如果會用lambda表示式的話則會更加的簡潔),一個方法做一件
        事情,再加多少個方法邏輯也不會亂,這樣寫不管過了多久再回過頭來看這段程式碼是不是也不迷糊了

- 案例2

    基本概念1-5應用
    獲取圖片,展示到ImageView上,出現異常時列印toast報錯(涉及到切換執行緒)

    sample1:不考慮執行緒問題,簡單實現11資料轉換(關鍵字just)
    public class MainActivity extends AppCompatActivity {

        public static final String TAG = MainActivity.class.getSimpleName();

        private ImageView ivAvatar;
        private int imgId = R.mipmap.avatar;

        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
            ivAvatar = findViewById(R.id.iv_avatar);
            //當物件是單個的時候呼叫just
            Observable.just(imgId)
                    //將圖片id轉化為drawable
                    .map(new Function<Integer, Drawable>() {
                        @Override
                        public Drawable apply(Integer integer) throws Exception {
                            return getResources().getDrawable(imgId);
                        }
                    })
                    //把drawable設定到ImageView上
                    .subscribe(new Consumer<Drawable>() {
                        @Override
                        public void accept(Drawable drawable) throws Exception {
                            ivAvatar.setImageDrawable(drawable);
                        }
                    });
    }


    sample2:增加執行緒切換,11資料轉換(關鍵字just)不瞭解執行緒切換的翻回去看 5. Scheduler
    public class MainActivity extends AppCompatActivity {

        public static final String TAG = MainActivity.class.getSimpleName();

        private ImageView ivAvatar;
        private int imgId = R.mipmap.avatar;

        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
            ivAvatar = findViewById(R.id.iv_avatar);
            //當物件是單個的時候呼叫just
            Observable
                    .just(imgId)

                    //將圖片id轉化為drawable, 比方這個地方走網路請求圖片的話這樣肯定是走不成了
                    //所以我們需要在這裡新增切換執行緒的方法
                    .subscribeOn(Schedulers.io())
                    //以下程式碼在子執行緒中執行
                    .map(new Function<Integer, Drawable>() {
                        @Override
                        public Drawable apply(Integer integer) throws Exception {
                            return getResources().getDrawable(imgId);
                        }
                    })
                    //把drawable設定到ImageView上 , 渲染頁面的邏輯在主執行緒中呼叫
                    .observeOn(AndroidSchedulers.mainThread())
                    //以下程式碼在主執行緒中執行
                    .subscribe(new Observer<Drawable>() {
                        @Override
                        public void onSubscribe(Disposable d) {}

                        @Override
                        public void onNext(Drawable drawable) {
                            ivAvatar.setImageDrawable(drawable);
                        }

                        @Override
                        public void onError(Throwable e) {
                            Toast.makeText(MainActivity.this,e.getMessage(),1).show();
                        }

                        @Override
                        public void onComplete() {}
                    });
        }

- 案例3

     基本概念1-5應用
     現在有兩張表,一張單位職工表,一張職工工資表,根據職工姓名(職工表)查詢職工每個月的工資(工資表)
     sample1:fori實現1對多查詢
    public class MainActivity extends AppCompatActivity {

        public static final String TAG = MainActivity.class.getSimpleName();

        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);

            SalaryBean jan = new SalaryBean("3000", 8);
            SalaryBean feb = new SalaryBean("3400", 9);
            SalaryBean mar = new SalaryBean("3000", 10);
            SalaryBean apr = new SalaryBean("3400", 11);
            SalaryBean may = new SalaryBean("5500", 12);

            List<SalaryBean> salaryList = new ArrayList<>();
            salaryList.add(jan);
            salaryList.add(feb);
            salaryList.add(mar);
            salaryList.add(apr);
            salaryList.add(may);
            StaffBean 禿頭李 = new StaffBean("李老闆", salaryList);

            for (int i = 0; i < 禿頭李.getmSalaryList().size(); i++) {
                SalaryBean salaryBean = 禿頭李.getmSalaryList().get(i);
                Log.i(TAG, "您" + salaryBean.getMonth() + "月份" + "的薪水:" + salaryBean.getSalary());
            }
        }
    }
    輸出結果:
    06-30 10:53:48.641 8985-8985/com.kwunyamshan.rxjava I/MainActivity: 您8月份的薪水:3000
    06-30 10:53:48.641 8985-8985/com.kwunyamshan.rxjava I/MainActivity: 您9月份的薪水:3400
    06-30 10:53:48.641 8985-8985/com.kwunyamshan.rxjava I/MainActivity: 您10月份的薪水:3000
    06-30 10:53:48.641 8985-8985/com.kwunyamshan.rxjava I/MainActivity: 您11月份的薪水:3400
    06-30 10:53:48.642 8985-8985/com.kwunyamshan.rxjava I/MainActivity: 您12月份的薪水:5500



    sample2:用RxJava實現1對多查詢(關鍵字flatMap)
    public class MainActivity extends AppCompatActivity {

        public static final String TAG = MainActivity.class.getSimpleName();

        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
            SalaryBean jan = new SalaryBean("3000", 8);
            SalaryBean feb = new SalaryBean("3400", 9);
            SalaryBean mar = new SalaryBean("3000", 10);
            SalaryBean apr = new SalaryBean("3400", 11);
            SalaryBean may = new SalaryBean("5500", 12);

            List<SalaryBean> salaryList = new ArrayList<>();
            salaryList.add(jan);
            salaryList.add(feb);
            salaryList.add(mar);
            salaryList.add(apr);
            salaryList.add(may);
            StaffBean 禿頭李 = new StaffBean("李老闆", salaryList);

            //查詢單個的員工用just
            Observable
                    .just(禿頭李)
                    //查詢員工每個月工資情況用flatMap
                    .flatMap(new Function<StaffBean, Observable<SalaryBean>>() {
                        @Override
                        public Observable<SalaryBean> apply(StaffBean staffBean) throws Exception {
                            return Observable.fromIterable(staffBean.getmSalaryList());

                        }
                    })
                    .subscribe(new Consumer<SalaryBean>() {
                        @Override
                        public void accept(SalaryBean salaryBeans) throws Exception {

                            Log.i(TAG,  "您" + salaryBeans.getMonth() + "月份" + "的薪水:" + salaryBeans.getSalary());
                        }
                    });

        }
    輸出結果:
    06-30 10:54:16.278 10183-10183/com.kwunyamshan.rxjava I/MainActivity: 您8月份的薪水:3000
    06-30 10:54:16.278 10183-10183/com.kwunyamshan.rxjava I/MainActivity: 您9月份的薪水:3400
    06-30 10:54:16.278 10183-10183/com.kwunyamshan.rxjava I/MainActivity: 您10月份的薪水:3000
    06-30 10:54:16.278 10183-10183/com.kwunyamshan.rxjava I/MainActivity: 您11月份的薪水:3400
    06-30 10:54:16.278 10183-10183/com.kwunyamshan.rxjava I/MainActivity: 您12月份的薪水:5500