uber / autodispose Goto Github PK
View Code? Open in Web Editor NEWAutomatic binding+disposal of RxJava streams.
Home Page: https://uber.github.io/AutoDispose/
License: Apache License 2.0
Automatic binding+disposal of RxJava streams.
Home Page: https://uber.github.io/AutoDispose/
License: Apache License 2.0
To fill in to our CONTRIBUTING.md
.
Specifically, some might want to just dispose quietly (in production, or always) rather than error. Should we make this more configurable? If so, how/to what degree? Moving this discussion here from an internal one.
Currently - LifecycleEndException
is propagated to the delegate observer's onError
. I see two options:
A. If the delegate doesn't implement onError
, it goes to RxJavaPlugins
and one could filter that in a plugin hook. This would not cover cases where onError
is implemented though. Is this fine?
B. We make this more explicitly configurable
RxJavaPlugins
-esque plugin system?ScopeProvider
implementation using ScopeUtils
?One major benefit to this approach is that while on RxJava 1 we (internally) threw hard exceptions on lifecycle boundary issues, I think we can actually safely swallow them now since disposal means no terminal events will be emitted.
Before, we couldn't really safely swallow these for two reasons:
Observable
contractAt the end of the day, this is still (probably) programmer error that we want to catch. Configurability should allow for enabling these checks in, say, debug builds.
There is around 27 synthetic accessors method (most of which are in tests). RecordingObserver, AutoDisposingSubscriberImpl (and similar classes) has around 12 methods. Maybe convert them to package protected to reduce dex count by 12.
autodispose-kotlin
autodispose-android-kotlin
Things to consider:
Add DialogScopeProvider module
public enum DialogLifecycleEvent {
SHOW, CANCLE, DISMISS
}
or just
public enum DialogLifecycleEvent {
SHOW, DISMISS
}
Using withScope()
currently is feeling weird, especially when some scope providers are just other classes (e.g. views) . Wanted to put up a couple others for thought:
scopeWith
scope
scopeTo
@uber/mobile-platform-android what do you think?
Do we want to include module specific READMEs?
Per internal discussion, for explicitness we are opting to go with something like this:
subscribe(AutoDispose.maybe().scopeTo(provider).around(...))
Also LifecycleProvider
-> ScopeProvider
Possible extra exploration: Only expose interfaces for the intermediaries with private implementations.
AutoDipose 0.3.0 requires something like this in my project (fwiw, 0.2.0 didn't need anything):
-dontwarn com.uber.javaxextras.FieldsMethodsAndParametersAreNonNullByDefault
So consumers could get access to changes like this ReactiveX/RxJava#5590
Ignored in #57
All we get is Instrumentation run failed due to 'Process crashed.'
. It crashes some time after onError
is forward to the delegate, but I can't get the debugger to work to properly investigate.
Glide 4.0 has some really cool ideas around allowing consumers to code gen their own APIs into the first party API. Iโve been thinking - what if we tried to do something like this in AutoDispose to allow developers to promote their own first party APIs while making the core APIs available for use (basically Maybe + ScopeProvider). This is a bit more left field, but I think it would be a nice evolution and support the myriad.
Details on how this would work are still fuzzy and Iโll need to think on it more, but want to toss it up for consideration.
ViewScopeProvider
LifecycleOwnerScopeProvider
(#71) for arch componentsLifecycleScopeProvider
_____Scoper
methods be optional?ScopeProvider
itself!I tried the AndroidLifecycleScopeProvider
implementation with the following code, but it doesn't work and keep emitting when onStop()` is called.
override fun onStart() {
super.onStart()
Observable.interval(0, 1, TimeUnit.SECONDS)
.doOnDispose { println("dispose") }
.to(ObservableScoper(AndroidLifecycleScopeProvider.from(this)))
.subscribe {
println("emit:" + it)
}
}
override fun onStop() {
super.onStop()
println("onStop")
}
Thanks.
Hi. I was asked to review the library's RxJava 2 usage for potential logical or concurrency issues.
Review @ commit: 0147484b
DisposableMaybeObserver
could be created and setOnce
'd before the call to lifecycle.subscribe()
.mainDisposable
could be already set and the delegate
currently executing one of its onXXX
methods which violates the contract unless it is guaranteed both the lifecycle signal and the onXXX calls happen on the same thread (i.e., forced observeOn(AndroidSchedulers.mainThread())
). Possible solution: always call delegate.onSubscribe(this)
first, mutually exclude the onXXX signals via an AtomicBoolean once
.onComplete
.synchronized
is pointless here.synchronized
is pointless here. If the purpose was to mutually exclude with #L73
then it is not worth it. If lazySet
wins then dispose(ref)
will do nothing, if they race, both cases could win probabilistically.CompletableObserver
implementation.CompletableObserver
implementation. Here one would need a HalfSerializer
to mutually exclude the onXXX events (as there could be multiple onNext
calls).CompletableObserver
implementation.CompletableObserver
implementation and then some.onStart
provided by the API and the delegate.onSubscribe
is called after mainSubscription
is definitely set: the null check should always pass and thus unnecessary. Note though that if one does the setOnce(mainSubscription)
before the lifecycle is attached, one may need to use deferred requesting to not trigger the upstream emission while the code is setting up other things in onSubscribe
.onSubscribe
is not called which can lead to a NullPointerException
in the dowstream. The no-op Disposable
should be sent to the Observer
first.Observer
may cancel immediately and since the listener is not registered with the view
yet, it won't get removed but then added unconditionally. Assuming the events also come from the main thread, one could add the listener first, and call onSubscribe
next. If adding the listener triggers an event emission immediately, keep the current order and after adding the listener, check if the listener has been disposed in the meantime and remove the listener.ViewAttachEventsObservable
.forintrospection
.s
from purpose
?final
.final
.final
.After a bit of experimentation in #79 and travis-ci/travis-ci#8360, the new trusty setup on travis CI's public/oss use is not getting the avdmanager on the PATH for some reason. In light of that, I'm going to disable the android tests in travis CI in #79 (and will link this in a comment above it).
Until it's fixed, we'll just have to manually check android checks before merging PRs.
WIP notes for changelog when we release, which should be soooooonish
AutoDispose's primary API is now via static autoDisposable()
methods on the AutoDispose
class. The previous to()
based APIs are now completely deprecated, and will be removed in AutoDispose 1.0.
This has been sort of the long-standing ideal API for AutoDispose for awhile, but wasn't possible until the introduction of the new as()
operator in RxJava. As this operator is still not marked as stable (and won't until RxJava 2.2.0), AutoDispose will not be updated to 1.0 until then.
The main difference is that you no longer have to specify the type indirection, and the returned converter is applicable for all 5 RxJava types. In use, it looks like this:
Flowable.just(1)
.as(autoDisposable(scope))
.subscribe()
Observable.just(1)
.as(autoDisposable(scope))
.subscribe()
Maybe.just(1)
.as(autoDisposable(scope))
.subscribe()
Single.just(1)
.as(autoDisposable(scope))
.subscribe()
Completable.complete()
.as(autoDisposable(scope))
.subscribe()
There are three overloads for autoDisposable()
, for each of the three scope types (Maybe
, ScopeProvider
, and LifecycleScopeProvider
).
The Kotlin bindings have also been updated to match semantics, with the autoDisposeWith
extension functions being deprecated in favor of analogous autoDisposable
. These are WARNING
level in this release, and will become ERROR
in AutoDispose 0.6.0, before finally being removed in 1.0. They also provide replaceWith
options (compatible with Kotlin's deprecation quickfixes).
autoDisposable
reads best when statically imported (so you can do .as(autoDisposable(...))
, which you can safely do if you're using Java 8.
TODO Provide structural replaces.
David Karnok (@akarnokd, RxJava project lead) did an audit of the current codebase and gave extensive feedback in #130. #138 implements that feedback. This handled a lot of concurrency gotchas and edge cases we were missing before. See the issue and PR for full details.
AutoDisposePlugins
has a new API to control whether or not lifecycle exception stacktraces are filled in. What this means is that if you opt out, the exceptions thrown in LifecycleScopeProvider
boundary issues will no longer have a stacktrace (getStacktrace()
will return an empty array) and only carry the type name and message. This can be useful to gain some performance if you track stacktracing via other means.
ScopeProvider
has a static instance of an "unbound" provider directly in the interface now for reuse. This obviates the need for TestScopeProvider#unbound()
, which has been removed. Usage is simple:
Observable.just(1)
.as(autoDisposable(ScopeProvider.UNBOUND))
.subscribe()
as()
) (#141)subscribeBy
example extension in the sample app displaying how you can add extension functions to the *SubscribeProxy
classes. (#127)delegateObserver()
APIs on AutoDisposing
observers have been promoted to stable. Considering they are useful for subscribeWith()
, we can just keep it observer-based and keep the library more flexible long-term (#144)Thanks to the following contributors! @charlesdurham @ajalt @tbsandee @akarnokd
Corresponding events works well , but how can i do a bindUntil just like rxLife ?
fun <T : Any> Observable<T>.mySubscribe(f: () -> Unit): Disposable {
return doOnSubscribe { f() }
.subscribe(Consumer {}, Consumer {})
}
With .to(ObservableScoper(AndroidLifecycleScopeProvider.from(this)))
, we can't take advantage of kotlin's extension to use my custom mySubscribe
method since it is a ObservableSubscribeProxy
type.
Do you think it is better to expose the observable source for ObservableSubscribeProxy
?
This adds autoDisposeWith()
extensions to RxJava types.
myObservable
.doWhatever()
.autoDisposeWith(this)
.subscribe()
Modeled after RxJava's plugins, this allows you to customize the behavior of AutoDispose with lifecycle boundary checks.
AutoDisposePlugins.setOutsideLifecycleHandler(t -> {
// Swallow the exception, or rethrow it, or throw your own!
})
A good use case of this is, say, just silently disposing/logging observers outside of lifecycle exceptions in production but crashing on debug.
Two helpers were added to simulate conditions in testing.
TestLifecycleScopeProvider
start()
and stop()
TestScopeProvider
emit()
.For testing with just the Maybe<?>
scope, we recommend using RxJava's built-in MaybeSubject
.
@CheckReturnValue
annotations to subscribeWith
methods. (#53)Other tidbits:
@NonNull
annotations. Everything is @NonNull
by default, and only elements@Nullable
are not.java-library
plugin for gradle (#64). The RxJava dependencies are marked as api
.compileOnly
, but if a need arises/community wants them - we can compile them in a future version.Could you pls provide some sample? I still not very clear to use it from read me.
This matches RxJava's subscribes and should make sense since that's their explicit purpose.
If I add .to(new ObservableScoper<RequestLocation>(lifecycle))
to an observable it makes it noticeably slower. Not super slow: just 0.13ms on a pixel (I put this in a loop with 500 iterations and measured the increase in duration). But slower than using a simple composite disposable pattern.
Some thoughts from a chat with Zac below
The exec path under the hood when you do this (this point when you call to()):
(this point on happens when you call subscribe())
potential areas of wins:
This is for AutoDispose 0.3.0.
A pattern I used for my custom scope providers for fragments/activities against AutoDispose 0.2.0 was to create a single scope provider (based on BehaviorSubject
) and then share it between all observable chains for that entity. This worked fine, I think.
Now I'm migrating to the new AndroidLifecycleScopeProvider
and I find that if I create a single instance for a fragment or activity, when, for example, a second onStart
event occurs I get the exception below.
If I recreate the AndroidLifecycleScopeProvider instance for each subscription then it works fine, but it feels like it could be excess overhead. I do notice that this is the pattern you use in the samples, so perhaps it's intentional. There's no indication in AndroidLifecycleScopeProvider that it's stateful, so I'm just checking.
io.reactivex.exceptions.OnErrorNotImplementedException: Lifecycle has ended!
E at io.reactivex.internal.functions.Functions$OnErrorMissingConsumer.accept(Functions.java:704)
E at io.reactivex.internal.functions.Functions$OnErrorMissingConsumer.accept(Functions.java:701)
E at io.reactivex.internal.observers.LambdaObserver.onError(LambdaObserver.java:77)
E at com.uber.autodispose.AutoDisposingObserverImpl.onError(AutoDisposingObserverImpl.java:102)
E at com.uber.autodispose.AutoDisposingObserverImpl$2.accept(AutoDisposingObserverImpl.java:57)
E at com.uber.autodispose.AutoDisposingObserverImpl$2.accept(AutoDisposingObserverImpl.java:55)
E at io.reactivex.internal.operators.maybe.MaybeCallbackObserver.onError(MaybeCallbackObserver.java:83)
E at io.reactivex.internal.operators.maybe.MaybeDoOnEvent$DoOnEventMaybeObserver.onError(MaybeDoOnEvent.java:100)
E at io.reactivex.internal.disposables.EmptyDisposable.error(EmptyDisposable.java:83)
E at io.reactivex.internal.operators.maybe.MaybeDefer.subscribeActual(MaybeDefer.java:44)
E at io.reactivex.Maybe.subscribe(Maybe.java:3727)
E at io.reactivex.internal.operators.maybe.MaybeDoOnEvent.subscribeActual(MaybeDoOnEvent.java:39)
E at io.reactivex.Maybe.subscribe(Maybe.java:3727)
E at io.reactivex.Maybe.subscribeWith(Maybe.java:3793)
E at io.reactivex.Maybe.subscribe(Maybe.java:3714)
E at io.reactivex.Maybe.subscribe(Maybe.java:3680)
E at com.uber.autodispose.AutoDisposingObserverImpl.onSubscribe(AutoDisposingObserverImpl.java:51)
E at io.reactivex.internal.observers.BasicFuseableObserver.onSubscribe(BasicFuseableObserver.java:66)
E at io.reactivex.internal.observers.BasicFuseableObserver.onSubscribe(BasicFuseableObserver.java:66)
E at com.jakewharton.rxrelay2.PublishRelay.subscribeActual(PublishRelay.java:68)
E at io.reactivex.Observable.subscribe(Observable.java:10910)
E at io.reactivex.internal.operators.observable.ObservableFilter.subscribeActual(ObservableFilter.java:30)
E at io.reactivex.Observable.subscribe(Observable.java:10910)
E at io.reactivex.internal.operators.observable.ObservableMap.subscribeActual(ObservableMap.java:33)
E at io.reactivex.Observable.subscribe(Observable.java:10910)
E at com.uber.autodispose.ObservableScoper$AutoDisposeObservable.subscribeActual(ObservableScoper.java:113)
E at io.reactivex.Observable.subscribe(Observable.java:10910)
E at io.reactivex.Observable.subscribe(Observable.java:10896)
E at io.reactivex.Observable.subscribe(Observable.java:10799)
E at com.uber.autodispose.ObservableScoper$1.subscribe(ObservableScoper.java:71)
E at com.orangebikelabs.BrowseFragment.onStart(BrowseFragment.java:222)
E at android.support.v4.app.Fragment.performStart(Fragment.java:2380)
E at android.support.v4.app.FragmentManagerImpl.moveToState(FragmentManager.java:1458)
E at android.support.v4.app.FragmentManagerImpl.moveFragmentToExpectedState(FragmentManager.java:1740)
E at android.support.v4.app.FragmentManagerImpl.moveToState(FragmentManager.java:1809)
E at android.support.v4.app.BackStackRecord.executePopOps(BackStackRecord.java:857)
E at android.support.v4.app.FragmentManagerImpl.executeOps(FragmentManager.java:2577)
E at android.support.v4.app.FragmentManagerImpl.executeOpsTogether(FragmentManager.java:2367)
E at android.support.v4.app.FragmentManagerImpl.removeRedundantOperationsAndExecute(FragmentManager.java:2322)
E at android.support.v4.app.FragmentManagerImpl.execPendingActions(FragmentManager.java:2229)
E at android.support.v4.app.FragmentManagerImpl$1.run(FragmentManager.java:700)
E at android.os.Handler.handleCallback(Handler.java:751)
E at android.os.Handler.dispatchMessage(Handler.java:95)
E at android.os.Looper.loop(Looper.java:154)
E at android.app.ActivityThread.main(ActivityThread.java:6120)
E at java.lang.reflect.Method.invoke(Native Method)
E at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:865)
E at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:755)
E Caused by: com.uber.autodispose.LifecycleEndedException: Lifecycle has ended!
E at com.uber.autodispose.android.lifecycle.AndroidLifecycleScopeProvider$1.apply(AndroidLifecycleScopeProvider.java:52)
E at com.uber.autodispose.android.lifecycle.AndroidLifecycleScopeProvider$1.apply(AndroidLifecycleScopeProvider.java:38)
E at com.uber.autodispose.ScopeUtil$3.call(ScopeUtil.java:91)
E at com.uber.autodispose.ScopeUtil$3.call(ScopeUtil.java:74)
E at io.reactivex.internal.operators.maybe.MaybeDefer.subscribeActual(MaybeDefer.java:41)
E ... 39 more
Ran into a case where an old RxJava method returned a Subscription bound to a lifecycle. We could leverage subscribeWith
to do the same, but currently around()
methods just return simple Observers and don't indicate that they're disposable. We can't make all the observers extend RxJava's available Disposable_______Observer
s, so we'd have to make our own. Question is if we want to do this in the API, like returning an AutoDisposeObserver
that implements Disposable
.
@uber/mobile-platform-android thoughts?
Mostly we can use what was detailed in the internal RFC.
Other things:
A little simpler. We can add an overload for views too out of convenience.
Looks like AutoDispose 0.3.0 is compatible only with android.arch.lifecycle 1.0.0-beta1 and the move to rc1 is not backwards compatible:
java.lang.ClassCastException: com.uber.autodispose.android.lifecycle.LifecycleEventsObservable_ArchLifecycleObserver_LifecycleAdapter cannot be cast to android.arch.lifecycle.GeneratedAdapter
This is probably fixed by the bump to use rc1 in PR #111.
This issue can serve as a sentry until that PR Is committed and a new version is released.
Need 'em
Version: 0.3.0
I was previously using RxLifecycle
and played around a bit with AutoDispose
. So far it works great, but i miss the bindUntilEvent
method from RxLifecycle
.
I have seen the other issue about this, but could not manage to get it to work, especially when using architecture components integration...
Some samples or even better, a first class api would be very appreciated.
My current RxLifecycle
code looks like this:
errorButton.clicks()
.bindUntilEvent(this, Lifecycle.Event.ON_DESTROY)
.subscribe {
// Do something.
}
Thanks in advance!
As I'm implementing more with AutoDispose, the type specification is getting annoying. With the changes I made in #17 though (the clauses pattern in particular), it actually is fairly trivial to pull these out to just individual classes. E.g.
AutoDisposeObserver
AutoDisposeSingleObserver
AutoDisposeMaybeObserver
etc
So the API becomes as follows:
myObservable.subscribe(AutoDisposeObserver
.withScope(...)
.around(...)
Trying this out briefly, it does save precious seconds while not blowing up autocomplete (if anything, saves autocomplete time since you can pick the type right there in the IDE). Cons are that refactoring to a different type is harder, and we sacrifice the convenience of a single point of entry.
@uber/mobile-platform-android what do you think?
Need to make a call on this based on our integration and see how it fairs using this in Java 7 source.
Ideally, it should be easy to create ScopeProvider
and LifecycleScopeProvider
instances for testing.
They should:
LifecycleScopeProvider
is particularly fragile).Right now the only version I can see is X.Y.Z
. (Which does not exist on maven). It would be great if you could tag releases.
Not possible until the repo is public, but is in place
Currently have -android and -android-archcomponents
Wondering if we should move these under a /android
directory and do the following
View.scope()
rather than ViewScopeProvider.from(view)
)AutoDisposeActivity
or AutoDisposeViewHolder
TestAndroidLifecycle
to its own artifact so the main archcomponents artifact only have a runtime dependencyWIP notes for changelog when we release, which should be soooooonish
Android components have been split up into several artifacts under :android
:
autodispose-android
: Core android utilities, previously :autodispose-android
autodispose-android-archcomponents
: Utilities for lifecycles in android archcomponents, previously :autodispose-android-archcomponents
but does not have the test helperautodispose-android-archcomponents-test
: Test utilities for working with arch components, namely TestLifecycleOwner
, formerly TestAndroidLifecycleScopeProvider
.
extensions
dependency from the main arch components artifact and keep this optional. This API can also be used for general use testing for arch components, as it's not actually specific to AutoDispose.autodispose-android-kotlin
: kotlin bindings for autodispose-android
autodispose-android-archcomponents-kotlin
: kotlin bindings for autodispose-android-archcomponents
autodispose-android-archcomponents-test-kotlin
: kotlin bindings for autodispose-android-test-archcomponents
Related changes:
1.0.0-rc1
, which should fix compatibility issues noted in #113untilEvent
overload for AndroidLifecycleScopeProvider (#107)
ON_STOP
would resolve to ON_DESTROY
. Now, they resolve to stop on the next destruction event. This brings it inline with the modern behavior of arch components version -rc1
.AndroidLifecycleScopeProvider
s are now reusable. This is somewhat experimental, as it works by dynamically resolving the last event based on the state. Please report any issues! (#121)A new autodispose-rxlifecycle
interop module was added, adding support for scoping to RxLifecycle's LifecycleProvider
API. (#118)
unbound()
factory on TestScopeProvider
(#108)Thanks to the following contributors! @rubengees @bangarharshit
This is something I think might be an inherent design flaw in AutoDispose currently. Maybe was used before under the idea that scopes could potentially not emit. However, it's left us in an awkward case where they could either emit or complete, which is different. Furthermore, it's actually not dangerous to depend on a single because in the event of termination of the source stream, it would just dispose the single it was waiting on.
This is a pretty significant API change. Fortunately I think it would be mitigated by the fact that most people use ScopeProvider
and LifecycleScopeProvider
+ hopefully the test helpers.
Should we add one? The gist would be that it could understand RxLifecycle's LifecycleProvider
mechanics, which in turn should also lend support for its various components (RxActivity
, etc)
Exceptions aren't free. Currently we use exceptions to indicate lifecycle boundaries. Normally their trace would be helpful, but in some setups (such as our internal one), users track sources of exceptions in rx chains separately and just use the exception as a signal. Perhaps we can save a little performance by making a plugin hook to configure this. Then when receiving exceptions, traces would not be inspectable and the type would be your only clue.
This would be possible for pre-java8 too by overriding fillInStacktrace()
and controlling behavior there. The default would be to remain enabled.
public class OutsideLifecycleException extends RuntimeException {
public OutsideLifecycleException(String s) {
super(s);
}
@Override public final synchronized Throwable fillInStackTrace() {
if (AutoDisposePlugins.fillInStacktraces()) {
return super.fillInStackTrace();
} else {
return this;
}
}
}
Questions:
fillInStacktraces()
method?lockdown()
)A declarative, efficient, and flexible JavaScript library for building user interfaces.
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. ๐๐๐
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google โค๏ธ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.