簡(jiǎn)介
RxLife是一款輕量級(jí)別的RxJava生命周期管理庫(kù),代碼侵入性極低,隨用隨取,不需要做任何準(zhǔn)備工作,支持在Activity/Fragment 的任意生命周期方法斷開管道垒迂。
原理
RxLife通過Jetpack 下的 Lifecycle 獲取 Activity/Fragment 的生命周期變化,并通過Observable.lift(ObservableOperator)
操作符妒蛇,注入自己實(shí)現(xiàn)的Observer對(duì)象(該對(duì)象能感知 Activity/Fragment的生命周期變化)机断,從而在onSubscribe(Disposable d)
方法中拿到Disposable對(duì)象,隨后在相應(yīng)的生命周期回調(diào)里執(zhí)行Disposable.dispose()
方法斷開管道绣夺,這樣就能將lift
操作符上面的所有Disposable對(duì)象全部斷開吏奸。
為什么要重復(fù)造輪子
熟悉RxJava的同學(xué)應(yīng)該都知道trello/RxLifecycle 項(xiàng)目,它在目前的3.0.0版本中通過Lifecycle
感知Activity/Fragment 的生命周期變化陶耍,并通過BehaviorSubject
類及compose
奋蔚、takeUntil
操作符來實(shí)現(xiàn)管道的中斷,這種實(shí)現(xiàn)原理有一點(diǎn)不足的是,它在管道斷開后泊碑,始終會(huì)往下游發(fā)送一個(gè)onComplete
事件坤按,這對(duì)于在onComplete
事件中有業(yè)務(wù)邏輯的同學(xué)來說,無(wú)疑是致命的馒过。那為什么會(huì)這樣呢臭脓?因?yàn)?code>takeUntil操作符內(nèi)部實(shí)現(xiàn)機(jī)制就是這樣的,有興趣的同學(xué)可以去閱讀takeUntil
操作符的源碼沉桌,這里不展開。而RxLife就不會(huì)有這樣問題算吩,因?yàn)樵谠砩?code>RxLife就與trello/RxLifecycle
不同留凭,并且RxLife還在lift
操作都的基礎(chǔ)上提供了一些額外的api,能有效的避免因RxJava內(nèi)部類持有Activity/Fragment的引用偎巢,而造成的內(nèi)存泄漏問題蔼夜,下面開始講解。
gradle依賴
implementation 'com.rxjava.rxlife:rxlife:1.0.4'
用法
Observable.timer(10, TimeUnit.SECONDS)
//默認(rèn)在onDestroy時(shí)中斷管道
.lift(RxLife.lift(this))
.subscribe(aLong -> {
Log.e("LJX", "accept =" + aLong);
});
//或者
Observable.timer(10, TimeUnit.SECONDS)
//指定在onStop時(shí)中斷管道
.lift(RxLife.lift(this,Event.ON_STOP))
.subscribe(aLong -> {
Log.e("LJX", "accept =" + aLong);
});
在Activity/Fragment 中压昼,使用Observable的lift()
操作符求冷,方法中傳入RxLife.lift(this)
,如果需要指定生命周期方法窍霞,額外再傳一個(gè)Event對(duì)象即可匠题。怎么樣?但金?是不是極其簡(jiǎn)單韭山,根本不需要做任何準(zhǔn)備工作,代碼侵入性極低冷溃。
處理內(nèi)存泄漏
我們來看一個(gè)案例
public void leakcanary(View view) {
Observable.timer(100, TimeUnit.MILLISECONDS)
.map(new MyFunction<>()) //阻塞操作
.lift(RxLife.lift(this))
.subscribe(new Consumer<Long>() { //這里使用匿名內(nèi)部類钱磅,持有Activity的引用
//注意這里不能使用Lambda表達(dá)式,否則leakcanary檢測(cè)不到內(nèi)存泄漏
@Override
public void accept(Long aLong) throws Exception {
Log.e("LJX", "accept =" + aLong);
}
});
}
//這里使用靜態(tài)內(nèi)部類似枕,不會(huì)持有外部類的引用
static class MyFunction<T> implements Function<T, T> {
@Override
public T apply(T t) throws Exception {
//當(dāng)dispose時(shí)盖淡,第一次睡眠會(huì)被吵醒,接著便會(huì)進(jìn)入第二次睡眠
try {
Thread.sleep(3000);
} catch (Exception e) {
}
try {
Thread.sleep(30000);
} catch (Exception e) {
}
return t;
}
}
上面的代碼會(huì)造成Activity無(wú)法回收,導(dǎo)致內(nèi)存泄漏凿歼,我們用Leakcannry工具來檢測(cè)一下褪迟,發(fā)現(xiàn)確實(shí)造成來內(nèi)存泄漏,如下
我們已經(jīng)使用RxLife
庫(kù)答憔,會(huì)自動(dòng)中斷管道牵咙,那為什么還會(huì)造成內(nèi)存泄漏呢?其實(shí)原因很簡(jiǎn)單攀唯,我們只是中斷了管道洁桌,而沒有中斷上游對(duì)下游引用『钹郑看上面的截圖就能知道另凌,上游始終持有下游的引用谱轨,而最下游的匿名內(nèi)部類Consumer
又持有了Activity的引用,所以就導(dǎo)致了Activity無(wú)法回收吠谢。
那為什么中斷管道時(shí)土童,不會(huì)中斷上下游的引用呢?
首先有一點(diǎn)我們需要明確工坊,調(diào)用Disposable.dispose()
方法來斷開管道献汗,并不是真正意義上的將上游與下游斷開,它只是改變了管道上各個(gè)Observer對(duì)象的一個(gè)標(biāo)志位的值王污,我們來看一下LambdaObserver
類的源碼就會(huì)知道
@Override
public void dispose() {
DisposableHelper.dispose(this);
}
呃呃罢吃,只有一行代碼,我們繼續(xù)
public static boolean dispose(AtomicReference<Disposable> field) {
Disposable current = field.get(); //此處得到上游的Disposable對(duì)象
Disposable d = DISPOSED;
if (current != d) {
current = field.getAndSet(d); //更改自己的標(biāo)志位為DISPOSED
if (current != d) {
if (current != null) {
current.dispose();//關(guān)閉上游的Disposable對(duì)象
}
return true;
}
}
return false;
}
可以看到昭齐,這里只做了兩件事尿招,一是更改自己的標(biāo)志位,二是調(diào)用上游的dispose()
方法阱驾,其實(shí)你只要多看看就谜,你就發(fā)現(xiàn),RxJava內(nèi)部大多數(shù)Observer在dispose()
方法都會(huì)干這兩件事里覆。
到這丧荐,我們?cè)撊绾谓鉀Q這個(gè)內(nèi)存泄漏問題呢?其實(shí)喧枷,RxJava早就想到了這一點(diǎn)篮奄,它給我們提供了一個(gè)onTerminateDetach()
操作符,這個(gè)操作符會(huì)在onError(Throwable t)
割去、onComplete()
窟却、dispose()
這個(gè)3個(gè)時(shí)刻,斷開上游對(duì)下游的引用呻逆,我們來看看源碼夸赫,源碼在ObservableDetach
類中
@Override
public void dispose() {
Disposable d = this.upstream;
this.upstream = EmptyComponent.INSTANCE;//上游重新賦值
this.downstream = EmptyComponent.asObserver();//下游重新賦值
d.dispose();//調(diào)用上游的dispose()方法
}
@Override
public void onError(Throwable t) {
Observer<? super T> a = downstream;
this.upstream = EmptyComponent.INSTANCE;//上游重新賦值
this.downstream = EmptyComponent.asObserver();//下游重新賦值
a.onError(t); //調(diào)用下游的onError方法
}
@Override
public void onComplete() {
Observer<? super T> a = downstream;
this.upstream = EmptyComponent.INSTANCE;//上游重新賦值
this.downstream = EmptyComponent.asObserver();//下游重新賦值
a.onComplete();//調(diào)用下游的onComplete方法
}
到這,我們就知道該怎么做了咖城,下面這樣寫就安全了
Observable.timer(100, TimeUnit.MILLISECONDS)
.map(new MyFunction<>())//阻塞操作
.onTerminateDetach() //管道斷開時(shí)茬腿,中斷上游對(duì)下游的引用
.lift(RxLife.lift(this)) //默認(rèn)在onDestroy時(shí)斷開管道
.subscribe(aLong -> {
Log.e("LJX", "accept =" + aLong);
});
可是,每次都要這樣寫嗎宜雀?有沒有更簡(jiǎn)單的切平,有,RxLife提供了RxLife.compose(LifecycleOwner)
方法辐董,內(nèi)部就是將onTerminateDetach
悴品、lift
這兩個(gè)操作符整合在了一起,接下來,看看如何使用
Observable.timer(100, TimeUnit.MILLISECONDS)
.map(new MyFunction<>())//阻塞操作
//注意這里使用compose操作符
.compose(RxLife.compose(this))//默認(rèn)在onDestroy時(shí)中斷管道苔严,并中斷下下游之間的引用
.subscribe(aLong -> {
Log.e("LJX", "accept =" + aLong);
});
如果需要指定生命周期的方法定枷,也可以
Observable.timer(100, TimeUnit.MILLISECONDS)
.map(new MyFunction<>())//阻塞操作
//注意這里使用compose操作符
.compose(RxLife.compose(this, Event.ON_STOP))//指定在onStop時(shí)斷開管道
.subscribe(aLong -> {
Log.e("LJX", "accept =" + aLong);
});
}
大多數(shù)情況下,我們希望觀察者能主線程進(jìn)行回調(diào)届氢,也許你會(huì)這樣寫
Observable.timer(100, TimeUnit.MILLISECONDS)
.map(new MyFunction<>())//阻塞操作
.observeOn(AndroidSchedulers.mainThread()) //在主線程回調(diào)
.compose(RxLife.compose(this, Event.ON_STOP))//指定在onStop回調(diào)時(shí)中斷管道欠窒,并中斷上下游引用
.subscribe(aLong -> {
Log.e("LJX", "accept =" + aLong);
});
如果你是用RxLife的話,就可以這樣寫退子,使用RxLife.composeOnMain
方法
Observable.timer(100, TimeUnit.MILLISECONDS)
.map(new MyFunction<>())//阻塞操作
//在主線程進(jìn)程回調(diào)岖妄,在onStop回調(diào)時(shí)中斷管道,并中斷上下游引用
.compose(RxLife.composeOnMain(this, Event.ON_STOP))
.subscribe(aLong -> {
Log.e("LJX", "accept =" + aLong);
});
RxLife類就只有6個(gè)靜態(tài)方法寂祥,如下
注意荐虐,前方高能預(yù)警!H腊小8壳巍>V椤!恬惯!
結(jié)合RxLife使用Observable的lift
向拆、compose
操作符時(shí),下游除了subscribe
操作符外最好不要有其它的操作符酪耳,前面講過浓恳,當(dāng)調(diào)用Disposable.dispose()
時(shí),它會(huì)往上一層一層的調(diào)用上游的dispose()
方法碗暗,如果下游有Disposable
對(duì)象颈将,是調(diào)用不到的,如果此時(shí)下游有自己的事件需要發(fā)送言疗,那么就無(wú)法攔截了晴圾。
如:
Observable.just(1)
.compose(RxLife.compose(this))
.flatMap((Function<Integer, ObservableSource<Long>>) integer -> {
//每隔一秒發(fā)送一個(gè)數(shù)據(jù),共10個(gè)
return Observable.intervalRange(0, 10, 0, 1, TimeUnit.SECONDS);
})
.subscribe(aLong -> {
Log.e("LJX", "accept =" + aLong);
});
這樣噪奄,即使Activity關(guān)閉了死姚,觀察者每隔一秒后,依然能收到來自上游的事件勤篮,因?yàn)?code>compose無(wú)法切斷下游的管道都毒,我們改一下上面的代碼
Observable.just(1)
.flatMap((Function<Integer, ObservableSource<Long>>) integer -> {
//每隔一秒發(fā)送一個(gè)數(shù)據(jù),共10個(gè)
return Observable.intervalRange(0, 10, 0, 1, TimeUnit.SECONDS);
})
.compose(RxLife.compose(this))
.subscribe(aLong -> {
Log.e("LJX", "accept =" + aLong);
});
這樣ok了碰缔,其實(shí)這不是RxLife
的問題账劲,使用鼎鼎大名的trello/RxLifecycle
庫(kù)也是一樣的,因?yàn)镽xJava的設(shè)計(jì)就是如此,上游拿不到下游的Disposable
對(duì)象涤垫,所以姑尺,我們?cè)谑褂?code>RxLife時(shí),一定要注意在lift
或者compose
操作符的下游蝠猬,除了subscribe
操作符外最好不要有其它的操作符切蟋,這一點(diǎn)一定需要注意。
RxLife最新版本已經(jīng)使用as操作符規(guī)避這個(gè)問題榆芦,詳情查看Android RxLife 一款輕量級(jí)別的RxJava生命周期管理庫(kù)(二)
小彩蛋
RxLife類里面的life柄粹、compose系列方法,皆適用于Flowable匆绣、Observable驻右、Single、Maybe崎淳、Completable這5個(gè)被觀察者對(duì)象堪夭,道理都一樣,這里不在一一講解拣凹。
結(jié)尾
Ok森爽,RxLife的使用基本就介紹完了,到這我們會(huì)發(fā)現(xiàn)嚣镜,使用RxLife庫(kù)爬迟,我們只需要關(guān)注一個(gè)類即可,那即是RxLife類菊匿,api簡(jiǎn)單功能卻強(qiáng)大付呕。敢興趣的同學(xué),可以去閱讀RxLife
的源碼跌捆,有疑問徽职,請(qǐng)留言,我會(huì)在第一時(shí)間作答佩厚。
擴(kuò)展
RxLife結(jié)合HttpSender發(fā)送請(qǐng)求姆钉,簡(jiǎn)直不要太爽。
HttpSender詳情請(qǐng)點(diǎn)擊HttpSender OkHttp+RxJava超好用可款、功能超級(jí)強(qiáng)大的Http請(qǐng)求框架