RxAndroid 基礎(chǔ)#
參考hi大頭鬼hi 的微博沐飘,寫代碼進(jìn)行測試學(xué)習(xí),以下記錄共享,同時以便之后查閱影晓。 由于不熟悉lambda疤祭,同時開始學(xué)習(xí)也不建議直接使用lambda,以下大部分代碼均使用常規(guī)方法編寫蜈项。
RxJava在github上的地址
RxAndroid在github上的地址
首先侥衬,工程中引入:
dependencies {
compile fileTree(dir: 'libs', include: ['*.jar'])
compile 'com.android.support:appcompat-v7:23.0.1'
//引入RxAndroid----begin
compile 'io.reactivex:rxandroid:1.1.0'
compile 'io.reactivex:rxjava:1.1.0'
//引入RxAndroid----end
// RxBinding
compile 'com.jakewharton.rxbinding:rxbinding:0.4.0'
compile 'com.jakewharton.rxbinding:rxbinding-appcompat-v7:0.4.0'
compile 'com.jakewharton.rxbinding:rxbinding-design:0.4.0'
}
一怀樟、基本使用
常規(guī)語法
Observable<String> myObservable = Observable.create(
new Observable.OnSubscribe<String>() {
@Override
public void call(Subscriber<? super String> sub) {
sub.onNext("Observable Hello, world!");
sub.onCompleted();
}
}
);
Subscriber<String> mySubscriber = new Subscriber<String>() {
@Override
public void onNext(String s) {
L.d("onNext s: " + s);
}
@Override
public void onCompleted() { }
@Override
public void onError(Throwable e) { }
};
myObservable.subscribe(mySubscriber);
簡化語法
Observable.just(1)
.subscribe(new Action1<Integer>() {
@Override
public void call(Integer integer) {
L.d("onNext integer: " + integer);
}
});
map操作符變換
Observable.just("Hello, world!")
.map(new Func1<String, String>() {
@Override
public String call(String s) {
return s + " -Map";
}
})
.subscribe(new Action1<String>() {
@Override
public void call(String s) {
L.d("onNext s: " + s);
}
});
二虑灰、操作符
Observable.from()
Observable.from()方法,它接收一個集合作為輸入对湃,然后每次輸出一個元素給subscriber:
List<String> urls = new ArrayList<>();
urls.add("url1");
urls.add("url2");
urls.add("url3");
Observable.from(urls)
.subscribe(new Action1<String>() {
@Override
public void call(String s) {
L.d("onNext s: " + s);
}
});
輸出如下:
nNext s: url1
onNext s: url2
onNext s: url3
Observable<List<String>> myObservableUrls = Observable.create(
new Observable.OnSubscribe<List<String>>() {
@Override
public void call(Subscriber<? super List<String>> subscriber) {
List<String> urls = new ArrayList<>();
urls.add("url1");
urls.add("url2");
urls.add("url3");
subscriber.onNext(urls);
}
}
);
myObservableUrls.subscribe(new Action1<List<String>>() {
@Override
public void call(List<String> strings) {
Observable.from(strings)
.subscribe(new Action1<String>() {
@Override
public void call(String s) {
L.d("myObservableUrls onNext s: " + s);
}
});
}
});
輸出如下:
myObservableUrls onNext s: url1
myObservableUrls onNext s: url2
myObservableUrls onNext s: url3
在前一個Observable輸出是list時斤儿,嵌套太多,看起來比較亂陕贮。
Observable.flatMap()
Observable.flatMap()接收一個Observable的輸出作為輸入,同時輸出另外一個Observable戈擒。
myObservableUrls.flatMap(new Func1<List<String>, Observable<String>>() {
@Override
public Observable<String> call(List<String> urls) {
return Observable.from(urls);
}
}).subscribe(new Action1<String>() {
@Override
public void call(String s) {
L.d("myObservableUrls.flatMap onNext s: " + s);
}
});
避免了上面直接使用Observable.from時需要內(nèi)如再次subscribe導(dǎo)致的內(nèi)部嵌套。理解flatMap的關(guān)鍵點在于,flatMap輸出的新的Observable正是我們在Subscriber想要接收的“绯瑁現(xiàn)在Subscriber不再收到List<String>,而是收到一些列單個的字符串轿偎,就像Observable.from()的輸出一樣。
flatMap(),它可以返回任何它想返回的Observable對象仓蛆。
接著前面的例子豆拨,現(xiàn)在我不想打印URL了,而是要打印收到的每個網(wǎng)站的標(biāo)題弥搞。
現(xiàn)在的方法每次只能傳入一個URL,并且返回值不是一個String,而是一個輸出String的Observabl對象:
private Observable<String> getTitle(final String url) {
return Observable.create(new Observable.OnSubscribe<String>() {
@Override
public void call(Subscriber<? super String> subscriber) {
subscriber.onNext("title: " + url);
}
});
}
實現(xiàn):
myObservableUrls.flatMap(new Func1<List<String>, Observable<String>>() {
@Override
public Observable<String> call(List<String> urls) {
return Observable.from(urls);
}
}).flatMap(new Func1<String, Observable<?>>() {
@Override
public Observable<?> call(String s) {
return getTitle(s);
}
}).subscribe(new Action1<Object>() {
@Override
public void call(Object o) {
L.d("myObservableUrls.flatMap onNext o: " + o);
}
});
filter()
filter()輸出和輸入相同的元素伪煤,并且會過濾掉那些不滿足檢查條件的
myObservableUrls.flatMap(new Func1<List<String>, Observable<String>>() {
@Override
public Observable<String> call(List<String> urls) {
return Observable.from(urls);
}
}).flatMap(new Func1<String, Observable<?>>() {
@Override
public Observable<?> call(String s) {
return getTitle(s);
}
}).filter(new Func1<Object, Boolean>() {
@Override
public Boolean call(Object o) {
return !o.equals("title: url2");
}
}).subscribe(new Action1<Object>() {
@Override
public void call(Object o) {
L.d("myObservableUrls.flatMap onNext o: " + o);
}
});
take()
take()輸出最多指定數(shù)量的結(jié)果扁誓。
doOnNext()
doOnNext()允許我們在每次輸出一個元素之前做一些額外的事情蝗敢,比如這里的保存標(biāo)題锁右。
直接上網(wǎng)上的示例:
query("Hello, world!")
.flatMap(urls -> Observable.from(urls))
.flatMap(url -> getTitle(url))
.filter(title -> title != null)
.take(5)
.doOnNext(title -> saveTitle(title))
.subscribe(title -> System.out.println(title));
三码泞、響應(yīng)式的好處
錯誤處理
每一個Observerable對象在終結(jié)的時候都會調(diào)用onCompleted()或者onError()方法领铐,
這種模式有以下幾個優(yōu)點:
1.只要有異常發(fā)生onError()一定會被調(diào)用
這極大的簡化了錯誤處理。只需要在一個地方處理錯誤即可以莲兢。
2.操作符不需要處理異常
將異常處理交給訂閱者來做坟岔,Observerable的操作符調(diào)用鏈中一旦有一個拋出了異常,就會直接執(zhí)行onError()方法燕鸽。
3.你能夠知道什么時候訂閱者已經(jīng)接收了全部的數(shù)據(jù)鸥拧。
知道什么時候任務(wù)結(jié)束能夠幫助簡化代碼的流程。(雖然有可能Observable對象永遠(yuǎn)不會結(jié)束)
使用RXJAVA腕柜,OBSERVABLE對象根本不需要知道如何處理錯誤!操作符也不需要處理錯誤狀態(tài)-一旦發(fā)生錯誤娩脾,就會跳過當(dāng)前和后續(xù)的操作符俩功。所有的錯誤處理都交給訂閱者來做。
調(diào)度器
使用RxJava碰声,你可以使用subscribeOn()指定觀察者代碼運行的線程诡蜓,使用observerOn()指定訂閱者運行的線程:
myObservableServices.retrieveImage(url)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(bitmap -> myImageView.setImageBitmap(bitmap));
訂閱(Subscriptions)
調(diào)用Observable.subscribe(),會返回一個Subscription對象胰挑。這個對象代表了被觀察者和訂閱者之間的聯(lián)系蔓罚。
ubscription subscription = Observable.just("Hello, World!")
.subscribe(s -> System.out.println(s));
你可以在后面使用這個Subscription對象來操作被觀察者和訂閱者之間的聯(lián)系.
subscription.unsubscribe();
System.out.println("Unsubscribed=" + subscription.isUnsubscribed());
RxJava的另外一個好處就是它處理unsubscribing的時候贡这,會停止整個調(diào)用鏈。如果你使用了一串很復(fù)雜的操作符喜爷,調(diào)用unsubscribe將會在他當(dāng)前執(zhí)行的地方終止泛源。不需要做任何額外的工作!
當(dāng)然也不是所有的代碼都使用響應(yīng)式的方式溪王,僅僅當(dāng)代碼復(fù)雜到我想將它分解成簡單的邏輯的時候,我才使用響應(yīng)式代碼掐禁。
四教届、在Android中使用響應(yīng)式編程
這部分主要是摘錄倦卖,有些方法現(xiàn)在已過時昧狮,后續(xù)在實際應(yīng)用中有用到再進(jìn)行補充更新,
RxAndroid
首先,AndroidSchedulers提供了針對Android的線程系統(tǒng)的調(diào)度器糠悯。需要在UI線程中運行某些代碼?很簡單烤芦,只需要使用AndroidSchedulers.mainThread():
retrofitService.getImage(url)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(bitmap -> myImageView.setImageBitmap(bitmap));
throttleFirst(): 在每次事件觸發(fā)后的一定時間間隔內(nèi)丟棄新的事件陪蜻。常用作去抖動過濾,例如按鈕的點擊監(jiān)聽器:
RxView.clickEvents(button) // RxBinding 代碼花颗,后面的文章有解釋
.throttleFirst(500, TimeUnit.MILLISECONDS) // 設(shè)置防抖間隔為 500ms
.subscribe(subscriber);
遺留代碼,運行極慢的代碼
絕大多數(shù)時候Observable.just() 和 Observable.from() 能夠幫助你從遺留代碼中創(chuàng)建 Observable 對象:
private Object oldMethod() { ... }
public Observable<Object> newMethod() {
return Observable.just(oldMethod());
}
上面的例子中如果oldMethod()足夠快是沒有什么問題的剩辟,但是如果很慢呢卦羡?調(diào)用oldMethod()將會阻塞住他所在的線程。
為了解決這個問題呻纹,可以參考我一直使用的方法–使用defer()來包裝緩慢的代碼:
private Object slowBlockingMethod() { ... }
public Observable<Object> newMethod() {
return Observable.defer(() -> Observable.just(slowBlockingMethod()));
}
現(xiàn)在,newMethod()的調(diào)用不會阻塞了专缠,除非你訂閱返回的observable對象雷酪。
生命周期
如何處理Activity的生命周期?主要就是兩個問題:
1.在configuration改變(比如轉(zhuǎn)屏)之后繼續(xù)之前的Subscription涝婉。
比如你使用Retrofit發(fā)出了一個REST請求哥力,接著想在listview中展示結(jié)果。如果在網(wǎng)絡(luò)請求的時候用戶旋轉(zhuǎn)了屏幕怎么辦墩弯?你當(dāng)然想繼續(xù)剛才的請求吩跋,但是怎么搞?
2.Observable持有Context導(dǎo)致的內(nèi)存泄露
這個問題是因為創(chuàng)建subscription的時候渔工,以某種方式持有了context的引用锌钮,尤其是當(dāng)你和view交互的時候,這太容易發(fā)生引矩!如果Observable沒有及時結(jié)束梁丘,內(nèi)存占用就會越來越大侵浸。
第一個問題的解決方案就是使用RxJava內(nèi)置的緩存機制,這樣你就可以對同一個Observable對象執(zhí)行unsubscribe/resubscribe氛谜,卻不用重復(fù)運行得到Observable的代碼掏觉。cache() (或者 replay())會繼續(xù)執(zhí)行網(wǎng)絡(luò)請求(甚至你調(diào)用了unsubscribe也不會停止)。這就是說你可以在Activity重新創(chuàng)建的時候從cache()的返回值中創(chuàng)建一個新的Observable對象值漫。
Observable<Photo> request = service.getUserPhoto(id).cache();
Subscription sub = request.subscribe(photo -> handleUserPhoto(photo));
// ...When the Activity is being recreated...
sub.unsubscribe();
// ...Once the Activity is recreated...
request.subscribe(photo -> handleUserPhoto(photo));
注意澳腹,兩次sub是使用的同一個緩存的請求。當(dāng)然在哪里去存儲請求的結(jié)果還是要你自己來做杨何,和所有其他的生命周期相關(guān)的解決方案一延虎酱塔,必須在生命周期外的某個地方存儲。(retained fragment或者單例等等)危虱。
第二個問題的解決方案就是在生命周期的某個時刻取消訂閱延旧。一個很常見的模式就是使用CompositeSubscription來持有所有的Subscriptions,然后在onDestroy()或者onDestroyView()里取消所有的訂閱槽地。
private CompositeSubscription mCompositeSubscription
= new CompositeSubscription();
private void doSomething() {
mCompositeSubscription.add(
AndroidObservable.bindActivity(this, Observable.just("Hello, World!"))
.subscribe(s -> System.out.println(s)));
}
@Override
protected void onDestroy() {
super.onDestroy();
mCompositeSubscription.unsubscribe();
}
你可以在Activity/Fragment的基類里創(chuàng)建一個CompositeSubscription對象迁沫,在子類中使用它。
注意! 一旦你調(diào)用了 CompositeSubscription.unsubscribe()捌蚊,這個CompositeSubscription對象就不可用了, 如果你還想使用CompositeSubscription集畅,就必須在創(chuàng)建一個新的對象了。
最后缅糟,附上測試時使用的完整工程挺智,代碼上面均有貼出來了。
RxAndroid基本使用測試代碼
http://download.csdn.net/detail/yaodong379/9486905