Android之解決RxJava內存泄漏:RxLifecycle詳解及原理分析

前言

隨着RxJava及RxAndroid的逐漸推廣,使用者越來越多,但是有一個問題,RxJava的使用不當極有可能會導致內存泄漏。

比如,使用RxJava發佈一個訂閱後,當Activity被finish,此時訂閱邏輯還未完成,如果沒有及時取消訂閱,就會導致Activity無法被回收,從而引發內存泄漏。

目前網上對RxJava的內存泄漏有幾種方案:

1、通過封裝,手動爲RxJava的每一次訂閱進行控制,在指定的時機進行取消訂閱;
2、使用 Daniel Lew的 RxLifecycle,通過監聽Activity、Fragment的生命週期,來自動斷開subscription以防止內存泄漏。

筆者上述兩種方式都使用過,RxLifecycle顯然對於第一種方式,更簡單直接,並且能夠在Activity/Fragment容器的指定生命週期取消訂閱,實在是好用。

依賴並使用RxLifecycle

1、添加依賴

首先在build.gradle文件中添加依賴:

compile 'com.trello.rxlifecycle2:rxlifecycle:2.2.1'
compile 'com.trello.rxlifecycle2:rxlifecycle-android:2.2.1'
compile 'com.trello.rxlifecycle2:rxlifecycle-components:2.2.1'

2、配置Activity/Fragment容器

Activity/Fragment需繼承RxAppCompatActivity/RxFragment,目前支持的有如下:

RxLifecycle支持的Component

代碼如下:

//只需要繼承即可
public class MainActivity extends RxAppCompatActivity {
      ...
      ...
}

3、使用compose操作符綁定容器生命週期

有兩種方式:

3.1 使用bindToLifecycle()

以Activity爲例,在Activity中使用bindToLifecycle()方法,完成Observable發佈的事件和當前的組件綁定,實現生命週期同步。從而實現當前組件生命週期結束時,自動取消對Observable訂閱,代碼如下:

public class MainActivity extends RxAppCompatActivity {
     
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        // 當執行onDestory()時, 自動解除訂閱
        Observable.interval(1, TimeUnit.SECONDS)
            .doOnDispose(new Action() {
                @Override
                public void run() throws Exception {
                    Log.i(TAG, "Unsubscribing subscription from onCreate()");
                }
            })
            .compose(this.<Long>bindToLifecycle())
            .subscribe(new Consumer<Long>() {
                @Override
                public void accept(Long num) throws Exception {
                    Log.i(TAG, "Started in onCreate(), running until onDestory(): " + num);
                }
            });
    }
}

3.2 使用bindUntilEvent()

使用ActivityEvent類,其中的CREATE、START、 RESUME、PAUSE、STOP、 DESTROY分別對應生命週期內的方法。使用bindUntilEvent指定在哪個生命週期方法調用時取消訂閱:

public class MainActivity extends RxAppCompatActivity {
    @Override
    protected void onResume() {
        super.onResume();
        Observable.interval(1, TimeUnit.SECONDS)
            .doOnDispose(new Action() {
                @Override
                public void run() throws Exception {
                    Log.i(TAG, "Unsubscribing subscription from onResume()");
                }
            })
              //bindUntilEvent(),內部傳入指定生命週期參數
            .compose(this.<Long>bindUntilEvent(ActivityEvent.DESTROY))
            .subscribe(new Consumer<Long>() {
                @Override
                public void accept(Long num) throws Exception {
                    Log.i(TAG, "Started in onResume(), running until in onDestroy(): " + num);
                }
            });
    }
}

以上,僅僅需要三步:依賴、繼承、compose操作符,即可完成在容器的指定生命週期內,RxJava的自動取消訂閱。

原理分析

RxLifecycle的原理可以說非常簡單。

我們直接看一下這行代碼的內部原理:

Observable.compose(this.<Long>bindToLifecycle())

1、RxAppCompatActivity

public abstract class RxAppCompatActivity extends AppCompatActivity implements LifecycleProvider<ActivityEvent> {
    
    //1.實際上RxAppCompatActivity內部存儲了一個BehaviorSubject
    private final BehaviorSubject<ActivityEvent> lifecycleSubject = BehaviorSubject.create();

    public final Observable<ActivityEvent> lifecycle() {
        return lifecycleSubject.hide();
    }

    public final <T> LifecycleTransformer<T> bindUntilEvent(@NonNull ActivityEvent event) {
        return RxLifecycle.bindUntilEvent(lifecycleSubject, event);
    }
    
    //2.實際上返回了一個LifecycleTransformer
    public final <T> LifecycleTransformer<T> bindToLifecycle() {
        return RxLifecycleAndroid.bindActivity(lifecycleSubject);
    }
    
    //3.Activity不同的生命週期,BehaviorSubject對象會發射對應的ActivityEvent
    @Override
    @CallSuper
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        lifecycleSubject.onNext(ActivityEvent.CREATE);
    }

    @Override
    @CallSuper
    protected void onStart() {
        super.onStart();
        lifecycleSubject.onNext(ActivityEvent.START);
    }

    @Override
    @CallSuper
    protected void onResume() {
        super.onResume();
        lifecycleSubject.onNext(ActivityEvent.RESUME);
    }

    @Override
    @CallSuper
    protected void onPause() {
        lifecycleSubject.onNext(ActivityEvent.PAUSE);
        super.onPause();
    }

    @Override
    @CallSuper
    protected void onStop() {
        lifecycleSubject.onNext(ActivityEvent.STOP);
        super.onStop();
    }

    @Override
    @CallSuper
    protected void onDestroy() {
        lifecycleSubject.onNext(ActivityEvent.DESTROY);
        super.onDestroy();
    }
}

我們繼承的RxAppCompatActivity,其內部實際上存儲了一個BehaviorSubject,關於BehaviorSubject,實際上也還是一個Observable,不瞭解的朋友可以閱讀筆者的前一篇文章,本文不再贅述:

理解RxJava(四)Subject用法及原理分析

這個BehaviorSubject會在不同的生命週期發射不同的ActivityEvent,比如在onCreate()生命週期發射ActivityEvent.CREATE,在onStop()發射ActivityEvent.STOP。

在2中,我們可以看到,bindToLifecycle()方法實際返回了一個LifecycleTransformer,那麼這個LifecycleTransformer是什麼呢?

2、LifecycleTransformer

public final class LifecycleTransformer<T> implements ObservableTransformer<T, T>,
                                                      FlowableTransformer<T, T>,
                                                      SingleTransformer<T, T>,
                                                      MaybeTransformer<T, T>,
                                                      CompletableTransformer
{
    final Observable<?> observable;

    LifecycleTransformer(Observable<?> observable) {
        checkNotNull(observable, "observable == null");
        this.observable = observable;
    }

    @Override
    public ObservableSource<T> apply(Observable<T> upstream) {
        return upstream.takeUntil(observable);
    }
    //隱藏其餘代碼,這裏只以Observable爲例
}

熟悉Transformer的同學都應該能看懂,LifecycleTransformer實際是實現了不同響應式數據類(Observable、Flowable等)的Transformer接口。

以Observable爲例,實際上就是通過傳入一個Observable序列並存儲爲成員,然後作爲參數給上游的數據源執行takeUntil方法。

takeUntil操作符

takeUntil操作符:當第二個Observable發射了一項數據或者終止時,丟棄原Observable發射的任何數據。

 

回顧

 Observable.interval(1, TimeUnit.SECONDS)
            .doOnDispose(new Action() {
                @Override
                public void run() throws Exception {
                    Log.i(TAG, "Unsubscribing subscription from onCreate()");
                }
            })
            .compose(this.<Long>bindToLifecycle())
            .subscribe(new Consumer<Long>() {
                @Override
                public void accept(Long num) throws Exception {
                    Log.i(TAG, "Started in onCreate(), running until onDestory(): " + num);
                }
            });

現在回頭來看這段代碼,就很好理解了,Activity.<Long>bindToLifecycle()實際上就是指定上游的數據源,當接收到某個Observable(就是LifecycleTransformer中那個神祕的成員變量)的某個事件時,該數據源自動解除訂閱。

