Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Using LifecycleObservable #127

Closed
david-hoze opened this issue Jan 28, 2015 · 16 comments
Closed

Using LifecycleObservable #127

david-hoze opened this issue Jan 28, 2015 · 16 comments

Comments

@david-hoze
Copy link

Hi, it seems that using LifecycleObservable and binding it to an activity or fragment, obviates the need for AppObservable, am I correct, or do I need to use both in conjunction?
My guess is that LifecycleObservable includes the features of AppObservable, but since it's a part of rxandroid-framework and not the core, AppObservable is not deprecated or something like that.

Also, can I use LifecycleObservable.bindActivityLifecycle whenever I want? (i.e. after different observable operators etc.)

@david-hoze david-hoze changed the title Using LifecycleObservable over AppObservable Using LifecycleObservable Jan 28, 2015
@hamidp
Copy link
Contributor

hamidp commented Jan 28, 2015

Consider AppObservable deprecated and use LifecycleObservable.bindActivityLifecycle

There is also a bindFragmentLifecycle

It's part of the framework while we figure out where it should go. Framework is more experimental things.

@dlew
Copy link
Collaborator

dlew commented Jan 28, 2015

AppObservable solves a separate problem which is currently unsolved. If you use AndroidSchedulers.mainThread(), it is still possible for items to emit even after you've unsubscribed.

The two sort of work in tandem and perhaps should be combined:

  1. AppObservable prevents items from being emitted past the proper point in the lifecycle, but doesn't auto-unsubscribe unless an item is emitted.
  2. LifecycleObservable automatically unsubscribes from Subscriptions at the right time, but doesn't prevent an item from being emitted past that point (due to the issue above).

Ideally we'd fix the underlying problem that AppObservable solves, but no one has figured it out yet.

See more info here: #95 (comment)

@david-hoze
Copy link
Author

@hamidp, it seems that @dlew's answer is more accurate.
@dlew, what about where I can call the LifecycleObservable.bindActivityLifecycle and AppObservable.bindActivity, should I call them right after the Observable is created, right before the subscription, does it matter at all?

Thanks, Amitai

@hamidp
Copy link
Contributor

hamidp commented Jan 28, 2015

@amitai-hoze Yep, listen to @dlew as he wrote LifecycleObservable

You should bind the observable itself upon creation.

@david-hoze
Copy link
Author

@ersin-ertan, You also need to add the rxandroid-framework dependency, like so:

compile 'io.reactivex:rxandroid-framework:0.24.0'

@david-hoze
Copy link
Author

@dlew, the documentation of ViewObservable::bindView says:
Unlike rx.android.app.AppObservable::bindActivity or rx.android.app.AppObservable::bindFragment, you don't have to unsubscribe the returned Observable on the detachment. bindView does it automatically.

Does this mean I don't have to use LifeCycleObservable to bind it to the lifecycle?
Also, does the following code look right?

public class SomeView extends LinearLayout {
    private Button someButton;

    @Override
    protected void onFinishInflate() {
        super.onFinishInflate();
        someButton = (Button) findViewById(R.id.some_button);
        Observable<OnClickEvent> someButtonClicks =
                ViewObservable.bindView(this,ViewObservable.clicks(sendPhoneButton));

        someButtonClicks.subscribe(onClickEvent -> {/*do something*/});
    }
}

@david-hoze
Copy link
Author

@dlew, a few more questions.
I figured as AppObservable stops the observable from emitting items, it should be bound to the activity at the creation of the source observable so that the emission could stop at the source.
Also, as LifecycleObservable only unsubscribes, it can be bound to the lifecycle at any point in an observable chain, as the unsubscription propagates through out all the observable chain.
Am I right?

Also, you said that AppObservable solves the problem of stopping the emission of events, which LifecycleObservable doesn't solve, what kind of observable continues emitting items after it is unsubscribed from? Is it some asynchronous network request, or an observable bound to UI, when does this sort of thing happen?

@david-hoze
Copy link
Author

OK, I think I figured it out.

Where in an observable chain to call the bind methods
An observable has to use AppObservable::bindActivity or AppObservable::bindFragment or ViewObservable::bindView before it changes a state which is no longer valid to change when the activity/fragment/view is destroyed. Of course, if the methods are invoked upon creation of the root source observable it will certainly prevent an invalid state change but they don't have to be.
An observable has to use LifecycleObservable::bindActivityLifecycle at the end of each observable chain call so that all of the observables on the chain unsubscribe at the desired lifecycle event.

