1. 程式人生 > >解決RxJava記憶體洩漏(前篇):RxLifecycle詳解及原理分析

解決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'
  • 1
  • 2
  • 3

2、配置Activity/Fragment容器

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

RxLifecycle支援的Component

程式碼如下:

//只需要繼承即可
public class MainActivity extends RxAppCompatActivity {
      ...
      ...
}
  • 1
  • 2
  • 3
  • 4
  • 5

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);
                }
            });
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24

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);
                }
            });
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21

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

原理分析

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

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

Observable.compose(this.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();
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62

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

這個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為例
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19

熟悉Transformer的同學都應該能看懂,LifecycleTransformer實際是實現了不同響應式資料類(Observable、Flowable等)的Transformer介面。

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

takeUntil操作符

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

takeUntil操作符

回顧

 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);
                }
            });
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

現在回頭來看這段程式碼,就很好理解了,Activity.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);
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

不難猜測,實際上,那個神祕人,就是我們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));
    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
    //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);
    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15

最後我們走到了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");
                }
            }
        };
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22

也就是說,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;
        }
    };
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18

bind生成LifecycleTransformer

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

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

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

總結

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

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

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

轉折:AutoDispose

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

我花了一些時間研究了一下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 
AutoDispose are inspired by RxLifecycle.

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

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

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

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

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

2018-3-1追加更新

填坑後歸來: