android rxJava

一、什么是rxJava

這篇文章是看了給 Android 開發者的 RxJava 詳解
之后的一些自己描述。

一個詞:異步
rxJava在github主頁上的介紹是
"a library for composing asynchronous and event-based programs using observable sequences for the Java VM"
大概的意思就是一個在 Java VM 上使用可觀測的序列來組成異步的、基于事件的程序的庫。

其實rxJava的本質就是一個詞,異步,它就是一個異步操作的庫。

二、rxJava的好處

簡潔
異步操作的比較關鍵的一點就是程序的簡潔,在調用復雜的異步操作的時候,代碼回顯得很復雜,不僅難寫也很難懂。雖然android 創造的asynTask和handler 都是為了讓代碼更加簡潔。
rxJava的優勢在于,隨著程序邏輯越來越復雜,代碼依然很清晰

三、rxJava的基本原理

rxJava實現異步,是通過擴展觀察者模式來實現的。

首先,講述下,觀察者模式
觀察者模式即是,a對象對b對象的某一個動作特別關注,做著密切的觀察,當a對象做出了這個動作的時候,b對象立刻做出相應的處理。就好比android中的點擊事件(onClickListener),

onClickListener.jpg

當button按鈕被點擊的時候,觀察者對這個點擊事件做出自己的反應

轉變為通用的觀察者模式如下:

rxJava.jpg

observable被觀察者,在做出某一事件的時候,通知observer觀察者做出處理

RxJava使用的就是通用型的觀察者模式。

RxJava觀察者模式

rxJava有四個基本概念,observable(可觀察者、被觀察者)、observer(觀察者)、subscrib(訂閱)。
observable和observer通過subscrib實現訂閱的關系,在observable需要的時候,發送通知給observer。

和傳統的觀察者模式不同,rxJava的回調事件除了onNext事件意外(相當于onClick,Onevent事件),還定義了兩個特殊的事件:onCompleted()、onError()

onCompleted():事件結束觸發。rxJava不僅僅將事件單獨處理,還會把他們作為一個隊列,在沒有onNext()事件觸發的時候,通過調用omCompleted()作為結束

onError():事件隊列異常觸發。當事件隊列發生異常的時候調研onError(),同時事件隊列停止,不執行任何事件了。

在隊列事件中,onCompleted()和onError()是相互對立的,兩者正常只會有一個調用。

rxJava觀察者模式,大致如下:

rxJava觀察者.jpg
rxJava基本實現

rxJava基本實現主要有三個要素

(1)創建observer

observer是觀察者,它覺得事件觸發的時候將什么樣的行為。RxJava中observer的借口實現方式如下:

    Observer<String>observer=new Observer<String>() {
                @Override
                public void onCompleted() {

                }

                @Override
                public void onError(Throwable e) {

                }

                @Override
                public void onNext(String s) {

                }
            };

除了observer借口之外,rxJava還實現了一個observer的抽象類subscriber。subscriber對observer進行了一些擴展,但是兩者使用方法基本一樣。

    //subscriber
            Subscriber<String>subscriber=new Subscriber<String>() {
                @Override
                public void onCompleted() {

                }

                @Override
                public void onError(Throwable e) {

                }

                @Override
                public void onNext(String s) {

                }
            };

不僅僅是使用基本一樣,rxJava在subscrib建立觀察的時候,也是講observer轉化為subscriber在使用的。
所以observer功能可能只是subscriber的一部分,但是也是能滿足基本的使用

observer和subscriber的區別:

1.onStart():這個是有subscriber提供的方法,是在事件發生之前的調用,可以用于一些數據的初始化,例如數據的添加,刪除,清空等。但是onStart()只能發生在subscrib所在的線程中,不能再獨立的線程中使用,所以使用的時候需要注意,一些只能在ui線程中使用的,可能不能再onStart()中使用。

2.unSubscribe(),這個是subscriber提供的另外一個接口,通常在調用之前會先調用isUnSubscribe()判斷一下是否已經取消觀察了。unSubscriber()方法很重要,因為,observable在subscribe的過程中會持有observer對象,所以在合適的時候,需要將對象釋放,防止內存泄漏。可以在onpause()或者onStop()的時候。

(2.)創建observable

observable是被觀察者,它覺得了什么時候觸發事件,以及觸發什么樣的事件。

    //observable
            Observable<String>observable=Observable.create(new Observable.OnSubscribe<String>() {
                @Override
                public void call(Subscriber<? super String> subscriber) {

                }
            });

在onsubscribe中做一些事情,在某些特殊的事情的時候,可以釣魚subscriber的onNext()、onCompleted()、onError()等事件,通知observer或者subscriber事件發生了。

(3.)subscrib訂閱

已經創建了observable和observer,下面只需要用subscrib將兩者聯系起來。

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

