-
Notifications
You must be signed in to change notification settings - Fork 226
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
Incorporate feedback from #130 #138
Conversation
Adds a convenience assertion and uses peek instead of poll for hasError
These were missing before and necessary to mark this observer as appropriately disposed after, though without calling dispose()
Saving dex methods
I'm not 100% I've done this right, but leveraging some existing examples in RxJava as my guide. I've copied in the relevant internal rxjava APIs (since they're internal) and also adjusted the onNext methods to return true or false indicating whether or not they terminated the underlying observer. Will seek further feedback during the PR. Main thing I'm not sure of is if it does terminate, do we need to dispose the lifecycle or does this mean that onError or onComplete have been hit in the main observer already (and doing so is a double dispose condition)
I actually missed the link in your original comment. Going to research more with that and report back! |
Ok I think I've got deferred requesting set up in 500f337 after reading the docs. If that looks good, I'll bring those methods into the |
Ok, I'm missing something about how to wire up deferred requesting. Let me know if you have any advice! |
Here is the wiki entry for deferred requesting.
Does the lifecycle's PublishSubject.create()
.to(AutoDispose.with(ScopeProvider.UNBOUND).forObservable())
.subscribe(new DisposableObserver<Object>() {
PrintWriter fout;
@Override public void onStart() {
fout = new PrintWriter(new FileWriter("output.txt"));
}
@Override public void onNext(Object o) {
fout.println(o);
}
@Override public void onError(Throwable t) {
t.printStackTrace(fout);
fout.close();
}
@Override public void onComplete() {
fout.close();
}
}); If the lifecycle ends, |
Yep that's correct behavior. Something may be considered "unbound" in the sense that they won't be disposed by the lifecycle ending. In the example case, it would be as if it was running without autodisposing. The intention for For the deferred requesting, I tried implementing pretty much like the
Does anything stand out to you as off in my implementation? |
Could you point me to the implementation that is targeted by that test? |
You have to call |
That unfortunately causes several other tests to fail with a double subscription error:
Specifically -
|
Also - calling delegate.onSubscribe() before the lifecycle kind of gets to the other remaining concern I had in the description:
Would be curious to know your thoughts |
You don't need to call |
D'oh, I totally missed that. Fixed in e7e1b87. With that, I think I've covered the points from your issue if you want to do a full review? |
if (!isMainThread()) { | ||
observer.onError( | ||
new IllegalStateException("Lifecycles can only be bound to on the main thread!")); | ||
return; | ||
} | ||
ArchLifecycleObserver archObserver = | ||
new ArchLifecycleObserver(lifecycle, observer, eventsObservable); | ||
observer.onSubscribe(archObserver); | ||
lifecycle.addObserver(archObserver); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If the Observer
disposes immediately, this will still add the listener and keep a reference to it. You should check for isDisposed
after this add and call removeObserver
:
lifecycle.addObserver(archObserver);
if (archObserver.isDisposed()) {
lifecycle.removeObserver();
}
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@@ -46,8 +48,6 @@ | |||
// Emit the last event, like a behavior subject | |||
observer.onNext(ViewLifecycleEvent.ATTACH); | |||
} | |||
Listener listener = new Listener(view, observer); | |||
observer.onSubscribe(listener); | |||
view.addOnAttachStateChangeListener(listener); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Here too, if disposed, the listener will remain added.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@@ -166,7 +166,7 @@ | |||
return new LifecycleScopeProviderHandlerImpl(scope); | |||
} | |||
|
|||
private static class MaybeScopeHandlerImpl implements ScopeHandler { | |||
private static final class MaybeScopeHandlerImpl implements ScopeHandler { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'd avoid private
in case you don't want to get accessor methods on Android.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good catch. Fixed in 959fe09 but planning to remove this class in the near future anyway now that as()
is in RxJava :)
} | ||
}; | ||
if (AutoDisposeEndConsumerHelper.setOnce(lifecycleDisposable, o, getClass())) { | ||
lifecycle.subscribe(o); | ||
if (AutoDisposeEndConsumerHelper.setOnce(mainDisposable, d, getClass())) { | ||
delegate.onSubscribe(this); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
callMainSubscribeIfNecessary
may be a problem here too causing double onSubscribe
s.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hmm, the only events that could cause this though would be if the lifecycle terminated, in which case mainDisposable
would either be dispose()
'd or be lazySet to DISPOSED
(and thus avoid a double subscription error from being thrown) right? Or do you mean there's race condition potential if the lifecycle terminates in the middle of this code execution?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Unless you can confine this entire onSubscribe
to the main thread, it is possible this gets executed asynchronously while the main thread ends the lifecycle.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actually I think I see what you mean. I can call delegate.onSubscribe early rather than try to be defensive against lifecycle ending immediately (which is what I keep being worried about, as it bit me in the past). We're already subscribed at this point so it's not like calling onSubscribe on the delegate early is going to change anything
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done in ba8bd87
delegate.onComplete(); | ||
} | ||
} | ||
|
||
@Override public void onError(Throwable e) { | ||
if (!isDisposed()) { | ||
lazyDispose(); | ||
AutoDisposableHelper.dispose(lifecycleDisposable); | ||
mainDisposable.lazySet(AutoDisposableHelper.DISPOSED); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Swap these so the mainDisposable
change gets flushed with the dispose()
call.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As in just swap the order they're executed?
mainDisposable.lazySet(AutoDisposableHelper.DISPOSED);
AutoDisposableHelper.dispose(lifecycleDisposable);
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Assuming so after googling a bit - part of 9c3edf4
} | ||
}; | ||
if (AutoDisposeEndConsumerHelper.setOnce(lifecycleDisposable, o, getClass())) { | ||
lifecycle.subscribe(o); | ||
if (AutoDisposeEndConsumerHelper.setOnce(mainDisposable, d, getClass())) { | ||
delegate.onSubscribe(this); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
callMainSubscribeIfNecessary
may cause double onSubscribe
here.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
part of ba8bd87
@@ -95,21 +91,24 @@ void callMainSubscribeIfNecessary(Disposable d) { | |||
|
|||
@Override public void onSuccess(T value) { | |||
if (!isDisposed()) { | |||
lazyDispose(); | |||
AutoDisposableHelper.dispose(lifecycleDisposable); | |||
mainDisposable.lazySet(AutoDisposableHelper.DISPOSED); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Swap these.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
part of 9c3edf4
} | ||
}; | ||
if (AutoDisposeEndConsumerHelper.setOnce(lifecycleDisposable, o, getClass())) { | ||
lifecycle.subscribe(o); | ||
if (AutoDisposeEndConsumerHelper.setOnce(mainDisposable, d, getClass())) { | ||
delegate.onSubscribe(this); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Double onSubscribe
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
part of ba8bd87
} | ||
}; | ||
if (AutoDisposeEndConsumerHelper.setOnce(lifecycleDisposable, o, getClass())) { | ||
lifecycle.subscribe(o); | ||
if (AutoDisposeEndConsumerHelper.setOnce(mainDisposable, d, getClass())) { | ||
delegate.onSubscribe(this); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Double onSubscribe
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
part of ba8bd87
We were disposing the lifecycle a second time after, and unnecessarily disposing the main disposable twice too
I think I've addressed the existing comments and a little extra. Thanks again for taking the time to review this, I'm learning a lot! |
@akarnokd let me know if there's anything else you see, otherwise I'll try to land this monday or tuesday |
No further comments. 👍 |
Awesome. Thank you so much for the review! |
This PR incorporates feedback from #130, but it not currently complete yet. CC @akarnokd whenever you have time to review (no rush!)
Completed
Mentioned in the issue, opted to keep this to match semantics of RxJava's plugins. We also operate on a large monorepo structure internally where this actually becomes useful to help catch downstream usages.
Touched on in the issue, and after thinking on it I think I'm going to keep this as is until there's a new minor release or new API we want to target. Keeping the lower API makes it easier for consumers to adopt without imposing newer versions if they haven't been able to update yet.
bd22bc2
caf904a
79e6dbf
94212eb
9bf1993
cb432a8. I think I've mostly got this. Main thing I'm not sure of is if it does terminate, do we need to dispose the lifecycle or does this mean that onError or onComplete have been hit in the main observer already (and doing so is a double dispose condition)
07f0f1e and corresponding one for
LifecycleEventsObservable
ec190c0I think this is covered as a part of 07f0f1e, but let me know if I've missed something
b38d03f
TODO
If I subscribe first - couldn't it synchronously execute its entire sequence before the lifecycle gets a chance then? Part of ths reason it's done ater the lifecycle check is such that the lifecycle can indicate that it's ended, and just immediately dispose after onSubscribe().
Could you expand on this one a bit? I'm not sure I totally understood
See comment below