前言:
Android技能樹系列:
Android基礎知識
Android技能樹 — Android儲存路徑及IO操作小結
資料結構基礎知識
演算法基礎知識
Rx系列相關
Android技能樹 — Rxjava取消訂閱小結(1):自帶方式
Android技能樹 — Rxjava取消訂閱小結(2):RxLifeCycle
現在很多專案都在使用Rxjava了,對於RxJava的使用,估計都很熟悉了,但是很多人在使用RxJava的時候容易產生記憶體洩漏問題,比如我們在用RxJava配合Retrofit的時候,發出請求出去,拿到資料後我們可能會去重新整理介面,但是如果這時候網路比較差,返回比較慢,而我們的Activity這時候關閉了,那RxJava當拿到返回的資料的時候去重新整理介面就會報空指標異常了。所以我們當Activity關閉的時候,我們這時候如果RxJava還沒執行完,我們應該取消訂閱。
常用的主要三種方式:(按照⭐️推薦從低到高來介紹)
- 自帶取消訂閱方式(⭐️)
- RxLifeCycle(⭐️⭐️)
- AutoDispose(⭐️⭐️⭐️)
本文主要講自帶取消訂閱方式。
1. 自帶取消訂閱方式:
在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 一樣的方式來取消訂閱。
1.1 DisposableObserver
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();
複製程式碼
1.2 subscribeWith
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 裡面的寫法一模一樣了。
1.3 CompositeDisposable
我在看很多一些開源專案中,有些人一個介面的上會有多個訂閱(比如有多個網路介面請求),這時候我們需要批量取消訂閱,有些人會寫一個ArrayList,然後把這些上面我們返回的DisposableObserver物件加入到ArrayList中,然後當我們的介面關閉的時候,再遍歷ArrayList,把裡面的元素取出來一個個取消訂閱。實際上RxJava 2 中有替我們考慮到這個需求。那便是CompositeDisposable類。
CompositeDisposable compositeDisposable = new CompositeDisposable();
//批量新增
compositeDisposable.add(observer1);
compositeDisposable.add(observer2);
compositeDisposable.add(observer2);
//最後一次性全部取消訂閱
compositeDisposable.dispose();
複製程式碼
2. 配合MVP做封裝:
我們以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了:
- 只需要繼承BaseFrameActivity,然後把要例項化的P和M物件傳入:
public class SplashActivity
extends BaseFrameActivity<XPresenter, XModel>
implements XContract.View {}
複製程式碼
直接就可以使用mPresenter執行相關操作,並且mPresenter例項化的時候也已經例項化一個RxManager例項物件。
- 假設我們用的是最原始的Observer來訂閱:
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.......請多多指教。