Android——2016新技術Rxjava

Android——2016新技術Rxjava

剛開始接觸Rxjava的時候,覺得這門技巧讓我很懵逼,因為之前看的都是些概念性的解析和深解,到自己動手去操作的時候才發現,其實沒有我想像中的那麼難懂,所以,你要想真正去理解,一些單方面的書籍遠遠是不夠的,真正走到實踐中去,才能真正體會其中的奧妙。自己寫了一個簡單的demo,到後面會分享給大家,接下來我們來看下Rxjava概念性的知識。

Rxjava到底是什麼?

一個詞:異步。

RxJava 在GitHub 主頁上的自我介紹是“a library for composing asynchronous and event-based programs using observable sequences for the Java VM”(一個在Java VM 上使用可觀測的序列來組成異步的、基於事件的程序的庫)。這就是RxJava ,概括得非常精準。

然而,對於初學者來說,這太難看懂了。因為它是一個『總結』,而初學者更需要一個『引言』。

其實, RxJava 的本質可以壓縮為異步這一個詞。說到根上,它就是一個實現異步操作的庫,而別的定語都是基於這之上的。

Rxjava的好處在哪裡?

換句話說,『同樣是做異步,為什麼人們用它,而不用現成的AsyncTask / Handler / XXX / … ?』

一個詞:簡潔。

異步操作很關鍵的一點是程序的簡潔性,因為在調度過程比較複雜的情況下,異步代碼經常會既難寫也難被讀懂。Android創造的AsyncTask和Handler ,其實都是為了讓異步代碼更加簡潔。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()) //Android主线程
    .subscribe(new Action1<Bitmap>() {
        @Override
        public void call(Bitmap bitmap) {
            imageCollectorView.addImage(bitmap);
        }
    });

那位說話了:『你這代碼明明變多了啊!簡潔個毛啊!』大兄弟你消消氣,我說的是邏輯的簡潔,不是單純的代碼量少(邏輯簡潔才是提升讀寫代碼速度的必殺技對不?)。觀察一下你會發現, RxJava 的這個實現,是一條從上到下的鍊式調用,沒有任何嵌套,這在邏輯的簡潔性上是具有優勢的。當需求變得複雜時,這種優勢將更加明顯(試想如果還要求只選取前10 張圖片,常規方式要怎麼辦?如果有更多這樣那樣的要求呢?再試想,在這一大堆需求實現完兩個月之後需要改功能,當你翻回這裡看到自己當初寫下的那一片迷之縮進,你能保證自己將迅速看懂,而不是對著代碼重新捋一遍思路?)。

到這里相信你對Rxjava有一點初步的了解了,下面看下原理性的東西:

API 介紹和原理簡析

這個我就做不到一個詞說明了……因為這一節的主要內容就是一步步地說明RxJava 到底怎樣做到了異步,怎樣做到了簡潔。

  1. 概念:擴展的觀察者模式

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

觀察者模式

先簡述一下觀察者模式,已經熟悉的可以跳過這一段。

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

OnClickListener的大致圖:
這裡寫圖片描述

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

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觀察模式大致圖:
這裡寫圖片描述

基本實現

以上的概念,總結出實現無非就只有三點:
1、創建Observer
2、創建Observable
3、訂閱subscribe

首先看下:1、創建Observer: Observer即為觀察者,它決定事件觸發的時候有怎樣的行為。在Rxjava中的實現方式如下:

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!");
    }
};

除了Observer 接口之外,RxJava 還內置了一個實現了Observer 的抽像類:Subscriber。Subscriber 對Observer 接口進行了一些擴展,但他們的基本使用方式是完全一樣的:

Subscriber<String> subscriber = new Subscriber<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!");
    }
};

這裡總結了一點:Observer在subscribe()過程中會轉換成Subscriber對像後再使用的。他們的基本操作都是完全一樣的,既然如此,我們就直接用Subscriber代替Observer,這樣更加嚴謹。

另外Subscriber和Observer的區別有兩點:

  1. onStart(): 這是Subscriber 增加的方法。它會在subscribe剛開始,而事件還未發送之前被調用,可以用於做一些準備工作,例如數據的清零或重置。這是一個可選方法,默認情況下它的實現為空。需要注意的是,如果對準備工作的線程有要求(例如彈出一個顯示進度的對話框,這必須在主線程執行),onStart() 就不適用了,因為它總是在subscribe所發生的線程被調用,而不能指定線程。要在指定的線程來做準備工作,可以使用doOnSubscribe()方法,具體可以在後面的文中看到。
  2. unsubscribe(): 這是Subscriber 所實現的另一個接口Subscription的方法,用於取消訂閱。在這個方法被調用後,Subscriber 將不再接收事件。一般在這個方法調用前,可以使用isUnsubscribed() 先判斷一下狀態。unsubscribe() 這個方法很重要,因為在subscribe() 之後,Observable 會持有Subscriber的引用,這個引用如果不能及時被釋放,將有內存洩露的風險。所以最好保持一個原則:要在不再使用的時候盡快在合適的地方(例如onPause() onStop() 等方法中)調用unsubscribe() 來解除引用關係,以避免內存洩露的發生。

2、創建Observable
Observable即為被觀察者,它決定什麼時候觸發事件,及觸發怎樣的事件。它的創建是使用create,實現該接口如下:

