38

If an observable completes, do I still have to unsubscribe / dispose (in RxJava2) the observable to remove the Observer (prevent memory leaks) or is this handled internally by RxJava once a onComplete or onError event occurs?

what about other types like Single, Completable, Flowable etc.

sockeqwe
  • 15,574
  • 24
  • 88
  • 144
  • 1
    Possible duplicate of [Do we need to unsubscribe from observable that completes/errors-out?](https://stackoverflow.com/questions/41334931/do-we-need-to-unsubscribe-from-observable-that-completes-errors-out) – Mouneer Aug 07 '18 at 19:10

2 Answers2

39

Yes you are correct.

After a stream is terminated ( onComplete / onError has been called ), subscriber unsubscribes automatically. You should be able to test these behaviors using isUnsubscribed() method on the Subscription object.

koperko
  • 2,447
  • 13
  • 19
16

While you do not need to manually unsubscribe from a terminated stream, you can still create a memory leak using RxJava2 if you are not careful.

Consider the following code:

repository.getData()
    .subscribeOn(Schedulers.io())
    .observeOn(AndroidSchedulers.mainThread())
    .subscribe(data -> myTextView.setText(data.toString()));

The lambda parameter in the subscribe is "syntatic sugar" over an anonymous inner class:

subscribe(new Consumer<Data>() {
    @Override
    public void accept(final Data data) {
        myTextView.setText(data.toString());
    }
});

On the JVM, an anonymous inner class maintains a reference to the outer class.

Assume that for the above naive code, the outer class is an Activity (this would also follow for a Fragment, Service, BroadcastReceiver or any class whose lifecycle is controlled by the Android OS).

The Activity subscribes to the Observer but then is destroyed by the Android OS in conditions of low-memory (you can mimic this effect by turning on Developer Options/Don't Keep Activities). If the work on Schedulers.io() is still running when the Activity is destroyed, a reference will still be maintained to the Activity through the anonymous inner class. This means a memory leak that prevents the Activity from being finalized by the garbage collector. If the Activity has a number of Views or, say, a Bitmap object then the memory leak can be quite substantial.

There are a number of solutions here but one of them is to maintain a CompositeDisposable object and to clear this in the onDestroy() lifecycle method of the Android Activity:

public class MyActivity extends Activity {

   DataRepository dataRepository;
   CompositeDisposable disposables;

   @Override
   public void onCreate(Bundle savedInstanceState) {
       super.onCreate(savedInstanceState);
       disposables = new CompositeDisposable();
   }

   public void onButtonClick(View v) {
       repository.getData()             
          .subscribeOn(Schedulers.io())
          .observeOn(AndroidSchedulers.mainThread())
          .doOnSubscribe(disposable -> disposables.add(disposable))
          .subscribe(data -> myTextView.setText(data.toString()));
   }

   @Override
   public void onDestroy() {
       disposables.clear();
       super.onDestroy();
   }
}

You can refer to a good example of how to use RxJava in an Android app in the official Google Android Architecture Blueprints.

David Rawson
  • 20,912
  • 7
  • 88
  • 124
  • 3
    In your example of the leaked activity, it would still be finalizable once I/O is compete, right? So it's more a case of delayed finalize than a permanent leak. – Dabbler May 04 '18 at 19:21
  • 2
    @Dabbler I think so. But these delayed finalizations seem to get called "memory leaks" a lot in Android-land – David Rawson May 04 '18 at 20:24
  • that of course is an issue and is a classic example of possible memory leak .. however, that is not the concern of question from OP, where he clearly wants to know what happens if the subscription terminates before its owner is being finalised.. And in that case, no leak occurs as activity/fragment/service/etc. is no longer tied to the observer object – koperko Aug 14 '19 at 14:27