一、什么是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),
當button按鈕被點擊的時候,觀察者對這個點擊事件做出自己的反應
轉變為通用的觀察者模式如下:
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基本實現
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()做了三個工作:
- 調用onStart(),在開始之前的一些準備工作。
- onSubscrible.call(subscriber),事件的邏輯開始運行,可以看出,observable不是在創建的時候開始發送消息,而是在subscrib訂閱的時候開始。
- 將observer對象返回,是為了方便unSubscrib()。
除了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);
舉例子
-
打印字符串數組
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(調度器)。
- 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的核心功能之一。
-
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中常見的現象。示意圖如下:
-
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的原理是這樣的:
- 傳入事件對象創建一個observable對象
- 并不發送這個對象,而是將這個對象激活,于是他開始發送事件
- 每一個被創建出來的observable都會被匯入同一個observable中,而這個observable負責統一將事件交給subscriber回調。
flatMap()示意圖如下:
線程控制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地址