All of you have used or heard of RxJava, which can cause memory leaks when observables and observers do not release their subscriptions in time. A typical scenario is to make a network request using RxJava, at which point the application is killed and the subscription is not released in time. Of course, this can be done manually in onDestroy. If the network request is not returned successfully, the application enters the background, and the UI should not be updated even if the request is returned successfully. How to handle this situation in the case of RxJava?

We have come across a situation where there is a solution that is available for RxLifecycle, which is officially defined as:

RxLifecycle

This library allows one to automatically complete sequences based on a second lifecycle stream.

This capability is useful in Android, where incomplete subscriptions can cause memory leaks.
Copy the code

In simple terms, it can be used to deal with memory leaks caused by RxJava. Today we’ll focus on several ways RxLifecycle can be used. After the opportunity to re-analyze the source code.

There are mainly the following ways of use:

1 bindToLifecycle

2 bindUntilEvent

3 LifecycleProvider

First you need to add a dependency to your module’s build.gradle:

The compile 'com. Trello. Rxlifecycle2: rxlifecycle - components: 2.1.0' compile 'com. Trello. Rxlifecycle2: rxlifecycle - navi: 2.1.0'Copy the code

1.bindToLifecycle

This method automatically unties Activity or Fragment based on its lifecycle and is very convenient to use. Avtivity inherits RxActivity and Fragment inherits RxFragment

public class MainActivity extends BaseActivity{ @Override protected void onStart() { super.onStart(); Observable.interval(1, TimeUnit.SECONDS) .subscribeOn(Schedulers.io()) .compose(this.<Long>bindToLifecycle()) .observeOn(AndroidSchedulers.mainThread()) .subscribe(new Consumer<Long>() { @Override public void accept(Long num) throws Exception { Timber.tag(TAG).d("onStart, running num : " + num); }}); }}Copy the code

It’s bound in onStart. If the Activity stops Observable when it enters the onStop life cycle, look at the log:





bindToLifecycle.PNG



bindToLifecycle
Activity
bindToLifecycle
onDestroy

/**
 * Lifecycle events that can be emitted by Activities.
 */
public enum ActivityEvent {

    CREATE,
    START,
    RESUME,
    PAUSE,
    STOP,
    DESTROY

}
Copy the code

Fragments also have life cycles that correspond symmetrically.

/**
 * Lifecycle events that can be emitted by Fragments.
 */
public enum FragmentEvent {

    ATTACH,
    CREATE,
    CREATE_VIEW,
    START,
    RESUME,
    PAUSE,
    STOP,
    DESTROY_VIEW,
    DESTROY,
    DETACH

}
Copy the code

2.bindUntilEvent

You can bind the onStart event in a button without having to uninstall the onStart event.

I added a button to the Activity above, click on the event in getData(View View), and look at the code:

public void getData(View view) {
    Observable observable = Observable.interval(1, TimeUnit.SECONDS).
            subscribeOn(Schedulers.io()).compose(this.bindUntilEvent(ActivityEvent.PAUSE));
    observable.observeOn(AndroidSchedulers.mainThread())
            .subscribe(new Consumer<Long>() {
                @Override
                public void accept(Long num) throws Exception {
                    Timber.tag(TAG).d("getData, running until num : " + num);
                }
            });
}
Copy the code

Observable stops sending data when an Activity enters onPause. Observable stops sending data when an Activity enters onPause. Observable stops sending data when an Activity enters onPause.







bindUntilEvent.PNG

Basically the above two ways are enough, the following way LifecycleProvider is more useful in MVP mode. Let’s move on.

3.LifecycleProvider

The way to use it is to first inherit NaviActivity and then add this sentence to the Activity

LifecycleProvider<ActivityEvent> provider = NaviLifecycle.createActivityLifecycleProvider(this);
Copy the code

This allows you to listen to the life cycle through the provider. I’m going to pass it in when I initialize presenter

@Override protected void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); . // Initialize Presenter = new Presenter(provider); }Copy the code

Add a button to the Activity that simulates a network request with a 3-second delay and updates the UI once the request is successful. We send life cycle events through the provider, and then we determine the event type in onNext, and if the Activity is already in one of onPause, onStop, onDestroy, we don’t update the UI anymore, and we disconnect through Disposable.

FactoryModel.getModel(Token.STRING_MODEL).params(params).execute(new CallBack<String>() {
            @Override
            public void onSuccess(final String data) {
                provider.lifecycle().subscribe(new Observer<ActivityEvent>() {
                    @Override
                    public void onSubscribe(Disposable d) {
                        disposable = d;
                    }

                    @Override
                    public void onNext(ActivityEvent activityEvent) {
                        Timber.tag("Presenter_TAG").i("received activityEvent, activityEvent = %s" , activityEvent.name());
                        if (null != disposable && disposable.isDisposed()){
                            Timber.tag("Presenter_TAG").i("disposable isDisposed");
                            return;
                        }
                        if (activityEvent == ActivityEvent.PAUSE || activityEvent == ActivityEvent.STOP || activityEvent == ActivityEvent.DESTROY){
                            Timber.tag("Presenter_TAG").e("do not refresh UI, activityEvent = %s", activityEvent.name());
                            onComplete();
                            return;
                        }
                        if (isViewAttached()) {
                            Timber.tag("Presenter_TAG").i("refresh UI, activityEvent = %s" , activityEvent.name());
                            view.showData(data);
                        }
                    }

                    @Override
                    public void onError(Throwable e) {

                    }

                    @Override
                    public void onComplete() {
                        if (null != disposable && !disposable.isDisposed()){
                            disposable.dispose();
                            Timber.tag("Presenter_TAG").d("LifecycleProvider disposed");
                        }
                    }
                });
            }
        });
Copy the code

Look at the normal request success log, as expected, the UI update is normal.







PNG

After the network request is made, press the home button to cut the APP to the background, so that the Activity enters the onStop lifecycle. If the network request is successful, the UI should be updated again.





Damage. PNG



provider

if (activityEvent == ActivityEvent.PAUSE || activityEvent == ActivityEvent.STOP || activityEvent == ActivityEvent.DESTROY){
     Timber.tag("Presenter_TAG").e("do not refresh UI, activityEvent = %s", activityEvent.name());
     onComplete();
     return;
}
Copy the code

In fact, it can be, check out the Demo log:





Once the subscription is cut, listen again for the life cycle. PNG

So what’s going on? It’s actually in the following code:

provider.lifecycle()
Copy the code

When you get an Observable, you know what the original features are. Look at the source code:

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

@Override
@NonNull
@CheckResult
public Observable<ActivityEvent> lifecycle() {
    return lifecycleSubject.hide();
}

Copy the code

The BehaviorSubject annotation Hides the identity of this Observable and its Disposable. This returns a new Observable:

/**
     * Hides the identity of this Observable and its Disposable.
     * <p>Allows hiding extra features such as {@link io.reactivex.subjects.Subject}'s
     * {@link Observer} methods or preventing certain identity-based
     * optimizations (fusion).
     * <dl>
     *  <dt><b>Scheduler:</b></dt>
     *  <dd>{@code hide} does not operate by default on a particular {@link Scheduler}.</dd>
     * </dl>
     * @return the new Observable instance
     *
     * @since 2.0
     */
    @CheckReturnValue
    @SchedulerSupport(SchedulerSupport.NONE)
    public final Observable<T> hide() {
        return RxJavaPlugins.onAssembly(new ObservableHide<T>(this));
    }
Copy the code

4. To summarize

RxLifecycle is relatively less intrusive and can be monitored for life cycles with little to no change to the original code, providing another solution to prevent RxJava subscription memory leaks, which is nice.

That’s it for today and I’ll have an opportunity to share the source code for RxLifecycle.

I am also busy with my work. Writing a blog really requires endurance. If it is helpful to you, please pay attention to it and give a thumb-up.

Finally, thank @rightward for your understanding and support.

The above!