老師,原理已經搞清楚了,我還有最後一個問題:

神祕人是誰?

回到RxAppCompatActivity中來,我們來看bindToLifecycle()方法:

public abstract class RxAppCompatActivity extends AppCompatActivity implements LifecycleProvider<ActivityEvent> {

    private final BehaviorSubject<ActivityEvent> lifecycleSubject = BehaviorSubject.create();

    public final <T> LifecycleTransformer<T> bindToLifecycle() {
        //執行了這行代碼,返回了LifecycleTransformer
        return RxLifecycleAndroid.bindActivity(lifecycleSubject);
    }
}

不難猜測,實際上,那個神祕人,就是我們RxAppCompatActivity 中的BehaviorSubject成員變量(它本身就是一個Observable)!

我們點進去看看:

    //1.執行了 bind(lifecycle, ACTIVITY_LIFECYCLE);
    public static <T> LifecycleTransformer<T> bindActivity(@NonNull final Observable<ActivityEvent> lifecycle) {
        return bind(lifecycle, ACTIVITY_LIFECYCLE);
    }
    
    //2.執行了bind(takeUntilCorrespondingEvent(lifecycle.share(), correspondingEvents))
    public static <T, R> LifecycleTransformer<T> bind(@Nonnull Observable<R> lifecycle,
                                                      @Nonnull final Function<R, R> correspondingEvents) {
        return bind(takeUntilCorrespondingEvent(lifecycle.share(), correspondingEvents));
    }

    //3.最終抵達這裏,這個方法執行了什麼呢?
    private static <R> Observable<Boolean> takeUntilCorrespondingEvent(final Observable<R> lifecycle,
                                                                       final Function<R, R> correspondingEvents) {
        return Observable.combineLatest(
            lifecycle.take(1).map(correspondingEvents),
            lifecycle.skip(1),
            new BiFunction<R, R, Boolean>() {
                @Override
                public Boolean apply(R bindUntilEvent, R lifecycleEvent) throws Exception {
                    return lifecycleEvent.equals(bindUntilEvent);
                }
            })
            .onErrorReturn(Functions.RESUME_FUNCTION)
            .filter(Functions.SHOULD_COMPLETE);
    }

最後我們走到了3,我們一行一行分析:

Observable.combineLatest

這行代碼實際上是將lifecycle(就是我們傳進來的BehaviorSubject)的事件進行了一次分割:

lifecycle.take(1)指的是最近發射的事件,比如說我們在onCreate()中執行了bindToLifecycle,那麼lifecycle.take(1)指的就是ActivityEvent.CREATE,經過map(correspondingEvents),這個map中傳的函數就是 1中的ACTIVITY_LIFECYCLE:

private static final Function<ActivityEvent, ActivityEvent> ACTIVITY_LIFECYCLE =
        new Function<ActivityEvent, ActivityEvent>() {
            @Override
            public ActivityEvent apply(ActivityEvent lastEvent) throws Exception {
                switch (lastEvent) {
                    case CREATE:
                        return ActivityEvent.DESTROY;
                    case START:
                        return ActivityEvent.STOP;
                    case RESUME:
                        return ActivityEvent.PAUSE;
                    case PAUSE:
                        return ActivityEvent.STOP;
                    case STOP:
                        return ActivityEvent.DESTROY;
                    case DESTROY:
                        throw new OutsideLifecycleException("Cannot bind to Activity lifecycle when outside of it.");
                    default:
                        throw new UnsupportedOperationException("Binding to " + lastEvent + " not yet implemented");
                }
            }
        };

也就是說,lifecycle.take(1).map(correspondingEvents)實際上是返回了 CREATE對應的事件 DESTROY,它意味着本次訂閱將在Activity的onDestory進行取消。

lifecycle.skip(1)就簡單了,除去第一個保留剩下的,以ActivityEvent.Create爲例,這裏就剩下:

ActivityEvent.START
ActivityEvent.RESUME
ActivityEvent.PAUSE
ActivityEvent.STOP
ActivityEvent.DESTROY

第三個參數 意味着,lifecycle.take(1).map(correspondingEvents)的序列和 lifecycle.skip(1)進行combine,形成一個新的序列:

