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

OperatorWeakBinding (deprecates OperatorObserveFromAndroidComponent) #938

Merged
merged 7 commits into from
Mar 13, 2014
Merged

OperatorWeakBinding (deprecates OperatorObserveFromAndroidComponent) #938

merged 7 commits into from
Mar 13, 2014

Conversation

mttkay
Copy link
Contributor

@mttkay mttkay commented Mar 2, 2014

Android UI operator that weakly binds to a fragment or activity. (see discussion in #899)


private void handleLostBinding(Subscriber<? super T> sub, String context) {
if (sub == null) {
Log.d(LOG_TAG, "subscriber gone; skipping " + context);
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should guard this log call

@cloudbees-pull-request-builder

RxJava-pull-requests #877 FAILURE
Looks like there's a problem with this pull request

private final WeakReference<Subscriber<? super T>> subscriberRef;
private final WeakReference<R> boundRef;

private WeakSubscriber(Subscriber<? super T> op, WeakReference<R> boundRef) {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should this be super(op)?

@@ -95,4 +101,21 @@ private AndroidObservable() {}
throw new IllegalArgumentException("Target fragment is neither a native nor support library Fragment");
}
}

public static <T> Observable<T> bindActivity(Activity activity, Observable<T> cachedSequence) {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we don't actually have to assume that the sequence is cached. A PublishSubject doesn't cache but still makes sense to weakly bind to when not calling unsubscribe explicitly

@samueltardieu
Copy link
Contributor

Couldn't you, in addition, check Activity#isFinishing to prevent values from being delivered to a dying (or already dead) activity?

@mttkay
Copy link
Contributor Author

mttkay commented Mar 6, 2014

That should not be necessary, and it is something that we only realized
after writing the very first version of the current operator.

Android guarantees that it will not process messages entering the main
looper while the current Activity is in the process of being recreated,
that is, between calls to onDestroy of instance A and onCreate of instance
B of the same class.

So as long as you use OperatorWeakBinding with the main thread scheduler,
any Rx notifications will be queued up until after onCreate should they
arrive while the Activity is gone.

bindActivity in AndroidObservable combines the two to achieve this.

Does that make sense?

@samueltardieu
Copy link
Contributor

I am more concerned about what happens when the activity is gone (replaced by another one) but not yet reclaimed by the GC. Won't items be delivered to the UI thread which is now unrelated to the previous activity?

@mttkay
Copy link
Contributor Author

mttkay commented Mar 7, 2014

No that's what I tried to say: Android stops processing the main looper
during a configuration change. Since HandlerThreadScheduler posts
notifications to a looper, they will queue up until your activity exists
again.

@samueltardieu
Copy link
Contributor

We obviously don't understand each other :) I'll try again.

I'm not talking about the case where the activity is recreated, but where it is destroyed and another unrelated activity is started. The same thread can be reused for the UI of the new activity, and AFAIK (but I may be wrong) the looper is tied to a thread, not to the activity.

I understand that the looper will be suspended between the previous activity death and the new activity creation, but won't queued messages then be delivered to the new activity which would use the same looper as the previous one?

I have had cases where messages were delivered through a Handler to a destroyed activity, which manifested through an error while attempting to do UI operations on now-not-displayed components, and I wonder whether this could be the case here.

@mttkay
Copy link
Contributor Author

mttkay commented Mar 7, 2014

Not if you unsubscribe from the Activity/subscriber in onDestroy or let the
weak binding do that for you. Am I missing something?
On Mar 7, 2014 9:42 AM, "Samuel Tardieu" notifications@github.com wrote:

We obviously don't understand each other :) I'll try again.

I'm not talking about the case where the activity is recreated, but where
it is destroyed and another unrelated activity is started. The same
thread can be reused for the UI of the new activity, and AFAIK (but I
may be wrong) the looper is tied to a thread, not to the activity.

I understand that the looper will be suspended between the previous
activity death and the new activity creation, but won't queued messages
then be delivered to the new activity which would use the same looper as
the previous one?

I have had cases where messages were delivered through a Handler to a
destroyed activity, which manifested through an error while attempting to
do UI operations on now-not-displayed components, and I wonder whether this
could be the case here.

Reply to this email directly or view it on GitHubhttps://github.com//pull/938#issuecomment-36977549
.

@samueltardieu
Copy link
Contributor

The weak binding will unsubscribe when the activity is freed by the garbage collector. This may happen a long time after the activity is destroyed. This is not a synchronous process as it would be in Python where references are reference-counted. Here, we have to wait until the garbage collector decides to collect unreferenced objects for the weak reference to become null.

(of course, when unsubscribing explicitly, there is no problem :-)

@mttkay
Copy link
Contributor Author

mttkay commented Mar 7, 2014

FWIW, I should add that the implementation you're now using does not check
for isFinishing and I wasn't able to reproduce this scenario in the tests I
ran with OperatorWeakBinding. I will give it another closer look though.

Moreover, if we find we do need this check after all, I suggest we follow
the advice from the first issue linked, which is to pass a predicate to the
operator that can perform such checks, or alternatively, call filter on the
result of the weak binding.

@mttkay
Copy link
Contributor Author

mttkay commented Mar 12, 2014

@samueltardieu Sorry for the delay, finally got time to work on this. You were right, there is a case in which this can still happen, which is when exiting the activity via the back button. I still couldn't reproduce it when going through a rotation change, but that case is sufficient to warrant a change or extension to this.

My suggestion is then to extend this operator to also address what @tehmou asked for, which is to be able to pass in a predicate that tests for validity of the bound component. This could be isFinishing for an activity, or isAdded for a fragment. I would like to keep the operator itself to remain fairly generic, where it would perform the following things:

  • weakly bind a reference of R
  • weakly bind the subscriber
  • accept a predicate in the constructor that performs tests on the R instance for every notification

this test can then be customized, but the bindActivity and bindFragment helpers could still pass a default predicate implementing above mentioned checks.

Does that make sense? I'll implement this, run some tests and then push it for you guys to reviews.

@cloudbees-pull-request-builder

RxJava-pull-requests #898 FAILURE
Looks like there's a problem with this pull request

@cloudbees-pull-request-builder

RxJava-pull-requests #899 SUCCESS
This pull request looks good

final WeakReference<Subscriber<? super T>> subscriberRef;

private WeakSubscriber(Subscriber<? super T> source) {
subscriberRef = new WeakReference<Subscriber<? super T>>(source);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think you need to add super(source); here to chain the Subscription.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good catch!

@cloudbees-pull-request-builder

RxJava-pull-requests #900 SUCCESS
This pull request looks good

@cloudbees-pull-request-builder

RxJava-pull-requests #901 SUCCESS
This pull request looks good

benjchristensen added a commit that referenced this pull request Mar 13, 2014
OperatorWeakBinding (deprecates OperatorObserveFromAndroidComponent)
@benjchristensen benjchristensen merged commit 90d5978 into ReactiveX:master Mar 13, 2014
@mttkay mttkay deleted the operator-weak-binding branch March 13, 2014 21:12
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

Successfully merging this pull request may close these issues.

None yet

5 participants