3

I'm trying to implement a timer Observable that is Shared between the Activities of my application. I am making the implementation on a class that is a Dagger singleton which I inject in each Presenter of each different Activity.

I create the Observable once, that way:

Observable.defer(() -> Observable.timer(milliseconds, TimeUnit.MILLISECONDS).map(t -> this::doSomethingCool()))
            .subscribeOn(Schedulers.newThread())
            .observeOn(AndroidSchedulers.mainThread())
            .share();

I make the subscribe from the Presenter with the function:

public Observable<Status> register(Callback callback) {

    PublishSubject<Status> subject = PublishSubject.create();
    subject.subscribe(status -> {},
            throwable -> L.LOGE(TAG, throwable.getMessage()),
            () -> callback.onStatusChanged(mBasketStatus));

    mObservable.subscribe(subject);
    basketCounterCallback.onStatusChanged(status));

    subject.doOnUnsubscribe(() -> L.LOGD(TAG, "Unsubcribed from subject!"));
    return subject.asObservable();
}

I store that Subject as Observable in every presenter, and I call to: obs.unsubscribeOn(AndroidSchedulers.mainThread()) to unsubscribe (in onPause() method). I also tried to unsubscribe using the Scheduler Schedulers.immediate()

But the callback is called anyway X times (where X is all the Presenters that I have subscribed to the timer), so it is not unsubscribing. Also the Log "Unsubcribed from subject!" is not getting called.

How can I unsubscribe correctly from every subject?

Thanks in advance

EDIT:

Added more implementation details due to the comments:

This is the part where I create the Observable and store in a member of the Singleton class StatusManager (the status is also a singleton):

private Observable<BasketStatus> mObservable;
private Status mStatus;

public Observable<BasketStatus> start(long milliseconds, Status status, Callback callback) {

    if (mObservable == null) mObservable = createObservable(milliseconds, status);

    return register(callback);
}

private Observable<BasketStatus> createObservable(long milliseconds, Status status) {

    mStatus = status;

    return Observable.defer(() -> Observable.timer(milliseconds, TimeUnit.MILLISECONDS).map(t -> status.upgradeStatus()))
            .subscribeOn(Schedulers.newThread())
            .observeOn(AndroidSchedulers.mainThread())
            .share();
}

public Observable<BasketStatus> register(Callback callback) {

    PublishSubject<Status> subject = PublishSubject.create();
    subject.subscribe(status -> {},
            throwable -> L.LOGE(TAG, throwable.getMessage()),
            () -> callback.onStatusChanged(mStatus));

    mObservable.subscribe(subject);
    callback.onStatusChanged(mStatus));

    subject.doOnUnsubscribe(() -> L.LOGD(TAG, "Unsubcribed from subject!"));
    return subject.asObservable();
}

After to call to the method start(...) from the Presenter that start the timer I call to register(...) method from the next Presenters:

class Presenter implements Callback {

    private Observable<BasketStatus> mRegister;

    @Inject
    public Presenter(Status status, StatusManager statusManager) {
        mRegister = statusManager.start(20000, status, this);
    }

    // Method called from onPause()
    public void unregisterFromBasketStatus() {
         mRegister.unsubscribeOn(Schedulers.immediate());
    }
}

And the next presenter...

@Inject
public NextPresenter(StatusManager statusManager) {
    mBasketStatusManager.register(this);
}
antonicg
  • 934
  • 10
  • 24
  • let's take a step back. What do you want to achieve ? – Blackbelt Feb 07 '17 at 08:50
  • I want to know when the timer is over and notify only the Activity that is showed in that moment and only if the application is opened. So I think that a Service is not a solution so I thought about this solution. – antonicg Feb 07 '17 at 09:02
  • is `mObservable` the timer ? – Blackbelt Feb 07 '17 at 09:05
  • Your code example is not sufficient .. What observable are you unsubscribing from? The one returned by `register` method or the timer observable stored in `mObservable` ? Also, do you actually call `.unsubscribe()` in onPause method ? It seems to me like you do not understand what `unsubscribeOn()` does .. It merely instructs to run the unsubscription task on a particular scheduler. It doesn't invoke it in any way .. I might be wrong about your understanding, that's why we need more code.. – koperko Feb 07 '17 at 09:05
  • @Blackbelt yes! – antonicg Feb 07 '17 at 09:34

1 Answers1

4

As I mentioned in the comment, you are not getting the behavior of unsubscribeOn operator. It doesn't unsubscribe from anything, rather it tells every subscriber, where it should do its work when the unsubscription happens. It wouldn't make sense either, because you are not unsubscribing from an Observable but from a Subscription which represents the connection between an observer and the stream itself.

Back to your problem. The way your code is now designed, subjects that you return are completely useless. You are subscribing to the timer observable in register method, but you forward those notifications to the subject which is never subscribed by anyone afterwards. If you really need them, in some part of the code that we don't see for example, you need to store the result of subscribe() method which is a Subscription object and call unsubscribe() on it when you need to ( in unregisterFromBasketStatus I presume).

But there are more issues in the code. For example :

subject.doOnUnsubscribe(() -> L.LOGD(TAG, "Unsubcribed from subject!"));
return subject.asObservable(); 

In the first line, you do not store the result of that operation anywhere. Since Observables are immutable structures, every operator creates a new stream which receives notifications from the previous one. From this, it is obvious that when you return subject.asObservable() in the second line, you are not returning modified observable from the first line, but the old unmodified one. To correct this, simply replace subject variable with the result: subject = subject.doOnUnsubscribe(() -> L.LOGD(TAG, "Unsubcribed from subject!"));

Second, one of the reasons for using streams is to replace callbacks altogether. Of course, it will work but you are mitigating a lot of benefits Rx brings to codebase. However, it is far less readable and anyone who will have to handle your code after you will curse you to hell more than enough that he might actually succeed :-)) I understand, that Rx is hard to learn from the beginning, but try to avoid this as much as possible.

koperko
  • 2,447
  • 13
  • 19
  • Thanks, that was a great answer! But I have a problem, I was using a `Subject` in order to not start again the timer every time a `Presenter` subscribes (`register()`) to the observable. If I use the subscription the timer starts again with every subscription and I don't want this behaviour. Now that I understand the subjects better (thanks :)), could I subscribe to the `Subject` from the Presenters? Besides that, I'm using a Callback in order to decouple the RxJava code from the Presenters, don't you think that this is a good way? – antonicg Feb 07 '17 at 13:53