Observable observable = Observable.create(new Observable.OnSubscribe<String>() {
    @Override
    public void call(Subscriber<? super String> subscriber) {
        subscriber.onNext("Hello");
        subscriber.onNext("Hi");
        subscriber.onNext("Aloha");
        subscriber.onCompleted();
    }
});

可以看到,這里傳入了一個OnSubscribe 對像作為參數。OnSubscribe 會被存儲在返回的Observable 對像中,它的作用相當於一個計劃表,當Observable 被訂閱的時候,OnSubscribe 的call() 方法會自動被調用,事件序列就會依照設定依次觸發(對於上面的代碼,就是觀察者Subscriber 將會被調用三次onNext() 和一次onCompleted())。這樣,由被觀察者調用了觀察者的回調方法,就實現了由被觀察者向觀察者的事件傳遞,即觀察者模式。

create()是Rxjava最基本的創建事件序列的操作,基於這個方法,Rxjava還有更簡潔更快捷的事件列隊的方法。
比如:

  • just(T…): 將傳入的參數依次發送出來。
Observable observable = Observable.just("Hello", "Hi", "Aloha");
  • from(T[]) / from(Iterable
String[] words = {"Hello", "Hi", "Aloha"};
Observable observable = Observable.from(words);

上面just(T…) 的例子和from(T[]) 的例子,都和之前的create(OnSubscribe) 的例子是等價的。

3、訂閱subscribe
創建了Observer和Observable之後,把它們關聯起來就可以工作了,就是訂閱。操作也比較簡單:

observable.subscribe(observer);
// 或者:
observable.subscribe(subscriber);

注意:有人可能會注意到, subscribe()這個方法有點怪:它看起來是『observalbe訂閱了observer / subscriber』而不是『observer / subscriber訂閱了observalbe』,這看起來就像『雜誌訂閱了讀者』一樣顛倒了對象關係。這讓人讀起來有點彆扭,不過如果把API設計成observer.subscribe(observable) / subscriber.subscribe(observable) ,雖然更加符合思維邏輯,但對流式API的設計就造成影響了,比較起來明顯是得不償失的。

除了observable.subscribe(observer)和observable.subscribe(subscriber)支持不完整的回調,還有subscribe()也支持。Rxjava會根據條件創建Subscriber對象。
例如:

Action1<String> onNextAction = new Action1<String>() {
    // onNext()
    @Override
    public void call(String s) {
        Log.d(tag, s);
    }
};
Action1<Throwable> onErrorAction = new Action1<Throwable>() {
    // onError()
    @Override
    public void call(Throwable throwable) {
        // 一些错误处理写这里
    }
};
Action0 onCompletedAction = new Action0() {
    // onCompleted()
    @Override
    public void call() {
        Log.d(tag, "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);

簡單解釋一下這段代碼中出現的Action1 和Action0。Action0 是RxJava 的一個接口,它只有一個方法call(),這個方法是無參無返回值的;由於onCompleted() 方法也是無參無返回值的,因此Action0 可以被當成一個包裝對象,將onCompleted( ) 的內容打包起來將自己作為一個參數傳入subscribe() 以實現不完整定義的回調。這樣其實也可以看做將onCompleted() 方法作為參數傳進了subscribe(),相當於其他某些語言中的『閉包』。Action1 也是一個接口,它同樣只有一個方法call(T param),這個方法也無返回值,但有一個參數;與Action0 同理,由於onNext(T obj) 和onError(Throwable error) 也是單參數無返回值的,因此Action1 可以將onNext(obj) 和onError(error) 打包起來傳入subscribe() 以實現不完整定義的回調。事實上,雖然Action0 和Action1 在API 中使用最廣泛,但RxJava 是提供了多個ActionX 形式的接口(例如Action2, Action3) 的,它們可以被用以包裝不同的無返回值的方法。

到了這里大家更進了一步了解Rxjava,下面再看下demo,寫的很簡單,這裡我只說一下需要注意的地方,就不詳細去講了,後面我會把源碼下載地址貼出來給大家。
第一點:
我們要想使用Rxjava肯定要添加Rxjava相關的jar包,這點無疑的。
打開項目根下的build.gradle添加一下jar:

compile 'com.jakewharton:butterknife:8.2.1'
    apt 'com.jakewharton:butterknife-compiler:8.2.1'
    compile 'io.reactivex:rxjava:1.1.8'
    compile 'io.reactivex:rxandroid:1.2.1'
    compile 'com.trello:rxlifecycle:0.6.1'
    compile 'com.trello:rxlifecycle-components:0.6.1'
    compile 'com.jakewharton.rxbinding:rxbinding:0.4.0'

這裡的apt 'com.jakewharton:butterknife-compiler:8.2.1',我是使用了註解的方式,不想用的話可以不用添加這兩句,如果添加了使用還需注意一下兩點:
1、要在項目的根下build.gradle中最頂部添加

apply plugin: 'com.neenbedankt.android-apt'

2、既然引用了neebedankt就必須要聲明它,在最外層的build.gradle中的dependencies中添加

classpath 'com.neenbedankt.gradle.plugins:android-apt:1.8'

這樣才能使用ButterKnife的快捷註解方式。

最後附上源碼,祝愿大夥玩的愉快!!!

源碼下載地址

原文出處

Comments are closed.