false,false,fasle,false,true

這意味着,當Activity走到onStart生命週期時,爲false,這次訂閱不會取消,直到onDestroy,爲true,訂閱取消。

而後的onErrorReturn和filter是對異常的處理和判斷是否應該結束訂閱:

    //異常處理
    static final Function<Throwable, Boolean> RESUME_FUNCTION = new Function<Throwable, Boolean>() {
        @Override
        public Boolean apply(Throwable throwable) throws Exception {
            if (throwable instanceof OutsideLifecycleException) {
                return true;
            }
            Exceptions.propagate(throwable);
            return false;
        }
    };
    //是否應該取消訂閱,可以看到,這依賴於上游的boolean
    static final Predicate<Boolean> SHOULD_COMPLETE = new Predicate<Boolean>() {
        @Override
        public boolean test(Boolean shouldComplete) throws Exception {
            return shouldComplete;
        }
    };

bind生成LifecycleTransformer

看懂了3,我們回到2,我們生成了一個Observable,然後通過bind(Observable)方法,生成LifecycleTransformer並返回:

    public static <T, R> LifecycleTransformer<T> bind(@Nonnull final Observable<R> lifecycle) {
        return new LifecycleTransformer<>(lifecycle);
    }

神祕人的神祕面紗就此揭開。

總結

RxLifecycle並不難以理解,相反,它的設計思路很簡單:

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

實際上,Subject和ActivityEvent對RxLifecycle的使用者來講,是對應隱藏的。我們只需要調用它提供給我們的API,而內部的實現者我們無需考慮,但是也只有去閱讀和理解了它的思想,我們才能更好的選擇使用這個庫。

轉折:AutoDispose

在我沉迷於RxLifecycle對項目的便利時,一個機會,我有幸閱讀到了 Daniel Lew的文章《Why Not RxLifecycle?》(爲什麼放棄使用RxLifecycle)

作爲RxLifecycle的作者,Daniel Lew客觀陳述了使用RxLifecycle在開發時所遇到的窘境。並且爲我們提供了一個他認爲更優秀的設計:

AutoDispose: Automatic binding+disposal of RxJava 2 streams.

我花了一些時間研究了一下AutoDispose,不得不承認,AutoDispose相比較RxLifecycle,前者更加健壯,並且擁有更優秀的拓展性,如果我的項目需要一個處理RxJava自動取消訂閱的庫,也許AutoDispose更爲適合。

更讓我感到驚喜的是,AutoDispose的設計思想,有很大一部分和RxLifecycle相似,而且我在其文檔上,看到了Uber的工程師對於Daniel Lew所維護的 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
AutoDisposeare inspired by RxLifecycle.

在我嘗試將AutoDispose應用在項目中時,我發現國內對於這個庫所應用的不多,我更希望有更多朋友能夠和我一起使用這個庫,於是我準備寫一篇關於AutoDispose使用方式的文章。

但在這之前,我還是希望能夠先將RxLifecycle的個人心得分享給大家,原因有二:

一是RxLifecycle更簡單,原理也更好理解,學習一個庫不僅僅是爲了會使用它,我們更希望能夠從源碼中學習到作者優秀的設計和思想。因此,如果是從未接觸過這兩個庫的開發者,直接入手AutoDispose,在我看來不如先入手RxLifecycle

二是AutoDispose的很大一部分設計核心源於RxLifecycle,理解了RxLifecycle,更有利於AutoDispose的學習。

接下來我將會嘗試向大家闡述AutoDispose的使用及其內部原理,和RxLifecycle不同,我們不再需要去繼承RxActivity/RxFragment,而是內部依賴使用了google不久前推出的架構組件 Lifecycle(事實上這也是我青睞AutoDispose的原因之一,儘量避免繼承可以給代碼更多選擇的餘地)。

2018-3-1追加更新

填坑後歸來:

Android架構中添加AutoDispose解決RxJava內存泄漏



作者:卻把清梅嗅
鏈接:https://www.jianshu.com/p/8311410de676
來源:簡書
簡書著作權歸作者所有,任何形式的轉載都請聯繫作者獲得授權並註明出處。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章