Observable.subscrib(subscriber)方法的核心代碼如下:

    // 注意:這不是 subscribe() 的源碼,而是將源碼中與性能、兼容性、擴展性有關的代碼剔除后的核心代碼。
    // 如果需要看源碼,可以去 RxJava 的 GitHub 倉庫下載。
    public Subscription subscribe(Subscriber subscriber) {
        subscriber.onStart();
        onSubscribe.call(subscriber);
        return subscriber;
    }

可以看到subscrib()做了三個工作:

  1. 調用onStart(),在開始之前的一些準備工作。
  2. onSubscrible.call(subscriber),事件的邏輯開始運行,可以看出,observable不是在創建的時候開始發送消息,而是在subscrib訂閱的時候開始。
  3. 將observer對象返回,是為了方便unSubscrib()。
事件發生流程.gif

除了observable(observer)或者observable(subscriber)之外,rxJava還支持定義一些不完全的回調,如下

    Observable<String>observable=Observable.create(new Observable.OnSubscribe<String>() {
                @Override
                public void call(Subscriber<? super String> subscriber) {

                }
            });
            Action1<String>onNext=new Action1<String>() {
                @Override
                public void call(String s) {

                }
            };
            Action1<Throwable>onError=new Action1<Throwable>() {
                @Override
                public void call(Throwable s) {

                }
            };
            Action0 onCompleted=new Action0() {
                @Override
                public void call() {

                }
            };

            observable.subscribe(onNext);
            observable.subscribe(onNext,onError);
            observable.subscribe(onNext,onError,onCompleted);
舉例子
  1. 打印字符串數組

     String[] array={"a","b","c"};
    
             Observable.from(array)
                     .subscribe(new Action1<String>() {
                         @Override
                         public void call(String s) {
                             Log.e("call==",s);
                         }
                     });
    

2.由id取得突破,并且顯示

    //由id取出突破,并顯示
            ImageView imageView = new ImageView(this);
            int[] drables = {R.mipmap.ic_launcher, R.mipmap.ic_launcher, R.mipmap.ic_launcher};

            Observable.create(new Observable.OnSubscribe<Drawable>() {
                @Override
                public void call(Subscriber<? super Drawable> subscriber) {
                    try {
                        for (int i = 0; i < drables.length; i++) {
                            Drawable draw = getResources().getDrawable(drables[i]);
                            subscriber.onNext(draw);
                        }
                        subscriber.onCompleted();
                    } catch (Exception e) {
                        e.printStackTrace();
                        subscriber.onError(e);
                    }

                }
            })
                    .subscribe(new Action1<Drawable>() {
                                   @Override
                                   public void call(Drawable drawable) {
                                        imageView.setImageDrawable(drawable);
                                   }
                               },
                            new Action1<Throwable>() {
                                @Override
                                public void call(Throwable throwable) {
                                    Toast.makeText(UndefindSubscriberActivity.this,throwable.toString(),Toast.LENGTH_LONG);
                                }
                            },
                            new Action0() {
                                @Override
                                public void call() {
                                    Toast.makeText(UndefindSubscriberActivity.this,"完成了",Toast.LENGTH_LONG);
                                }
                            });
(4.)線程控制Scheduler

在不指定線程的情況下,rxJava遵循線程不變的原則,即,在哪個線程調用subscrib(),就在哪個線程產生時間,在哪個線程產生時間,就在哪個線程消耗時間。
如果需要切換線程,就需要用到scheduler(調度器)。

  1. scheduler(調度器),在rxJava中,scheduler相當于線程控制器,在rxJava中利用他們來表明每一段代碼在什么樣的線程中使用。rxJava中已經內置了幾個scheduler,他們已經適合大部分的場景
  • Schedulers.immediate():直接在當前線程中使用,即,不指定線程,默認就是這個值
    *Schedulers.newThread():總是啟用一個新的線程,并在新的線程中運行。
    *Schedulers.IO():IO操作所使用的線程(讀寫文件、數據庫操作、網絡操作等)。io線程和newThread線程差不多,區別在于,io線程是一個沒有上線的線程池,很好的維護了創建的線程,可以復用空閑的線程。所以正常情況下,io線程要比newThread線程要好一些。
    *Scheduler.computation():計算用的線程,例如圖像的計算。這個計算是CPU的密集型計算,所以很消耗CPU,使用的是固定的線程。
    *AndroidSchedulers.mainThread():這是android專用的主線程。

有了這幾個線程之后,就可以使用subscribOn()或者observeOn(),來指定運行在哪個線程了。subscribOn()是指
subscrib所在的線程,即observable.subscrib()的時候,調用者是observable,所以指定的是時間產生時候的線程。observeOn()指定的是observer的使用線程,即消費事件所在的線程。回憶一下,observable.subscrib()調用之后得到的是observer,所以observeOn()的調用者是observer。

