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? |
| ArchLifecycleObserver archObserver = | ||
| new ArchLifecycleObserver(lifecycle, observer, eventsObservable); | ||
| observer.onSubscribe(archObserver); | ||
| lifecycle.addObserver(archObserver); |
There was a problem hiding this comment.
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();
}| } | ||
| Listener listener = new Listener(view, observer); | ||
| observer.onSubscribe(listener); | ||
| view.addOnAttachStateChangeListener(listener); |
There was a problem hiding this comment.
Here too, if disposed, the listener will remain added.
| } | ||
|
|
||
| private static class MaybeScopeHandlerImpl implements ScopeHandler { | ||
| private static final class MaybeScopeHandlerImpl implements ScopeHandler { |
There was a problem hiding this comment.
I'd avoid private in case you don't want to get accessor methods on Android.
There was a problem hiding this comment.
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.
callMainSubscribeIfNecessary may be a problem here too causing double onSubscribes.
There was a problem hiding this comment.
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.
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.
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
| if (!isDisposed()) { | ||
| lazyDispose(); | ||
| AutoDisposableHelper.dispose(lifecycleDisposable); | ||
| mainDisposable.lazySet(AutoDisposableHelper.DISPOSED); |
There was a problem hiding this comment.
Swap these so the mainDisposable change gets flushed with the dispose() call.
There was a problem hiding this comment.
As in just swap the order they're executed?
mainDisposable.lazySet(AutoDisposableHelper.DISPOSED);
AutoDisposableHelper.dispose(lifecycleDisposable);There was a problem hiding this comment.
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.
callMainSubscribeIfNecessary may cause double onSubscribe here.
| if (!isDisposed()) { | ||
| lazyDispose(); | ||
| AutoDisposableHelper.dispose(lifecycleDisposable); | ||
| mainDisposable.lazySet(AutoDisposableHelper.DISPOSED); |
| if (AutoDisposeEndConsumerHelper.setOnce(lifecycleDisposable, o, getClass())) { | ||
| lifecycle.subscribe(o); | ||
| if (AutoDisposeEndConsumerHelper.setOnce(mainDisposable, d, getClass())) { | ||
| delegate.onSubscribe(this); |
| if (AutoDisposeEndConsumerHelper.setOnce(lifecycleDisposable, o, getClass())) { | ||
| lifecycle.subscribe(o); | ||
| if (AutoDisposeEndConsumerHelper.setOnce(mainDisposable, d, getClass())) { | ||
| delegate.onSubscribe(this); |
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
LifecycleEventsObservableec190c0I 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