Android技能樹 — Rxjava取消訂閱小結(1):自帶方式

青蛙要fly發表於2018-05-29

前言:

Android技能樹系列:

Android基礎知識

Android技能樹 — 動畫小結

Android技能樹 — View小結

Android技能樹 — Activity小結

Android技能樹 — View事件體系小結

Android技能樹 — Android儲存路徑及IO操作小結

Android技能樹 — 多程式相關小結

Android技能樹 — Drawable小結

資料結構基礎知識

Android技能樹 — 陣列,連結串列,雜湊表基礎小結

Android技能樹 — 樹基礎知識小結(一)

演算法基礎知識

Android技能樹 — 排序演算法基礎小結

Rx系列相關

Android技能樹 — RxPermission分析

Android技能樹 — Rxjava取消訂閱小結(1):自帶方式

Android技能樹 — Rxjava取消訂閱小結(2):RxLifeCycle

現在很多專案都在使用Rxjava了,對於RxJava的使用,估計都很熟悉了,但是很多人在使用RxJava的時候容易產生記憶體洩漏問題,比如我們在用RxJava配合Retrofit的時候,發出請求出去,拿到資料後我們可能會去重新整理介面,但是如果這時候網路比較差,返回比較慢,而我們的Activity這時候關閉了,那RxJava當拿到返回的資料的時候去重新整理介面就會報空指標異常了。所以我們當Activity關閉的時候,我們這時候如果RxJava還沒執行完,我們應該取消訂閱。

常用的主要三種方式:(按照⭐️推薦從低到高來介紹)

  1. 自帶取消訂閱方式(⭐️)
  2. RxLifeCycle(⭐️⭐️)
  3. AutoDispose(⭐️⭐️⭐️)

Android技能樹 — Rxjava取消訂閱小結(1):自帶方式

本文主要講自帶取消訂閱方式。

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 取消訂閱方式差不多 。答案是當然可以。

我們可以使用DisposableObserversubscribeWith二者結合來做的和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();
複製程式碼

Android技能樹 — Rxjava取消訂閱小結(1):自帶方式

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了:

  1. 只需要繼承BaseFrameActivity,然後把要例項化的P和M物件傳入:
public class SplashActivity 
      extends BaseFrameActivity<XPresenter, XModel> 
      implements XContract.View {}
複製程式碼

直接就可以使用mPresenter執行相關操作,並且mPresenter例項化的時候也已經例項化一個RxManager例項物件。

  1. 假設我們用的是最原始的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.......請多多指教。

相關文章