trello-archive / rxlifecycle Goto Github PK
View Code? Open in Web Editor NEWLifecycle handling APIs for Android apps using RxJava
License: Apache License 2.0
Lifecycle handling APIs for Android apps using RxJava
License: Apache License 2.0
We are running into an issue where there are some things that cannot occur after onSaveInstanceState is called (e.g. fragment transactions) and we want to make sure that the Rx chain is unsubscribed at that point, but the activity lifecycle methods do not cover that. Pause is close but there is no guaranteed order between onPause and onSaveInstanceState which still leaves a possibility of an error.
I could notice in fragments when I transform an Observable with bindUntilEvent(FragmentEvent.DESTROY_VIEW) that onNext callback gets called after onDestroyView method returns. Since I use ButterKnife and call unbind in onDestroyView I am getting NullPointerException when my onNext implementation try to access views. This behaviour does not happen all the time. Seems to be a concurrency issue. Any thoughts?
They're a bit too wordy and they could be simplified, to make it clear that it's really just about detaching.
Right now I'm in the process of upgrading to RxAndroid 1.0.1.
How can I get the same behavior as AppObservable.bindActivity
and AppObservable.bindFragment
as of RxAndroid 0.24.0?
Since AppObservable used the isFinishing method for binding and that one is not available as an ActivityEvent I'm a little bit puzzled as how to I can sustain that behavior.
Is there any equivalent behavior / method?
Are there any plans to implement Android lifecycle support (recreating over rotation)? I've seen some workarounds in the Internet, but afaiu it is some kind of the DIY LoaderManager approach. Maybe I would make pull request, if I have enough time and it is interesting to somebody but me.
Hey,
Just saw the kotlin extension to this lib and wanted to give it a try. Unfortunatly I wasnt able to resolve it from either jCenter nor MavenCentral. I checked both manually and they contain every rxlifecycle package except the -kotlin
one.
Did you forget to upload this?
Thanks,
danijoo
Currently this requires SDK version 23, which is very restrictive when trying to support "older" builds of Android. Dropping this to at least 22 would allow use in Android 5 and below.
Note to self...
RxTextView.textChanges(edt)
.compose(this.bindUntilEvent(ActivityEvent.PAUSE))
.doOnUnsubscribe(() -> Log.e("-->>", "doOnUnsubscribe"))
.debounce(600, TimeUnit.MILLISECONDS, AndroidSchedulers.mainThread())
.filter(charSequence -> {
tv.setText("");
return charSequence.length() > 0;
})
Error: (61, 40) error: find no sign
Symbol: Method length ()
Location: type Object variable charSequence
The RxBinding library contains over 600 methods. That's about five times the total number of methods in the RxLifecycle base + components libraries. It's unfortunate that this library pulls in that many extra methods, especially when it appears that library is only used for one line of code.
Would it be possible to split out the RxBinding-related functionality into an optional module?
Hi how cai I use RxFragment with Support v4?
I cannot find RxSupportFragment class :)
I tried the following code, based on your Kotlin example from the readme.
class MainActivity: RxAppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
lifecycle()
.bindUntilEvent(this, ActivityEvent.STOP)
.subscribe({Log.d("Test", "Success")},{Log.e("Test", it.message, it)})
}
}
I expected to see some "Success" log entries but got the following error.
Since my experience with RxJava and the RxLifecycle library is little, I am not sure whether my code is flawed or it is a bug.
E/Test: com.trello.rxlifecycle.ActivityEvent cannot be cast to rx.Observable
java.lang.ClassCastException: com.trello.rxlifecycle.ActivityEvent cannot be cast to rx.Observable
at com.trello.rxlifecycle.RxLifecycle$1.call(RxLifecycle.java:79)
at rx.internal.operators.OperatorMap$1.onNext(OperatorMap.java:54)
at rx.internal.operators.NotificationLite.accept(NotificationLite.java:150)
at rx.subjects.SubjectSubscriptionManager$SubjectObserver.accept(SubjectSubscriptionManager.java:318)
at rx.subjects.SubjectSubscriptionManager$SubjectObserver.emitLoop(SubjectSubscriptionManager.java:291)
at rx.subjects.SubjectSubscriptionManager$SubjectObserver.emitFirst(SubjectSubscriptionManager.java:270)
at rx.subjects.BehaviorSubject$1.call(BehaviorSubject.java:106)
at rx.subjects.BehaviorSubject$1.call(BehaviorSubject.java:102)
at rx.subjects.SubjectSubscriptionManager.add(SubjectSubscriptionManager.java:100)
at rx.subjects.SubjectSubscriptionManager.call(SubjectSubscriptionManager.java:60)
at rx.subjects.SubjectSubscriptionManager.call(SubjectSubscriptionManager.java:35)
at rx.Observable$2.call(Observable.java:162)
at rx.Observable$2.call(Observable.java:154)
at rx.Observable$2.call(Observable.java:162)
at rx.Observable$2.call(Observable.java:154)
at rx.Observable$2.call(Observable.java:162)
at rx.Observable$2.call(Observable.java:154)
at rx.Observable.subscribe(Observable.java:8191)
at rx.Observable.subscribe(Observable.java:8158)
at rx.Observable.subscribe(Observable.java:7962)
at ninja.mess.templateapplication.activity.MainActivity.onCreate(MainActivity.kt:28)
at android.app.Activity.performCreate(Activity.java:5990)
at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1106)
at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2278)
at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2387)
at android.app.ActivityThread.access$800(ActivityThread.java:151)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1303)
at android.os.Handler.dispatchMessage(Handler.java:102)
at android.os.Looper.loop(Looper.java:135)
at android.app.ActivityThread.main(ActivityThread.java:5254)
at java.lang.reflect.Method.invoke(Native Method)
at java.lang.reflect.Method.invoke(Method.java:372)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:903)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:698)
Caused by: rx.exceptions.OnErrorThrowable$OnNextValue: OnError while emitting onNext value: CREATE
at rx.exceptions.Exceptions.throwOrReport(Exceptions.java:187)
at rx.internal.operators.OperatorMap$1.onNext(OperatorMap.java:56)
at rx.internal.operators.NotificationLite.accept(NotificationLite.java:150)
at rx.subjects.SubjectSubscriptionManager$SubjectObserver.accept(SubjectSubscriptionManager.java:318)
at rx.subjects.SubjectSubscriptionManager$SubjectObserver.emitLoop(SubjectSubscriptionManager.java:291)
at rx.subjects.SubjectSubscriptionManager$SubjectObserver.emitFirst(SubjectSubscriptionManager.java:270)
at rx.subjects.BehaviorSubject$1.call(BehaviorSubject.java:106)
at rx.subjects.BehaviorSubject$1.call(BehaviorSubject.java:102)
at rx.subjects.SubjectSubscriptionManager.add(SubjectSubscriptionManager.java:100)
at rx.subjects.SubjectSubscriptionManager.call(SubjectSubscriptionManager.java:60)
at rx.subjects.SubjectSubscriptionManager.call(SubjectSubscriptionManager.java:35)
at rx.Observable$2.call(Observable.java:162)
at rx.Observable$2.call(Observable.java:154)
at rx.Observable$2.call(Observable.java:162)
at rx.Observable$2.call(Observable.java:154)
at rx.Observable$2.call(Observable.java:162)
at rx.Observable$2.call(Observable.java:154)
at rx.Observable.subscribe(Observable.java:8191)
at rx.Observable.subscribe(Observable.java:8158)
at rx.Observable.subscribe(Observable.java:7962)
at ninja.mess.templateapplication.activity.MainActivity.onCreate(MainActivity.kt:28)
at android.app.Activity.performCreate(Activity.java:5990)
at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1106)
at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2278)
at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2387)
at android.app.ActivityThread.access$800(ActivityThread.java:151)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1303)
at android.os.Handler.dispatchMessage(Handler.java:102)
at android.os.Looper.loop(Looper.java:135)
at android.app.ActivityThread.main(ActivityThread.java:5254)
at java.lang.reflect.Method.invoke(Native Method)
at java.lang.reflect.Method.invoke(Method.java:372)
These are my dependencies:
compile 'com.android.support:appcompat-v7:23.2.1'
compile 'io.reactivex:rxandroid:1.1.0'
compile 'io.reactivex:rxjava:1.1.0'
compile "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
compile 'com.trello:navi:0.2.0'
compile 'com.trello:rxlifecycle:0.5.0'
compile 'com.trello:rxlifecycle-components:0.5.0'
compile 'com.trello:rxlifecycle-navi:0.5.0'
compile 'com.trello:rxlifecycle-kotlin:0.5.0'
I ran into issues building against Android level 22. Issue went away targeting API 23. Not sure if I just missed it in the documentation or if it's worth adding to the README?
Using this method to tap into Activity lifecycle would avoid having to extend a specific activity implementation like RxAppCompatActivity. Any particular reasons not to use it?
If I compose using rxlifecycle, the compiler seems to throw errors. If I leave out the composition, the compiler is fine.
error:
Error:(55, 45) error: no suitable method found for subscribe(Subscriber<TestModel>)
method Observable.subscribe(Action1<? super Object>) is not applicable
(argument mismatch; Subscriber<TestModel> cannot be converted to Action1<? super Object>)
method Observable.subscribe(Observer<? super Object>) is not applicable
(argument mismatch; Subscriber<TestModel> cannot be converted to Observer<? super Object>)
method Observable.subscribe(Subscriber<? super Object>) is not applicable
(argument mismatch; Subscriber<TestModel> cannot be converted to Subscriber<? super Object>)
method Observable.<T>subscribe(Subscriber<? super T>,Observable<T>) is not applicable
(cannot infer type-variable(s) T
(actual and formal argument lists differ in length))
where T is a type-variable:
T extends Object declared in method <T>subscribe(Subscriber<? super T>,Observable<T>)
relevant objects:
PublishSubject<TestModel> pubTest = PublishSubject.create();
Subscriber<TestModel> testNotify = new Subscriber<TestModel>() {...};
protected void onResume() {
super.onResume();
pubTest.compose(bindToLifecycle()).subscribe(testNotify);
}
RxLifecyle.bindUnitEvent returns return new Observable.Transformer<T, T>
But in the components classes your are returning
Observable.Transformer<? super T, ? extends T>
on bindUntilEvent and bindToLifecycle
SO which one folks? To haphazard a guess would it be the RxLifecycle ones are correct?
I have a rather hard time to get RxJava
in conjunction with RxLifecycle
to play nicely during tests. I created a custom IdlingResource
basically mimicking this prior art, but I see many, many subscriptions that are started, but never stopped, thus the idling resource never getting idle.
I realized that it may have something to do with RxLifecycle
since it emits lifecycle events on a never-ending stream and indeed, when I remove my compose(bindToLifecycle())
calls, it kind of works. Now the interesting question is: Do you know of any way to detect (and ignore) subscriptions from RxLifecycle
or is deactivating the bindToLifecycle()
call in test mode altogether the only option?
Hello There,
can you please tell me why is minSDK set to 15?
Since I updated from v0.1.0 to v0.2.0 I get this error. I think it is a proguard problem.
I found this producerIndex
field in rx.internal.util.unsafe.SpscArrayQueueProducerFields
and in rx.internal.util.unsafe.SpmcArrayQueueProducerField
This line -keep class rx.internal.util.unsafe.** { *; }
in my proguard conf fixes the problem.
Maybe you want to add this info to the readme? :-)
I doesn't have this problem with RxAndroid or with RxBinding
Single is the alternate version of Observable and should be supported to automatically unsubscribe based on Lifecycle events as well.
I'd like to add the ability to receive events whenever onNewIntent() is called. How can this be done? Currently the event objects are enums (for example ActivityEvent) which can not be extended to include new objects.
Could it possible to add simple View lifecycle support? e.g. onAttachedToWindow
and onDetachedFromWindow
.
Previously we avoided this route so that we could remain consistent between Activities and Fragments, but perhaps it's worth optimizing the Activity route (especially since some people just avoid Fragments altogether).
The idea is you could call bindUntilEvent(Activity, Event)
and bindToLifecycle(Activity)
instead of the current methods. We would then register an ActivityLifecycleCallbacks with the Application of the Activity which can provide us a lifecycle Observable.
Some questions about implementation...
Right now, it throws an exception, but it would make more sense to immediately end the subscription (since, presumably, the only way this happened was because you delayed a subscription past the lifecycle, but intend not to keep it outside of the lifecycle).
It works well in version 0.3.0.
Here is my code snippet:
feedService.getTopicFeedList(groupId, loadPage)
.compose(this.bindToLifecycle())
.subscribe(
feedDetails -> {
BannerFeedsAdapter adapter = (BannerFeedsAdapter) recyclerView.getAdapter();
if (adapter == null) {
adapter = new BannerFeedsAdapter(BannerFeedsActivity.this, feedDetails, bannerUrl, isContainBanner);
recyclerView.setAdapter(adapter);
} else {
adapter.append(feedDetails);
adapter.notifyDataSetChanged();
}
bannerImg.setVisibility(View.GONE);
},
throwable -> RxUtil.handleError(throwable, BannerFeedsActivity.this)
);
The type of feedDetails
is Object
when I use the 0.3.1 version library.
With 0.3.1 Android Studio 1.5 started having problems inferring the types when using compose
.
Any chance the SNAPSHOT version of the library will be pushed to maven?
Since v. 0.3.0 is already released, maybe now it's time to bumb the version to 0.3.1-SNAPSHOT and push it to maven snapshot repository?
Hi,
After beginning using this library i do not using any Subscriptions.
My question is:
Is ok to use Observer instead of Subscriber because anyway i do not use subscription functionality?
Thanks
Hi!
There is no way to extend from an rx component which in turn extends from AppCompatActivity. Is there?
These methods are actually generic enough that they can be reused for other lifecycles (such as controllers in MVP patterns). Would be nice to use them rather than copying in logic and mixing in activity and controller lifecycles.
Good day, fellow colleagues! )
We had an issue with this approach your library describes.
Imagine there is a FragmentA
and FragmentB
. Both subscribe to some Observable in afterCreateView()
callback (this is our safety-callback after onCreateView()
). Using RxLifecycle we bound this subscription until onDestroyView()
callback.
Next: we show FragmentA
(got our subscription bound and ready for any incoming signals), then we use Android's FragmentManager
to navigate to FragmentB
- using replace, but leaving FragmentA
in backstack so we can easily navigate "back". During navigating FragmentManager
detaches FragmentA instance and the last lifecycle event we (and, therefore, our BehavoirSubject lifecycleSubject
) get is FragmentEvent.DESTROY_VIEW
.
FragmentB
also subscribes to our Observable
and bounds to lifecycle - nothing to do here, let's go back. Since FragmentA was navigated from with backstack enabled - it's instance is still there, and last lifecycle event that lifecycleSubject got was that FragmentEvent.DESTROY_VIEW
- he still remembers it, since that is how BehaviorSubject works and he never died because FragmentA's instance is still alive.
Getting to the point: when we resurrect FragmentA
instance (FragmentManager
does that for us during "back" navigation) we - as before - bind our Observable
to lifecycle and want to subscribe to it. But the first thing we get as soon as we bind - FragmentEvent.DESTROY_VIEW
event, out subscription immediately receives onCompleted
. Our FragmentA
is not happy about this - no data update is possible for him from now on.
So far we've resolved this by switching to PublishSubject
usage in our RxBaseFragment. But I fear there is something I might have forgotten about. Can you explain why BehaviorSubject
was used in the first place? Is our workaround applicable (assuming that none of our programmers will try to bind to observable after onDestroyView()
callback)?
P. S.: sorry, if my explanation is too broad - Rx approach is still tricky for me and I was at the same time trying to clear things up for myself with this issue.
Hi, I got some weird behavior with retrofit Observables when used together with RxLifecycle.
I am not quite sure, but by this documentation[1] the default behavior of Observables is to don't call onComplete if onError is fired, but the onComplete method is being called in onDestroy of Actvity/Fragment.
Is this the right thing to do?
In the code below I can get the openHomeScreen() to be called. Should I move it to onNext()?
mUserApiClient
.get()
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.compose(RxLifecycle.bindFragment(lifecycle()))
.subscribe(
new Subscriber<User>() {
@Override
public void onCompleted() {
openHomeScreen();
}
@Override
public void onError(Throwable e) {
handleRequestError(e);
}
@Override
public void onNext(User user) {
mUserManager.setUser(user);
}
});
}
Closely related to #39.
Hi.
Here is my environment
public <T> Transformer<T, T> applyToMainThread() {
return new Transformer() {
public Observable<T> call(Observable<T> observable) {
return observable.observeOn(AndroidSchedulers.mainThread());
}
};
}
public <T> Transformer<T, T> bindRxActivity(ActivityLifecycleProvider provider) {
return provider.bindToLifecycle();
}
java.lang.InternalError:22:12.233 13773-13809/? W/System.err﹕ java.lang.InternalError
at rx.internal.util.unsafe.UnsafeAccess.addressOf(SourceFile:103)
at rx.internal.util.unsafe.MpmcArrayQueueProducerField.<clinit>(SourceFile:31)
at rx.internal.util.ObjectPool.initialize(SourceFile:125)
at rx.internal.util.ObjectPool.<init>(SourceFile:55)
at rx.internal.util.ObjectPool.<init>(SourceFile:37)
at rx.internal.util.RxRingBuffer$1.<init>(SourceFile:279)
at rx.internal.util.RxRingBuffer.<clinit>(SourceFile:279)
at rx.internal.operators.OnSubscribeCombineLatest.<init>(SourceFile:52)
at rx.Observable.combineLatest(SourceFile:789)
at rx.Observable.combineLatest(SourceFile:520)
at com.trello.rxlifecycle.RxLifecycle$2.call(SourceFile:152)
at com.trello.rxlifecycle.RxLifecycle$2.call(SourceFile:148)
at rx.Observable.compose(SourceFile:204)
at com.restaurant.zoomi.SplashScreen.getApplicationObject(SourceFile:131)
at com.restaurant.zoomi.SplashScreen.onCreate(SourceFile:118)
at android.app.Activity.performCreate(Activity.java:5990)
at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1106)
at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2278)
at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2387)
at android.app.ActivityThread.access$800(ActivityThread.java:151)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1303)
at android.os.Handler.dispatchMessage(Handler.java:102)
at android.os.Looper.loop(Looper.java:135)
at android.app.ActivityThread.main(ActivityThread.java:5254)
at java.lang.reflect.Method.invoke(Native Method)
at java.lang.reflect.Method.invoke(Method.java:372)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:903)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:698)
at java.lang.Class.getDeclaredField(Class.java:890)
at rx.internal.util.unsafe.UnsafeAccess.addressOf(SourceFile:100)
SourceFile:152 points to following code.
private static <T, R> Transformer<T, T> bind(Observable<R> lifecycle, final Func1<R, R> correspondingEvents) {
if(lifecycle == null) {
throw new IllegalArgumentException("Lifecycle must be given");
} else {
final Observable sharedLifecycle = lifecycle.share();
return new Transformer() {
public Observable<T> call(Observable<T> source) {
return source.takeUntil(Observable.combineLatest(sharedLifecycle.take(1).map(correspondingEvents), sharedLifecycle.skip(1), new Func2() {
public Boolean call(R bindUntilEvent, R lifecycleEvent) {
return Boolean.valueOf(lifecycleEvent == bindUntilEvent);
}
}).takeFirst(new Func1() {
public Boolean call(Boolean shouldComplete) {
return shouldComplete;
}
}));
}
};
}
}
This code runs fine in debug build without obfuscation.
Is there anything done wrong w.r.t. proguard?
API 23 introduced Fragment.onAttach(Context)
and deprecated Fragment.onAttach(Activity)
. We should look into using the newer version (while maintaining backwards compatibility).
Hi, I'm using RxLifecycle in my project, it works well on most scenes, but when I using bindToLifecycle
with subscribeOn(AndroidSchedulers.mainThread())
, the Subscription#unsubscribe
was called when the screen turns off.
I'm new to RxJava so I'm not sure whether it's a bug or not.
Below is the sample code:
RxTextView.textChanges(mSearchEditor).compose(this.<CharSequence>bindToLifecycle()).doOnUnsubscribe(new Action0() {
@Override
public void call() {
// called when the screen turns off.
Log.d("test", "unsubscribe.");
}
}).subscribeOn(AndroidSchedulers.mainThread()).debounce(600, TimeUnit.MILLISECONDS, AndroidSchedulers.mainThread())
.subscribe(new Action1<CharSequence>() {
@Override
public void call(CharSequence charSequence) {
Log.d("test", "s:" + charSequence);
}
});
I'll be very grateful for your help.
When attempting to use RxLifecycle from Kotlin in an Observable chain the compiler cannot infer types in some cases unless explicitly specified.
Consider if I have a variable tasks of type Observable<List>
If I try to use that in an observable chain:
tasks.compose(RxLifecycle.bindView(taskList)) // followed by other clauses
The compiler complains that it cannot infer the type unless I explicitly specify it as in:
tasks.compose(RxLifecycle.bindView<List<Task>>(taskList)) // followed by other clauses
I think the problem is that you are not explicitly bounding the generic type to match the compose method. You have the method returning
Transformer<T, T>
And I think that should really be:
Transformer<? super T, ? extends T>
to match the semantics of the Observable.compose method.
I tested by writing a wrapper method that uses the latter return type and it fixes the type inference.
I'd like to know why the minSDK for these libraries is 15. Seems restrictive. If I force override on my project (min 14), should I expect some things to break?
AppCompatDialogFragment is currently not supported by rxlifecycle-components
bindView
sets up its detach listener in its OnSubscribe
call, which is nicer than the pre-1.x RxAndroid ViewObservable.bindView
since it doesn't immediately set up the listener, even if not yet subscribed.
However, that means if I try to do (assume I'm calling this from the main thread):
getNetworkThing()
.subscribeOn(Schedulers.io())
.compose(RxLifecycle.bindView(fooView))
.observeOn(AndroidSchedulers.mainThread())
.subscribe(putThingOnViewFn);
it will blow up in RxBinding in its onSubscribe
since it is being called off the main thread. One workaround is to defer, like:
Observable.defer(() -> getNetworkThing().subscribeOn(Schedulers.io()))
.compose(...)
...
but this is clunky at best. I'm not sure we would want a hard dependency on RxAndroid and its main scheduler (I sometimes use a wrapped main thread scheduler that can run things immediately if already on the main thread anyway), but maybe we could have overloads in RxLifecycle that allow you to optionally pass in a subscribeOn scheduler for the detach stream?
source.takeUntil(RxView.detaches(view).subscribeOn(scheduler))
seems like it would solve the problem, since the detach stream itself would subscribe on the passed in ui thread scheduler
…/src/main/AndroidManifest.xml Error:
uses-sdk:minSdkVersion 10 cannot be smaller than version 14 declared in library [com.trello:rxlifecycle:0.3.0] …/build/intermediates/exploded-aar/com.trello/rxlifecycle/0.3.0/AndroidManifest.xml
Suggestion: use tools:overrideLibrary="com.trello.rxlifecycle" to force usage
Is there a real reason for this limitation?
We use some ListFragments in our apps and it would be nice to have an Rx component that we can extend in these cases. It appears to be a quick addition so I can make a PR with this support if that makes sense. Thanks!
Hello there,
I started using the library after migrating from the old AppObservable. I experienced some weird crash (very sporadic) due to code being executed after the fragment view was destroyed. Then i tried to tie my tasks to PAUSE events and most of them went away but once in a while i experienced the same behavior.
After some digging and debugging my code i started to realize it is something related with this or the core library (not sure which one yet).
I discovered that sometimes you get the immediately subsequent event after the desired "STOP" FragmentEvent. Ex: If i want to receive events till onPause is called, sometimes i get an event immediately after onPause was sucessfully completed.
For this reason please consider this very basic fragment code
public class BauFragment extends RxFragment {
PublishSubject<Integer> publishSubject = PublishSubject.create();
public BauFragment() {
lifecycle().subscribe(
fragmentEvent -> {
Timber.d("Fragment event is: %s - Thread: %s", fragmentEvent, Thread.currentThread());
}
);
}
@Override
public void onPause() {
Timber.d("onPause before super");
super.onPause();
Timber.d("onPause after super");
}
@Override
public void onResume() {
super.onResume();
publishSubject
.compose(RxLifecycle.bindUntilFragmentEvent(lifecycle(), FragmentEvent.PAUSE))
.observeOn(AndroidSchedulers.mainThread())
.subscribeOn(Schedulers.io())
.subscribe(
integer -> {
Timber.d("Received %d, isResumed: %s, isRemoving: %s, thread: %s", integer, isResumed(), isRemoving(), Thread.currentThread());
}
);
new Handler() {
@Override
public void handleMessage(Message msg) {
publishSubject.onNext(msg.what);
sendEmptyMessageDelayed(msg.what+1, 10);
}
}.sendEmptyMessage(1);
}
}
It should look pretty straightforward. I'm trying to publish an integer through the publishSubject every 10ms ( bug appears also with 50ms or 100ms but is more sporadic ).
The generated output (depending how much time you let the fragment running before hitting the back button) should look similar to the following. (Note it does not always happen - as I said with 10ms you've more chances to get see it)
08-14 23:21:39.846 D/BauFragment﹕ Fragment event is: ATTACH - Thread: Thread[main,5,main]
08-14 23:21:39.847 D/BauFragment﹕ Fragment event is: CREATE - Thread: Thread[main,5,main]
08-14 23:21:39.848 D/BauFragment﹕ Fragment event is: START - Thread: Thread[main,5,main]
08-14 23:21:39.848 D/BauFragment﹕ Fragment event is: RESUME - Thread: Thread[main,5,main]
08-14 23:21:39.968 D/BauFragment﹕ Received 1, isResumed: true, isRemoving: false, thread: Thread[main,5,main]
08-14 23:21:40.010 D/BauFragment﹕ Received 2, isResumed: true, isRemoving: false, thread: Thread[main,5,main]
08-14 23:21:40.018 D/BauFragment﹕ Received 3, isResumed: true, isRemoving: false, thread: Thread[main,5,main]
08-14 23:21:40.027 D/BauFragment﹕ Received 4, isResumed: true, isRemoving: false, thread: Thread[main,5,main]
08-14 23:21:40.039 D/BauFragment﹕ Received 5, isResumed: true, isRemoving: false, thread: Thread[main,5,main]
08-14 23:21:40.049 D/BauFragment﹕ Received 6, isResumed: true, isRemoving: false, thread: Thread[main,5,main]
08-14 23:21:40.060 D/BauFragment﹕ Received 7, isResumed: true, isRemoving: false, thread: Thread[main,5,main]
08-14 23:21:40.069 D/BauFragment﹕ Received 8, isResumed: true, isRemoving: false, thread: Thread[main,5,main]
08-14 23:21:40.080 D/BauFragment﹕ Received 9, isResumed: true, isRemoving: false, thread: Thread[main,5,main]
[--- Removed for clarity ---]
08-14 23:21:40.750 D/BauFragment﹕ Received 75, isResumed: true, isRemoving: false, thread: Thread[main,5,main]
08-14 23:21:40.760 D/BauFragment﹕ Received 76, isResumed: true, isRemoving: false, thread: Thread[main,5,main]
08-14 23:21:40.770 D/BauFragment﹕ Received 77, isResumed: true, isRemoving: false, thread: Thread[main,5,main]
08-14 23:21:40.780 D/BauFragment﹕ Received 78, isResumed: true, isRemoving: false, thread: Thread[main,5,main]
08-14 23:21:40.791 D/BauFragment﹕ Received 79, isResumed: true, isRemoving: false, thread: Thread[main,5,main]
08-14 23:21:40.801 D/BauFragment﹕ Received 80, isResumed: true, isRemoving: false, thread: Thread[main,5,main]
08-14 23:21:40.810 D/BauFragment﹕ Received 81, isResumed: true, isRemoving: false, thread: Thread[main,5,main]
08-14 23:21:41.130 D/BauFragment﹕ Received 82, isResumed: true, isRemoving: false, thread: Thread[main,5,main]
08-14 23:21:41.194 D/BauFragment﹕ onPause before super
08-14 23:21:41.194 D/BauFragment﹕ Fragment event is: PAUSE - Thread: Thread[main,5,main]
08-14 23:21:41.194 D/BauFragment﹕ onPause after super
08-14 23:21:41.199 D/BauFragment﹕ Received 83, isResumed: false, isRemoving: false, thread: Thread[main,5,main]
08-14 23:21:41.565 D/BauFragment﹕ Fragment event is: STOP - Thread: Thread[main,5,main]
08-14 23:21:41.570 D/BauFragment﹕ Fragment event is: DESTROY_VIEW - Thread: Thread[main,5,main]
08-14 23:21:41.570 D/BauFragment﹕ Fragment event is: DESTROY - Thread: Thread[main,5,main]
08-14 23:21:41.571 D/BauFragment﹕ Fragment event is: DETACH - Thread: Thread[main,5,main]
Besides the headaches that this caused to me, it may be a "severe" bug if you bind to DESTROY_VIEW and, as I happened to see, sometimes an event goes through to the subscriber after onDestroyView gets called -> you'll likely to get a nullpointerexception (especially if you use Butterknife.unbind())
Before you ask these are my dependencies.
// Rxjava stuff
compile 'io.reactivex:rxjava:1.0.14'
compile 'io.reactivex:rxandroid:1.0.1'
compile 'com.trello:rxlifecycle:0.1.0'
compile 'com.trello:rxlifecycle-components:0.1.0'
compile 'com.jakewharton.rxbinding:rxbinding:0.1.0'
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.