Which kind of observable needs which binding method
Let's divide our observables into two types

UI events Observables

Those don't need to use AppObservable::bindActivity or AppObservable::bindFragment or ViewObservable::bindView, as they stop emitting items when the activity stops or the view is detached. However, they do need to be unsubscribed from (I think they retain the context and may leak it otherwise). So they should use LifecycleObservable::bindActivityLifecycle.

Background events Observables

e.g. a network request, a broadcast receiver etc. These should use AppObservable::bindActivity/AppObservable::bindFragment/ViewObservable::bindView, so they don't emit items and change the state of the activity, view etc. when it is no longer valid. They should also use LifecycleObservable::bindActivityLifecycle, to unsubscribe and dispose of themselves correctly, since the other methods only unsubscribe when the observable emits an item, which is not guaranteed to happen.

@dlew
Copy link
Collaborator

dlew commented Feb 9, 2015

Sorry for the delayed response, been out of town. Taking things in order...

bindView - It should auto-unsubscribe correctly.

LifecycleObservable location - If you're dealing with a single Subscription then placement doesn't really matter, but if you've got multiple people subscribing to the same Observable then you might have to take into account where to stick LifecycleObservable (depending on whether you want every subscriber to auto-unsubscribe, or only some of them).

"what kind of observable continues emitting items after it is unsubscribed from" - It's an unintended side effect of the AndroidSchedulers.mainThread() implementation. It is possible for an Observable to emit an item, then unsubscribe, but the emitted item continues to propagate past the call to unsubscribe. It's not desirable behavior but barring a better solution it's what we've got at the moment. :(

@david-hoze
Copy link
Author

Thanks.
A few questions:

  1. Why does ViewObservable send onComplete and LifecycleObservable doesn't?
  2. It seems that ViewObservable::bindView does the work of LifecycleObservable::bindActivityLifecycle and AppObservable::bindActivity together (with regards to a view and not an activity of course), meaning it also auto-unsubscribes and stops the emission of items after the view is detached. Why not implement something similar for activity and fragment?

@dlew
Copy link
Collaborator

dlew commented Feb 10, 2015

  1. I don't think that ViewObservable should send onComplete. We debated over whether LifecycleObservable should do so and decided it should not. I wasn't part of the development for ViewObservable though. Might be worth a PR.
  2. Looking at it more closely, I'm not convinced that ViewObservable::bindView actually negates the need for AppObservable::bindActivity. It only checks for unsubscribe, which again can be deceptive based on the user of Handler schedulers.

Also, Views have addOnAttachStateChangeListener which means that we can monitor the state from outside of it. Fragments and Activities do not offer any such capability so we had to subclass.

@david-hoze
Copy link
Author

OK, thanks a lot.
I'm trying to synthesize an observable that keeps emitting items after it's unsubscribed from for testing (subscribing and observing on AndroidSchedulers.mainThread() as the problem lies there) and can't succeed. Can you give me some code that does it?

@dlew
Copy link
Collaborator

dlew commented Feb 11, 2015

I've never had it happen myself, only heard of it happening from @mttkay.

@mttkay
Copy link
Collaborator

mttkay commented Feb 11, 2015

I believe we ran into this with OperatorCache, because these operators
break the link to the downstream subscription. I'm not totally sure, it
seemed to have happened only on a particular screen and I wasn't even able
to reproduce it on my dev machine. I've meanwhile filed it under sucks, but
life goes on. (It was one of the more complicated screens we have so some
margin for error there I thought was acceptable.)
On Feb 11, 2015 2:14 AM, "Daniel Lew" [email protected] wrote:

I've never had it happen myself, only heard of it happening from @mttkay
https://github.com/mttkay.


Reply to this email directly or view it on GitHub
#127 (comment).

@smallufo
Copy link

Execute me . Is there any sample MVVM-like project regarding to this discussion ? There's another sample project rx-android-architecture by tehmou , not using LifecycleObservable / AppObservable or any ViewObservable.bindView , instead he uses WeakReference to solve the problem. Is there any drawback regarding to that solution ? ( I mean the MVVM design )

Thanks.

@JakeWharton
Copy link
Contributor

LifecycleObservable has been removed for future releases as part of #172. While it might come back in some form, you're much better off either emitting specific events for the lifecycle or explicitly unsubscribing in the callback.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

6 participants