Android技能樹系列:java
Android基礎知識算法
Android技能樹 — Android存儲路徑及IO操做小結多線程
數據結構基礎知識ide
算法基礎知識
Rx系列相關
Android技能樹 — Rxjava取消訂閱小結(1):自帶方式
Android技能樹 — Rxjava取消訂閱小結(2):RxLifeCycle
如今不少項目都在使用Rxjava了,對於RxJava的使用,估計都很熟悉了,可是不少人在使用RxJava的時候容易產生內存泄漏問題,好比咱們在用RxJava配合Retrofit的時候,發出請求出去,拿到數據後咱們可能會去刷新界面,可是若是這時候網絡比較差,返回比較慢,而咱們的Activity這時候關閉了,那RxJava當拿到返回的數據的時候去刷新界面就會報空指針異常了。因此咱們當Activity關閉的時候,咱們這時候若是RxJava還沒執行完,咱們應該取消訂閱。
經常使用的主要三種方式:(按照⭐️推薦從低到高來介紹)
本文主要講自帶取消訂閱方式。
在RxJava 1的時候咱們知道在你用Observable
執行時候會返回一個Subscription
類:
Subscription subscription = Observable.xxx("yy").subscribe(.....);
複製代碼
而後咱們只須要在咱們界面的ondestory方法中對這個對象進行取消訂閱操做就能夠:
@Override
protected void onDestroy() {
if (subscription != null && !subscription.isUnsubscribed) {
subscription. unsubscribe();
}
super.onDestroy();
}
複製代碼
咱們能夠看到很簡單,這樣當咱們Activity關閉的時候已經自動取消了訂閱。
而RxJava2換了方式,可是基本方法是如出一轍的,只是換成了Disposable
:
若是咱們使用的是Consumer
,那和原來RxJava 1 是同樣的操做:
Disposable disposable = Observable.just(1).subscribe(new Consumer<Integer>() {
@Override
public void accept(@NonNull Integer integer) throws Exception {
}
});
//取消訂閱
if(disposable != null && !disposable.isDisposed()){
disposable.dispose();
}
複製代碼
可是咱們可能更多的是使用Observer等,那這時候subscrbe(observer)返回的是void
,因此不能像上面同樣操做,須要下面代碼所示那樣:
private Disposable disposable;
Observable.just(1).subscribe(new Observer<Integer>() {
@Override
public void onSubscribe(Disposable d) {
disposable = d;
}
@Override
public void onNext(Integer integer) {}
@Override
public void onError(Throwable e) {}
@Override
public void onComplete() {}
});
//而後在須要取消訂閱的地方調用便可
if(disposable != null && !disposable.isDisposed()){
disposable.dispose();
}
複製代碼
和RxJava 1 最大的區別主要是獲取這個取消訂閱對象的地方不一樣,Disposable
是在Observer裏面的onSubscribe
方法的參數拿到,而後咱們能夠定義一個臨時變量進行賦值,而後在須要取消訂閱的地方去調用便可。
可是不少人會說難道不能和RxJava 1 的方式差很少,由於不少項目已經按照RxJava 1 的方式來封裝了進行相應的取消訂閱代碼,直接換成RxJava 2 方式變化不同了,能不能變得和Rxjava 1 取消訂閱方式差很少 。答案是固然能夠。
咱們可使用DisposableObserver
和subscribeWith
兩者結合來作的和Rxjava 1 同樣的方式來取消訂閱。
DisposableObserver 是一個抽象的 Observer, 它經過實現了 Disposable 接口容許異步取消。
/**
* An abstract {@link Observer} that allows asynchronous cancellation by implementing Disposable.
*
* @param <T> the received value type
*/
public abstract class DisposableObserver<T> implements Observer<T>, Disposable {
final AtomicReference<Disposable> s = new AtomicReference<Disposable>();
@Override
public final void onSubscribe(Disposable s) {
if (DisposableHelper.setOnce(this.s, s)) {
onStart();
}
}
/**
* Called once the single upstream Disposable is set via onSubscribe.
*/
protected void onStart() {
}
@Override
public final boolean isDisposed() {
return s.get() == DisposableHelper.DISPOSED;
}
@Override
public final void dispose() {
DisposableHelper.dispose(s);
}
}
複製代碼
咱們能夠看到,這個DisposableObserver即實現了Observer,又實現了Disposable接口。
PS : DisposableObserver源碼裏面有個AtomicReference,有些人也許不知道這個類,能夠初步理解爲加了鎖,方便多線程操做。具體能夠看文章Java之美[從菜鳥到高手演練]之atomic包的原理及分析
因此咱們初步代碼能夠變爲:
//好比這個是咱們的Observer
DisposableObserver observer = new DisposableObserver() {
@Override
public void onNext(Object o) {}
@Override
public void onError(Throwable e) {}
@Override
public void onComplete() {}
};
//把咱們的Observer對Observable進行訂閱
Observable.just(1).subscribe(observer);
//而後在須要取消訂閱的地方對這個observer進行取消訂閱便可。
observer.dispose();
複製代碼
public final <E extends Observer<? super T>> E subscribeWith(E observer) {
subscribe(observer);
return observer;
}
複製代碼
咱們能夠看到 subscribeWith
訂閱的源碼是把Observer對象同時返回,正好配合上面的DisposableObserver:
DisposableObserver observer = Observable.just(1).subscribeWith(new DisposableObserver<Integer>() {
@Override
public void onNext(Integer integer) {}
@Override
public void onError(Throwable e) {}
@Override
public void onComplete() {}
});
//須要取消訂閱的地方:
observer.disposable();
複製代碼
這下是否是和咱們RxJava 1 裏面的寫法如出一轍了。
我在看不少一些開源項目中,有些人一個界面的上會有多個訂閱(好比有多個網絡接口請求),這時候咱們須要批量取消訂閱,有些人會寫一個ArrayList,而後把這些上面咱們返回的DisposableObserver對象加入到ArrayList中,而後當咱們的界面關閉的時候,再遍歷ArrayList,把裏面的元素取出來一個個取消訂閱。實際上RxJava 2 中有替咱們考慮到這個需求。那即是CompositeDisposable類。
CompositeDisposable compositeDisposable = new CompositeDisposable();
//批量添加
compositeDisposable.add(observer1);
compositeDisposable.add(observer2);
compositeDisposable.add(observer2);
//最後一次性所有取消訂閱
compositeDisposable.dispose();
複製代碼
咱們以Activity爲例:
public abstract class BaseFrameActivity<P extends BasePresenter, M extends BaseModel> extends BaseActivity implements BaseView {
public P mPresenter;
public M mModel;
@Override
protected void onCreate(Bundle savedInstanceState) {
//根據傳進來的第一個泛型參數實例化
mPresenter = TUtil.getT(this, 0);
//根據傳進來的第二個泛型參數實例化
mModel = TUtil.getT(this, 1);
if (this instanceof BaseView && mPresenter != null) {
//實例化的presneter綁定View和model
mPresenter.attachVM(this, mModel);
}
super.onCreate(savedInstanceState);
}
@Override
protected void onDestroy() {
if (mPresenter != null) {
//當onDestory的時候調用presenter的解除View和model的綁定
mPresenter.detachVM();
}
super.onDestroy();
}
}
複製代碼
好比咱們在BaseFrameActivity裏面傳入了p 和 m 的泛型,咱們須要動態實例化,固然你也能夠用Dagger2等,好比咱們是用反射:
public class TUtil {
public static <T> T getT(Object o, int i) {
try {
/**
* getGenericSuperclass() : 得到帶有泛型的父類
* ParameterizedType : 參數化類型,即泛型
* getActualTypeArguments()[] : 獲取參數化類型的數組,泛型可能有多個
*/
return ((Class<T>) ((ParameterizedType) (o.getClass()
.getGenericSuperclass())).getActualTypeArguments()[i])
.newInstance();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (ClassCastException e) {
e.printStackTrace();
}
return null;
}
// 得到類名className對應的Class對象
public static Class<?> forName(String className) {
try {
return Class.forName(className);
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
return null;
}
}
複製代碼
咱們來看BasePresenter.java:
public abstract class BasePresenter<M, V> {
public M mModel;
public V mView;
public RxManager mRxManager = new RxManager();
public void attachVM(V v, M m) {
this.mModel = m;
this.mView = v;
}
public void detachVM() {
mRxManager.clear();
mView = null;
mModel = null;
mDialog = null;
}
}
複製代碼
咱們把Observable等取消訂閱操做放在了RxManager裏面了:
public class RxManager {
private CompositeDisposable compositeDisposable = new CompositeDisposable();
public void add(Disposable d) {
compositeDisposable.add(d);
}
public void clear() {
compositeDisposable.dispose();
}
}
複製代碼
最終好比咱們要用本身的Activity了:
public class SplashActivity
extends BaseFrameActivity<XPresenter, XModel>
implements XContract.View {}
複製代碼
直接就可使用mPresenter執行相關操做,而且mPresenter實例化的時候也已經實例化一個RxManager實例對象。
public class XPresenter extends XContract.Presenter {
@Override
public void getXImage() {
mModel
.getXImage()
.subscribe(new Observer<SplashImgEntity>() {
@Override
public void onSubscribe(Disposable d) {
//自動就會把Disposable加入到RxManager中的CompositeDisposable 中。
mRxManager.add(d);
}
@Override
public void onNext(SplashImgEntity splashImgEntity) {
}
@Override
public void onError(Throwable e) {
}
@Override
public void onComplete() {
}
});
}
}
複製代碼
而後Activity銷燬時候,會本身去幫你取消訂閱。
emmmmmm.......請多多指教。