個人理解:在哪個線程中使用,主要是看調用者是誰,是observable就是事件產生的時候,是observer就是事件消費的時候。

舉例說明:

    Observable.just(1,2,3,4)
                    .subscribeOn(Schedulers.io())//指定 subscribe() 事件產生發生在 IO 線程
                    .observeOn(AndroidSchedulers.mainThread())//指定 Subscriber 消費事件的回調發生在主線程
                    .subscribe(new Action1<Integer>() {
                        @Override
                        public void call(Integer integer) {

                        }
                    });
(5.)變換

RxJava提供了對事件序列的變化功能,簡單說就是在事件運行的過程中,對事件傳遞對象進行加工和變化成另外一個對象。這也是rxJava的核心功能之一。

  1. API
    先來看下map:先上代碼,將string類型直接轉化為bitmap對象

     Observable.just("/image/1.png")//這是一個圖片途徑
                 .map(new Func1<String, Bitmap>() {
                     @Override
                     public Bitmap call(String path) {
    
                         return getBitmapFromPath(path);//通過路徑返回bitmap
                     }
                 })
             .subscribe(new Action1<Bitmap>() {
                 @Override
                 public void call(Bitmap bitmap) {
     //                imageView.setImageBitmap(bitmap);//得到bitmap之后的處理
                 }
             });
    

這里有一個Func1的類,他和Action1很相似,都是rxJava的一個接口,用于包裝一個帶參數的方法。Func1和Action1的區別在于,Func1有返回的參數,Action1沒有。

可以看到map()將參數string轉化為bitmap,事件參數類型也由string轉化為bitmap,這種直接變化對象參數并且返回的是很常見簡單的應用。

map():事件對象的直接轉換,是rxJava中常見的現象。示意圖如下:

map.jpg
  • flatMap():是一個很有用但是很難理解的變化。
    例子:打印出所有學生的所有課程的名稱

      Observable.from(students)
                      .flatMap(new Func1<Student, Observable<Course>>() {
                          @Override
                          public Observable<Course> call(Student student) {
                              //將student中的課程轉化為一個Observable<Course>
                              return Observable.from(student.getCourses());
                          }
                      })
              .subscribe(new Action1<Course>() {
                  @Override
                  public void call(Course course) {
                      //將course名字打印
                      Log.e("courseName",course.getCourseName());
                  }
              });
    

可以看出flatMap和map有一個共同點,就是將傳入的對象轉化為另一個對象并返回,但是不同的是,flatMap返回的是一個observable對象,并且這個對象不是直接返回到subscriber的回調對象中。
flatMap的原理是這樣的:

  1. 傳入事件對象創建一個observable對象
  2. 并不發送這個對象,而是將這個對象激活,于是他開始發送事件
  3. 每一個被創建出來的observable都會被匯入同一個observable中,而這個observable負責統一將事件交給subscriber回調。

flatMap()示意圖如下:

flatMap.jpg
線程控制scheduler(二)

在接觸了map和flatMap的時候,就有些疑問了,是否可以多次切換線程呢,答案是,可以的
先上一段代碼:

    Observable.just(1,2,3,4)
                    .subscribeOn(Schedulers.io())//事件產生在io線程
                    .observeOn(Schedulers.newThread())
                    .map(new Func1<Integer, String>() {//處理線程有上面的observeOn()決定-是一個新開的線程
                        @Override
                        public String call(Integer integer) {
                            return integer+"";
                        }
                    })
                    .observeOn(Schedulers.io())
                    .map(new Func1<String, Integer>() {//處理線程有上面的observeOn()決定-是在io線程中
                        @Override
                        public Integer call(String s) {
                            return Integer.parseInt(s);
                        }
                    })
                    .observeOn(AndroidSchedulers.mainThread())
                    .subscribe(new Action1<Integer>() {//處理線程有上面的observeOn()決定-是在主線程中
                        @Override
                        public void call(Integer integer) {

                        }
                    });

發現還有一個好用的使用場景:

    Observable.timer(4, TimeUnit.SECONDS).subscribe(new Action1<Long>() {
                @Override
                public void call(Long aLong) {
                    enterApp();
                }
            });

使用這個可以替換handeler等待10秒的功能。哈哈,好用。

好啦,rxJava基本到這里了,后面還有一些關于binding和rxBus的一些簡單介紹。
最后提供這篇文章的github地址

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 229,963評論 6 542
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 99,348評論 3 429
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 178,083評論 0 383
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,706評論 1 317
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 72,442評論 6 412
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,802評論 1 328
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,795評論 3 446
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,983評論 0 290
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 49,542評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 41,287評論 3 358
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,486評論 1 374
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 39,030評論 5 363
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,710評論 3 348
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 35,116評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,412評論 1 294
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 52,224評論 3 398
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,462評論 2 378

推薦閱讀更多精彩內容