1. 程式人生 > >Android架構中新增AutoDispose解決RxJava記憶體洩漏

Android架構中新增AutoDispose解決RxJava記憶體洩漏

概述

  • 如何通過使用 RxLifecycle 解決Android開發中RxJava的可能會導致的記憶體洩漏問題;

在文章的最後,我提到了 AutoDispose 這個庫,這個庫同樣可以解決Android生命週期元件導致的RxJava的記憶體洩漏情況。

但是不得不考慮的是,目前國內的Android開發圈子中,RxLifecycle已經逐漸為人所熟知,包括著名的一些開源架構也都採用RxLifecycle替代手動的處理方式 。比如 JessYanMVPArms

隨著越來越多的專案添加了RxLifecycle,我需要給出一個足夠有說服力的理由,足夠讓工程師同僚們去嘗試將AutoDispose替換RxLifecycle,或者讓他們在新的專案中優先考慮使用AutoDispose。

我花了一些時間研究了AutoDispose原始碼,並且在公司的新專案中嘗試使用AutoDispose,我嘗試將自己的所得分享出來,希望能夠拋磚引玉——如果看完之後,仍舊覺得 AutoDispose 的思想和設計沒什麼用,至少也讓客官您斬掉這個念頭,繼續安穩地使用 RxLifecycle

本文的主要內容如下:

  • AutoDispose的基礎使用
  • AutoDispose的基本原理
  • AutoDispose和RxLifecycle的區別
  • 如何新增到目前的Android專案中(以MVP架構為例)
  • 小結

基礎使用

官方文件永遠是最好的說明書:

1、新增依賴

compile 'com.uber.autodispose:autodispose:x.y.z'
compile 'com.uber.autodispose:autodispose-android-archcomponents:x.y.z'

x.y.z請參照官網的最新版本,寫這篇文章時,最新版本為0.6.1,即:

compile 'com.uber.autodispose:autodispose:0.6.1'
compile 'com.uber.autodispose:autodispose-android-archcomponents:0.6.1'

2、在你的程式碼中直接使用,比如:

//在Activity中使用
myObservable
    .map(...)
    .subscribeOn(...)
    .observeOn(...)
    .as(AutoDispose.autoDisposable(AndroidLifecycleScopeProvider.from(this
)) .subscribe(s -> ...);

通過給Observable(或者Single、Completable、Flowable等響應式資料型別,本文皆以Observable為例)新增這行程式碼:

as(AutoDispose.autoDisposable(AndroidLifecycleScopeProvider.from(lifecycleOwner))

該Observable就會在Activity的onDestory()生命週期時,自動解除訂閱,以防止因生命週期元件的生命週期而導致的RxJava記憶體洩漏事件。

看到這裡,對比RxLifecycle的程式碼使用方式:

myObservable
    .compose(RxLifecycle.bind(lifecycle))
    .subscribe();

似乎都差不多,都是通過新增一行程式碼實現RxJava事件流與元件生命週期的繫結,那麼替換RxLifecycle的意義何在?

先按住這個問題不表,我先簡單闡述一下AutoDispose的原理。

基本原理

直接長篇大論針對原始碼逐行分析,無疑是愚蠢的——我無法保證讀者能夠耐住性子看完枯燥無味的原始碼分析,然後興致勃勃去嘗試這個庫,這與我初衷不符。

拋開細節,直接闡述其設計思想。

首先前提是,您需要對Google最新的Lifecycle元件有一定的瞭解,在這裡我列出之前寫的部落格以供參考:

同時,如果您對RxLifecycle的原理也掌握的話,相信對於接下來的內容,閱讀起來會更加輕鬆:

我們首先思考三個問題:

as(AutoDispose.autoDisposable(AndroidLifecycleScopeProvider.from(this))

  • this是一個什麼樣的引數?
  • 如何實現生命週期的繫結?
  • as方法執行後生成了一個什麼?

1、我傳了一個什麼引數?

這個this直觀的講,就是Activity本身,當然它也可以是Fragment,這個引數物件只有一個要求,就是必須實現LifecycleOwner介面。

LifecycleOwner介面是Google Android官方架構元件:Lifecycle的一個重要元件,在v7包中,FragmentActivity和Fragment都實現了這個介面,實現了這個介面的物件都擁有生命週期(Lifecycle)。

這意味著,不僅是AppCompatActiviy(FragmentActivity的子類)和Fragment,只要是實現了LifecycleOwner的類,都可以作為引數傳給AutoDispose,用以控制Observable和元件生命週期的繫結。

2、如何實現生命週期的繫結

參考RxLifecycle的原理:

1.在Activity中,定義一個Observable(Subject),在不同的生命週期發射不同的事件;
2.通過compose操作符(內部實際上還是依賴takeUntil操作符),定義了上游資料,當其接收到Subject的特定事件時,取消訂閱;
3.Subject的特定事件並非是ActivityEvent,而是簡單的boolean,它已經內部通過combineLast操作符進行了對應的轉化。

AutoDispose獲取了Activity(LifecycleOwner)物件,並定義了一個新的Observable,在Activity的不同生命週期中,發射對應的事件。

和RxLifecycle很類似的是,AutoDispose在被訂閱時,獲取到Activity當前的生命週期,並找到對應需要結束訂閱的生命週期事件:

  private static final Function<Lifecycle.Event, Lifecycle.Event> DEFAULT_CORRESPONDING_EVENTS =
      new Function<Lifecycle.Event, Lifecycle.Event>() {
        @Override public Lifecycle.Event apply(Lifecycle.Event lastEvent) throws Exception {
          switch (lastEvent) {
            case ON_CREATE:
              return Lifecycle.Event.ON_DESTROY;//比如我在onCreate到onStart之間訂閱,我會在ON_DESTROY時結束訂閱
            case ON_START:
              return Lifecycle.Event.ON_STOP;
            case ON_RESUME:
              return Lifecycle.Event.ON_PAUSE;
            case ON_PAUSE:
              return Lifecycle.Event.ON_STOP;
            case ON_STOP://如果是onPause之後訂閱,會丟擲異常
            case ON_DESTROY:
            default:
              throw new LifecycleEndedException("Lifecycle has ended! Last event was " + lastEvent);
          }
        }
      };

也就是說,在我們的ObservableA訂閱時,就已經知道了自己在Activity的哪個生命週期讓AutoDispose內部自定義的ObservableB自動發射事件,ObservableA監聽到這個事件時且未dispose,解除訂閱避免記憶體洩漏。

畢竟記憶體洩漏是少數,更大的可能是ObservableA早就執行完任務dispose了,因此ObservableB實際上就是一個Maybe,類似於

ObservableA.takeUntil( Maybe< true > )

下面為核心程式碼:

 public static <E> Maybe<LifecycleEndNotification> resolveScopeFromLifecycle(
      Observable<E> lifecycle,
      final E endEvent) {
    return lifecycle.skip(1)
        .map(new Function<E, Boolean>() {
          @Override public Boolean apply(E e) throws Exception {
            return e.equals(endEvent);//是否是要解除訂閱的生命週期事件
          }
        })
        .filter(IDENTITY_BOOLEAN_PREDICATE)//篩選為true的資料,即篩選解除訂閱的生命週期事件
        .map(TRANSFORM_TO_END)//轉換為LifecycleEndNotification.INSTANCE,這個列舉只用來通知解除訂閱
        .firstElement();//返回值為Maybe,因為可能不到對應生命週期,ObservableA就已經完成任務,onComplete() -> dispose了
  }

  private static final Function<Object, LifecycleEndNotification> TRANSFORM_TO_END =
      new Function<Object, LifecycleEndNotification>() {
        @Override public LifecycleEndNotification apply(Object o) throws Exception {
          return LifecycleEndNotification.INSTANCE;
        }
      };

  public enum LifecycleEndNotification {
    INSTANCE
  }

3、as方法執行後生成了一個什麼?

as方法內部生成了一個AutoDisposeConverter物件,類似於compose,不同的是,Observable通過compose生成的物件還是Observable,但as方法生成的則是一個新的物件:

public final <R> R as(@NonNull ObservableConverter<T, ? extends R> converter)

實際上,拋開復雜的細節,AutoDispose最終將原來的Observable,生成了一個新的AutoDisposeObservable物件, 在執行訂閱時,也生成了一個新的AutoDisposingObserverImpl物件,篇幅所限,不再細述。

AutoDispose和RxLifecycle的區別

從上面的原理來講,似乎AutoDispose和RxLifecycle兩者沒什麼區別,原理都極為相似。

事實確實如此,因為AutoDispose本身就是很大一部分借鑑了RxLifecycle,同時,RxLifecycle的作者Daniel Lew 對於 AutoDispose的開發也有很多幫助:

以下摘錄於AutoDispose的官方文件:

Special thanks go to Dan Lew (creator of RxLifecycle), who helped pioneer this area for RxJava in android and humored many of the discussions around lifecycle handling over the past couple years that we’ve learned from. Much of the internal scope resolution mechanics of
AutoDispose are inspired by RxLifecycle.

那麼,究竟是什麼原因,讓RxLifecycle的作者Daniel Lew 在他自己的文章中,羅列出RxLifecycle在開發中的窘境,並同時強烈推薦使用AutoDispose呢?

I’m going to keep maintaining RxLifecycle because people are still using it (including Trello), but in the long term I’m pulling away from it. For those still wanting this sort of library, I would suggest people look into AutoDispose, since I think it is better architecturally than RxLifecycle.
(我將會繼續維護RxLifecycle因為很多人都在使用它,但是我會漸漸放棄這個庫,我建議大家可以參考AutoDispose,因為我認為它的設計更加優秀 )

壓倒性優勢?

我們來看看RxLifecycle的侷限性:

1、需要繼承父類(RxActivity / RxFragment等)

對於設計來講,【組合】的靈活度大多數情況都優於【繼承】,而RxLifecycle在父類中聲明瞭一個PublishSubject,用來發射生命週期事件,這是導致其侷限性的原因之一。

2、如何處處繫結生命週期?

最簡單的例子,我的RecyclerView的Adapter中訂閱了Observable,亦或者,在MVP的架構或者MVVM架構中,我的presenter或者我的viewModel無法直接獲取RxActivity的引用(作為View層,更應該抽象為一個介面與Presenter進行互動)。

這意味著,想要進行Observable的生命週期繫結,在RecyclerView的Adapter中,我必須要通過將Activity作為依賴,注入到Adapter中:

new ListAdapter(RxActivity activity);

而對於Presenter,我需要對View抽象介面進行instanceof 的判斷:

if (view instanceof RxActivity) {
   return bindToLifecycle((RxActivity) view);
}

當然還有一些其他的情況,請參考原作者的文章:

如何新增到目前的Android專案中(以MVP架構為例)

AutoDispose正常直接在專案中使用沒有什麼問題,比如:

//在Activity中使用
myObservable
    .as(AutoDispose.autoDisposable(AndroidLifecycleScopeProvider.from(this))  
    .subscribe(s -> ...);

但是,在實際的生產環境中,我們更希望有一個良好的方式,能夠達到統一管理Observable繫結生命週期的效果,在此筆者以MVP的架構為例,拋磚引玉,希望能夠獲得大家的意見和建議。

1、封裝Util類

首先封裝Util類,將職責賦予RxLifecycleUtils:

public class RxLifecycleUtils {

    private RxLifecycleUtils() {
        throw new IllegalStateException("Can't instance the RxLifecycleUtils");
    }

    public static <T> AutoDisposeConverter<T> bindLifecycle(LifecycleOwner lifecycleOwner) {
        return AutoDispose.autoDisposable(
                AndroidLifecycleScopeProvider.from(lifecycleOwner)
        );
    }
}

2、面向LifecycleOwner介面

現在,只要持有LifecycleOwner物件,Observable都可以通過RxLifecycleUtils.bindLifecycle(LifecycleOwner)進行繫結。

比如我們在BaseActivity/BaseFragment中新增程式碼:

public abstract class BaseActivity extends AppCompatActivity implements IActivity {
    //...忽略其他細節
    protected <T> AutoDisposeConverter<T> bindLifecycle() {
        return RxLifecycleUtils.bindLifecycle(this);
    }
}

這樣,在任何BaseActivity的實現類中,我們都可以通過下述程式碼實現Observable的生命週期繫結:

myObservable
    .as(bindLifecycle())  
    .subscribe(s -> ...);

但是這樣的行為意義不大,我們更希望在Presenter中也能夠像上述程式碼一樣達到Observable生命週期的繫結,但是不持有Activity的物件的同時,避免RxLifecycle中尷尬的instanceof判斷。

可行嗎,可行。

3、使用Google官方Lifecycle元件

首先讓我們的IPresenter介面實現LifecycleObserver介面:

public interface IPresenter extends LifecycleObserver {

    @OnLifecycleEvent(Lifecycle.Event.ON_CREATE)
    void onCreate(@NotNull LifecycleOwner owner);

    @OnLifecycleEvent(Lifecycle.Event.ON_DESTROY)
    void onDestroy(@NotNull LifecycleOwner owner);

    @OnLifecycleEvent(Lifecycle.Event.ON_ANY)
    void onLifecycleChanged(@NotNull LifecycleOwner owner,
                            @NotNull Lifecycle.Event event);
}

然後在BasePresenter中管理LifecycleOwner:

public class BasePresenter<V extends IView, M extends IModel> implements IPresenter {

    @Getter
    protected V mRootView;

    @Getter
    protected M mModel;

    private LifecycleOwner lifecycleOwner;

    public BasePresenter(V rootView, M model) {
        this.mRootView = rootView;
        this.mModel = model;
    }

    protected <T> AutoDisposeConverter<T> bindLifecycle() {
        if (null == lifecycleOwner)
            throw new NullPointerException("lifecycleOwner == null");
        return RxLifecycleUtils.bindLifecycle(lifecycleOwner);

    public void onCreate(@NotNull LifecycleOwner owner) {
        this.lifecycleOwner = owner;
    }

    public void onDestroy(@NotNull LifecycleOwner owner) {
            if (mModel != null) {
                mModel.onDestroy();
                this.mModel = null;
            }
            this.mRootView = null;
        }
    }

最後在Activity中新增Presenter為觀察者,觀察Activity的生命週期

public abstract class BaseActivity<P extends IPresenter> extends AppCompatActivity implements IActivity {

    @Inject
    protected P presenter;

        @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(getLayoutId());
        //...忽略其他程式碼,比如ButterKnife、Dagger2等
        lifecycle.addObserver(presenter);
    }       

    protected <T> AutoDisposeConverter<T> bindLifecycle() {
        return RxLifecycleUtils.bindLifecycle(this);
    }
}

這樣,我們即使在Presenter中,也能任意使用myObservable.as(bindLifecycle()) 方法了,和RxLifecycle相比,更加靈活。

篇幅所限,僅以最簡單的設計思路進行闡述,更多情況的使用請在專案中自行拓展。

2018.9追加

時隔數月,我的個人MVP架構:MvpArchitecture-Android 已經無法滿足我個人開發的需求,於我而言它更像是一個demo,因此我停止了對它的維護。

相比前者,我更喜歡MVVM架構中AutoDispose的這次實踐,它更趨近我理想中的設計,更加ReactiveFunctional

小結

事實上AutoDispose還有很多優秀的設計點,在原始碼中得以體現(包括自己實現Observable和其Observer,以及通過代理對內部的Observable進行封裝,對原子操作類的靈活運用 等等)。

但無論是RxLifecycle,AutoDispose,亦或者是自己封裝的util類,對於專案來說,只要滿足需求,並無優劣之分,我們更希望能夠從一些他人的框架中,學習到他們設計的思想,和良好的程式碼風格,便足矣。

歡迎拍磚,一起探討進步。