Android訊息機制(七) Rxjava
參考
學習資料彙總
深入淺出RxJava(一:基礎篇)作者:大頭鬼
給 Android 開發者的 RxJava 詳解 作者:扔物線
BaronZhang RxJava系列
可能是東半球最全的RxJava使用場景小結
一、基本概念
RxJava最核心的兩個東西是Observables(被觀察者,事件源)和Subscribers(觀察者)。Observables發出一系列事件,Subscribers處理這些事件。這裡的事件可以是任何你感興趣的東西(觸控事件,web介面呼叫返回的資料。。。)
一個Observable可以發出零個或者多個事件,直到結束或者出錯。每發出一個事件,就會呼叫它的Subscriber的onNext方法,最後呼叫Subscriber.onNext()或者Subscriber.onError()結束。
Rxjava的看起來很想設計模式中的觀察者模式,但是有一點明顯不同,那就是如果一個Observerble沒有任何的的Subscriber,那麼這個Observable是不會發出任何事件的。
Hello World
建立一個Observable物件很簡單,直接呼叫Observable.create即可
Observable<String> myObservable = Observable.create(
new Observable.OnSubscribe<String>() {
@Override
public void call(Subscriber<? super String> sub) {
sub.onNext("Hello, world!");
sub.onCompleted();
}
}
);
這裡定義的Observable物件僅僅發出一個Hello World字串,然後就結束了。接著我們建立一個Subscriber來處理Observable物件發出的字串。
Subscriber<String> mySubscriber = new Subscriber<String>() {
@Override
public void onNext(String s) { System.out.println(s); }
@Override
public void onCompleted() { }
@Override
public void onError(Throwable e) { }
};
這裡subscriber僅僅就是列印observable發出的字串。通過subscribe函式就可以將我們定義的myObservable物件和mySubscriber物件關聯起來,這樣就完成了subscriber對observable的訂閱。myObservable.subscribe(mySubscriber);
一旦mySubscriber訂閱了myObservable,myObservable就是呼叫mySubscriber物件的onNext和onComplete方法,mySubscriber就會列印出Hello World!
1.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方法寫起來太長,也有等價的簡化寫法just和from:
just(T...): 將傳入的引數依次傳送出來。
from(T[]) / from(Iterable<? extends T>) : 將傳入的陣列或 Iterable 拆分成具體物件後,依次傳送出來。
Observable observable = Observable.just("Hello", "Hi", "Aloha");
// 將會依次呼叫:
// onNext("Hello");
// onNext("Hi");
// onNext("Aloha");
// onCompleted();
String[] words = {"Hello", "Hi", "Aloha"};
Observable observable = Observable.from(words);
// 將會依次呼叫:
// onNext("Hello");
// onNext("Hi");
// onNext("Aloha");
// onCompleted();
2.Observer (觀察者)
onNext()
onCompleted()
onError()
**3.Observer 的抽象類:Subscriber。 **
Subscriber<String> mySubscriber = new Subscriber<String>() {
@Override
public void onNext(String s) { System.out.println(s); }
@Override
public void onCompleted() { }
@Override
public void onError(Throwable e) { }
};
Subscriber 對 Observer 介面進行了一些擴充套件,但他們的基本使用方式是完全一樣的.實質上,在 RxJava 的 subscribe 過程中,Observer 也總是會先被轉換成一個 Subscriber 再使用。所以如果你只想使用基本功能,選擇 Observer 和 Subscriber 是完全一樣的。它們的區別對於使用者來說主要有兩點:
onStart(): 這是 Subscriber 增加的方法。它會在 subscribe 剛開始,而事件還未傳送之前被呼叫,可以用於做一些準備工作,例如資料的清零或重置。這是一個可選方法,預設情況下它的實現為空。需要注意的是,如果對準備工作的執行緒有要求(例如彈出一個顯示進度的對話方塊,這必須在主執行緒執行), onStart() 就不適用了,因為它總是在 subscribe 所發生的執行緒被呼叫,而不能指定執行緒。要在指定的執行緒來做準備工作,可以使用 doOnSubscribe() 方法,具體可以在後面的文中看到。
unsubscribe(): 這是 Subscriber 所實現的另一個介面 Subscription 的方法,用於取消訂閱。在這個方法被呼叫後,Subscriber 將不再接收事件。一般在這個方法呼叫前,可以使用 isUnsubscribed() 先判斷一下狀態。 unsubscribe() 這個方法很重要,因為在 subscribe() 之後, Observable 會持有 Subscriber 的引用,這個引用如果不能及時被釋放,將有記憶體洩露的風險。所以最好保持一個原則:要在不再使用的時候儘快在合適的地方(例如 onPause() onStop() 等方法中)呼叫 unsubscribe() 來解除引用關係,以避免記憶體洩露的發生。
4.Subscribe方法 (訂閱)
Observable.subscribe(observer);
Observable.subscribe(Subscriber) 的內部實現是這樣的(僅核心程式碼):
// 注意:這不是 subscribe() 的原始碼,而是將原始碼中
//與效能、相容性、擴充套件性有關的程式碼剔除後的核心程式碼。
// 如果需要看原始碼,可以去 RxJava 的 GitHub 倉庫下載。
public Subscription subscribe(Subscriber subscriber) {
subscriber.onStart();
onSubscribe.call(subscriber);
return subscriber;
}
可以看到,subscriber() 做了3件事:
呼叫 Subscriber.onStart() 。這個方法在前面已經介紹過,是一個可選的準備方法。
呼叫 Observable 中的 OnSubscribe.call(Subscriber) 。在這裡,事件傳送的邏輯開始執行。從這也可以看出,在 RxJava 中, Observable 並不是在建立的時候就立即開始傳送事件,而是在它被訂閱的時候,即當 subscribe() 方法執行的時候。
將傳入的 Subscriber 作為 Subscription 返回。這是為了方便 unsubscribe().
5.Action
注:正如前面所提到的,Observer 和 Subscriber 具有相同的角色,而且 Observer 在 subscribe() 過程中最終會被轉換成 Subscriber 物件,因此,從這裡開始,後面的描述我將用 Subscriber 來代替 Observer ,這樣更加嚴謹。
上面提到了使用just,from來簡化next。現在說一下用Action來簡化 Subscriber。
上面的例子中,我們其實並不關心OnComplete和OnError,我們只需要在onNext的時候做一些處理,這時候就可以使用Action1類。
Observable.just("Hello, world!")
.subscribe(new Action1<String>() {
@Override
public void call(String s) {
System.out.println(s);
}
});
Action系列是沒有返回值的,只有一個Call方法。call方法裡沒有引數就用action0,一個引數就用action1。注意Action中的call方法和observable的create方法中那個一訂閱就執行的call方法是有區別的。
Action0 onCompletedAction = new Action0() {
// onCompleted()
@Override
public void call() {
Log.d(tag, "completed");
}
};
6.例子
a. 列印字串陣列
將字串陣列 names中的所有字串依次列印出來:
<pre>
String[] names = ...;
Observable.from(names)
.subscribe(new Action1<String>() {
@Override
public void call(String name) {
Log.d(tag, name);
}
});
</pre>
b. 由 id 取得圖片並顯示
由指定的一個 drawable 檔案 id drawableRes取得圖片,並顯示在 ImageView中,並在出現異常的時候列印 Toast 報錯:
<pre>
int drawableRes = ...;
ImageView imageView = ...;
Observable.create(new OnSubscribe<Drawable>() {
@Override
public void call(Subscriber<? super Drawable> subscriber) {
Drawable drawable = getTheme().getDrawable(drawableRes));
subscriber.onNext(drawable);
subscriber.onCompleted();
}
}).subscribe(new Observer<Drawable>() {
@Override
public void onNext(Drawable drawable) {
imageView.setImageDrawable(drawable);
}
@Override
public void onCompleted() {
}
@Override
public void onError(Throwable e) {
Toast.makeText(activity, "Error!", Toast.LENGTH_SHORT).show();
}
});
</pre>
總結:
根據訂閱者模式,被訂閱者使用事件或回撥,讓那個訂閱者去執行某些事情。Observable在執行subscribe時觸發了自己的call方法,去載入資源,然後在載入完成時去觸發訂閱者的onNext,onCompleted.顯然,這不應該都在一個執行緒,下面看一下切換執行緒。
二、切換執行緒Scheduler
1.subscribeOn(): 指定subscribe()所發生的執行緒,即 Observable.OnSubscribe被啟用時所處的執行緒。或者叫做事件產生的執行緒。
2.observeOn(): 指定Subscriber所執行在的執行緒。或者叫做事件消費的執行緒。
上面的例子可以改為,在IO執行緒載入圖片,在UI執行緒顯示圖片:
<pre>
int drawableRes = ...;
ImageView imageView = ...;
Observable.create(new OnSubscribe<Drawable>() {
@Override
public void call(Subscriber<? super Drawable> subscriber) {
Drawable drawable = getTheme().getDrawable(drawableRes));
subscriber.onNext(drawable);
subscriber.onCompleted();
}
})
.subscribeOn(Schedulers.io()) // 指定 subscribe() 發生在 IO 執行緒
.observeOn(AndroidSchedulers.mainThread()) // 指定 Subscriber 的回撥發生在主執行緒
.subscribe(new Observer<Drawable>() {
@Override
public void onNext(Drawable drawable) {
imageView.setImageDrawable(drawable);
}
@Override
public void onCompleted() {
}
@Override
public void onError(Throwable e) {
Toast.makeText(activity, "Error!", Toast.LENGTH_SHORT).show();
}
});
</pre>
在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 主執行緒執行。
三、變換
為什麼要用變換,或者說哪些地方需要變換?
比如我想在hello world中加上我的簽名,你可能會想到去修改Observable物件:Observable.just("Hello, world! -Dan").subscribe(s -> System.out.println(s));
如果你能夠改變Observable物件,這當然是可以的,但是如果你不能修改Observable物件呢?比如Observable物件是第三方庫提供的?比如我的Observable物件被多個Subscriber訂閱,但是我只想在對某個訂閱者做修改呢?
那麼在Subscriber中對事件進行修改怎麼樣呢?比如下面的程式碼:Observable.just("Hello, world!").subscribe(s -> System.out.println(s + " -Dan"));
這種方式仍然不能讓人滿意,因為我希望我的Subscribers越輕量越好,因為我有可能會在mainThread中執行subscriber。另外,根據響應式函式程式設計的概念,Subscribers更應該做的事情是“響應”,響應Observable發出的事件,而不是去修改。如果我能在某些中間步驟中對“Hello World!”進行變換是不是很酷?
<pre>
Observable.just("images/logo.png") // 輸入型別 String
.map(new Func1<String, Bitmap>() {
@Override
public Bitmap call(String filePath) { // 引數型別 String
return getBitmapFromPath(filePath); // 返回型別 Bitmap
}
})
.subscribe(new Action1<Bitmap>() {
@Override
public void call(Bitmap bitmap) { // 引數型別 Bitmap
showBitmap(bitmap);
}
});
</pre>
Func系列和Action系列類似,不過他是要返回引數的。傳入的String型別,返回了bitmap型別,然後提供給subscriber.
再來看另一個例子:
列印出每個學生所需要修的所有課程的名稱
<pre>
Student[] students = ...;
Subscriber<Student> subscriber = new Subscriber<Student>() {
@Override
public void onNext(Student student) {
List<Course> courses = student.getCourses();
for (int i = 0; i < courses.size(); i++) {
Course course = courses.get(i);
Log.d(tag, course.getName());
}
}
...
};
Observable.from(students)
.subscribe(subscriber);
</pre>
如果不想在 Subscriber中使用 for 迴圈,而是希望 Subscriber中直接傳入單個的 Course物件呢(這對於程式碼複用很重要)
<pre>
Student[] students = ...;
Subscriber<Course> subscriber = new Subscriber<Course>() {
@Override
public void onNext(Course course) {
Log.d(tag, course.getName());
}
...
};
Observable.from(students)
.flatMap(new Func1<Student, Observable<Course>>() {
@Override
public Observable<Course> call(Student student) {
return Observable.from(student.getCourses());
}
})
.subscribe(subscriber);
</pre>
flatMap很奇怪,它確實返回了一個Observable,這一點和map不同。map返回的是一個subscriber能用的型別物件,但flatMap返回的Observable居然subscriber也是能用的,連型別都沒有問題,就像用Observable.from直接拆出來的一樣。下面這段話比較費解:
flatMap() 的原理是這樣的:1. 使用傳入的事件物件建立一個 Observable 物件;2. 並不傳送這個 Observable, 而是將它啟用,於是它開始傳送事件;3. 每一個建立出來的 Observable 傳送的事件,都被匯入同一個 Observable ,而這個 Observable 負責將這些事件統一交給 Subscriber 的回撥方法。這三個步驟,把事件拆成了兩級,通過一組新建立的 Observable 將初始的物件『鋪平』之後通過統一路徑分發了下去。而這個『鋪平』就是 flatMap() 所謂的 flat。
四、記憶體洩露
參考
在Android開發中使用RxJava
RxJava 和 RxAndroid 三(生命週期控制和記憶體優化)
我們之前提到,使用AsyncTask的一大缺點就是它可能會造成記憶體洩露。當它們持有的Activity/Fragment的引用沒有正確處理時就會這樣。不幸的是,RxJava並不會自動防止這種情況發生,好在它可以很容易地防止記憶體洩露。Observable.subscribe()方法會返回一個Subscription物件,這個物件僅僅有兩個方法:isSbscribed()與unsubscribe()。你可以在Activity/Fragment的onDestroy方法中呼叫Subscription.isSubscribed()檢測是否這個非同步任務仍在進行。如果它仍在進行,則呼叫unsubscribe()方法來結束任務,從而釋放其中的強引用,防止記憶體洩露。如果你使用了多個Observable與Subscriber,那麼你可以將它們新增到CompositeSubscription中,並呼叫CompositeSubscription.unsubscribe()結束所有的任務。
1、取消訂閱 subscription.unsubscribe() ;
2、執行緒排程
3、rxlifecycle 框架的使用
相關文章
- Android訊息機制Message訊息池Android
- android訊息機制—HandlerAndroid
- 理解 Android 訊息機制Android
- Android訊息機制HandlerAndroid
- Android 之訊息機制Android
- Android的訊息機制Android
- android之 Android訊息機制Android
- Android訊息傳遞之Handler訊息機制Android
- Android非同步訊息機制Android非同步
- Android訊息機制Handler用法Android
- Android 訊息機制詳解Android
- OC訊息機制,訊息轉發機制
- [Android進階]Android訊息機制Android
- Android Handler 訊息機制詳述Android
- Android的Handler訊息機制 解析Android
- 深入理解Android訊息機制Android
- Android訊息機制原始碼分析Android原始碼
- 訊息機制
- Android 訊息機制詳解(Android P)Android
- 全面剖析Android訊息機制原始碼Android原始碼
- 深入理解 Android 訊息機制原理Android
- 深入探索Android訊息機制之HandlerAndroid
- 淺析Android中的訊息機制Android
- Android程式間通訊–訊息機制及IPC機制實現薦Android
- iOS訊息機制iOS
- SAP訊息機制
- Android 訊息機制:Handler、MessageQueue 和 LooperAndroidOOP
- 由外到內——剖析Android訊息機制Android
- Android-Handler訊息機制實現原理Android
- 原始碼分析:Android訊息處理機制原始碼Android
- Android應用程式訊息處理機制Android
- 06.Android之訊息機制問題Android
- Android進階;Handler訊息機制詳解Android
- Android Handler MessageQueue Looper 訊息機制原理AndroidOOP
- Android Handler訊息機制原始碼解讀Android原始碼
- Android 訊息處理機制:Handler|MessageAndroid
- Android Handler訊息傳遞機制詳解Android
- Android訊息機制不完全解析(上) .Android