Can someone confirm if calling unSubscribe on the Observable is the right way to cancel a request?
Annoyingly, the Nexus 7 issues duplicate network requests and the first call needs to be thrown away as the calling Activity has been destroyed.
I got round this by checking the callback (my own) is not null before trying to use it.
Unsubscribing however, seems a better solution but could not find any information if this is the correct way to go.
I noticed that Retrofit would throw an internal error (InterruptedException) but that did not bubble up to my error callback - a good thing!
To my knowledge, yes unsubscribing is a better way. If you haven't got RxJava already installed as a dependency I recommend you do so, it makes managing Observables much easier. RxJava is also totally compatible with Retrofit.
Then you can unsubscribe from a Subscription in the onDestroy method as so:
#Override
public void onDestroy() {
if (bookingSubscription != null) {
bookingSubscription.unsubscribe();
}
super.onDestroy();
}
Use of the onDestroy method will be useful when applied to your scenario too. You can unsubscribe from Observables in there.
Related
In RxJava 2 subscribe block is giving warning when after subscribing disposable is not used.
Example in Kotlin:
button.clicks().subscribe(...)
It is useful for asynchronous stuff or something that needs to be garbage collected in subscribe blocks, but in many cases it is just plain button click -> navigation stuff.
Question 1
Is it safe to NOT dispose pure view events, because it will get garbage collected with Activity itself or should I explicitly dispose of it, like
button.clicks().subscribe({startActivity(...)})
Question 2
What to do with those warnings? Let them be? Dispose just for the sake of getting rid of warnings?
Playing around with WorkManager recently as I try to move out of using old solutions for scheduling tasks in the background.
I was wondering though, is there any sort of feature for the Worker to post events during it's task? I normally would use some sort of an event bus like Otto wherein I'd register the bus to a subclass that posts events whenever the task reaches a certain milestone.
If there is no way for workers to post such events during a task, is there a way for us to register the eventBus into the worker so we can do this?
There were no takers so I ended up finding a workaround.
Instead of posting events via an eventbus, we can observe a live WorkStatus.
There are a few ways to observe work statuses and in my way, I've decided to observe base on the tag of my worker. Below is a snippet of how I went to do this:
WorkManager.getInstance().getStatusesByTag("MyWorker")
.observe(this, new Observer<List<WorkStatus>>() {
#Override
public void onChanged(#Nullable List<WorkStatus> workStatuses) {
doSomething();
}
});
Every time something changes, I look through all of my work statuses and decide what to do. You can also grab Data values here if you ever required them.
I noticed that in the compose method this observable could be binded to an activity lifecycle to do the desired unsubscription, the problem is that i am doing this in a service, so i cant use this library, or at least i dont know how to get this service lifecycle and bind the observable to it:
return bleDevice
.establishConnection(false)
.takeUntil(disconnectTriggerSubject)
//.compose(RxLifecycle.bindUntilEvent(MISSINGACTIVITYLIFECYCLE, ActivityEvent.DESTROY))
.doOnUnsubscribe(this::onDestroy)
.compose(new ConnectionSharingAdapter());
i am using RxandroidBle to scan numerous devices used as interface of for a sensoring system, all goes fine, i do this in a background service, the problem is when i stop the service i cant unsuscribe to that broadcast, i used the example provided in the repo. (UPDATED)
There are several possibilities to tackle this problem:
Unsubscribe by hand (my favourite)
Assign the Subscription resulting from .subscribe() to a property and then in service onStop() just call:
public void onStop() {
if (subscription != null) {
subscription.unsubscribe();
subscription = null;
}
}
If you have more than one Subscription to unsubscribe from you could check out what is a SerialSubscription or CompositeSubscription which may be useful.
Use disconnectTriggerSubject
Making the disconnectTriggerSubject to accept any value would make the RxBleDevice.establishConnection() to be unsubscribed so the connection will get closed. The downstream may not be aware that it should also unsubscribe. One could pass a special Throwable to the subject which would be passed down to all subscribers.
Just make sure that the subscribers would treat the Throwable passed differently than other
Implement RxLifecycle callbacks for your Service
Probably most tedious path to take. Especially when you found out that RxLifecycle is not a perfect solution
I found a few articles talking about how RxJava/RxAndroid can replace event busses (such as otto)
https://lorentzos.com/rxjava-as-event-bus-the-right-way-10a36bdd49ba#.7a4619qva
https://medium.com/mobiwise-blog/use-rxjava-instead-of-event-bus-libraries-aa78b5023097#.ew28h2urf
A quote from the first article:
Otto from Square got officially deprecated the previous days. In the Android world we can cheer now something like “EventBusses are dead long live RxJava”.
There is one thing I am missing though:
One of the perks of event buses is that they help a lot with the Activity lifecycle in that you don't need to manage registering/unregistering to callbacks manually (and thus avoiding memory leaks easily)
Example flow:
Activity subscribes to an event for getting songs (say SongsAvailableEvent)
We request songs (we make a network request)
We change the device's orientation mid-request
The Activity dies and a new one is built, that is also subscribed to the SongsAvailableEvent
The new activity gets the event and updates the UI, and the old Activity (which is now dead) does not get the event (yay!)
The articles above make it look like this flow is "solved" by RxAndroid/RxJava, but using Rx you still need to subscribe/unsubscribe on an Observable manually when you change the device's orientation. Moreover, if I want to "reuse" the request made in an Observable, I need to somehow persist it so that I will subscribe on that same Observable in the new Activity (I'm not quite sure how to do that, but it is not the point :) ).
My question is: is this problem easily solvable with pure RxAndroid/RxJava, or do I still need to use Rx with an event bus / extend Rx using something like RxLifecycle (which complicates things since I do not manage my Observables in the presentation layer)?
Your Activity's onDestroy can always call unsubscribe.
As for making things work to reuse request- Look into Loaders and LoaderManager. EventBus and RxJava to solve that was never needed.
I would venture to say that there isn't any way out of the fact that at some point in the chain, the Observable has to be tied to the lifecycle of some Android platform object, such as an Activity. Also, because you have not mentioned it as a partial solution, I assume you are avoiding using retained Fragments. If you are creating and holding a reference to the Observable only within your Activity, it is not possible for the results of a request in-flight to survive destruction of the Activity and be automatically subscribed to the new one. In addition, at some point, either during an orientation change, or the Activity finishing in the middle of a network request, your Observable will leak a reference to the Activity (via its subscribe() callback) if it is not unsubscribed on the Activity's onDestroy().
I have found RxLifecycle to be simple to use. My base Activity class has a method on it:
public <T> Observable.Transformer<T,T> bindLifecycleOnMainThread() {
return o -> o.compose(lifecycleProvider.bindToLifecycle())
.observeOn(AndroidSchedulers.mainThread());
}
lifecycleProvider is created as per the instructions for RxLifecycle, depending on how you create your provider. This particular implementation uses bindToLifecycle() rather than specifying an explicit lifecycle event, so its use is contextual. Calling it during onResume will cause it to end on onPause. Calling it during onStart will cause it to end on onStop. Calling it other other times will cause it to end on onDestroy. Since this subscription will be updating the UI, it must only be observed on the UI thread.
This can then then used in the Activity as follows:
yourObservable.compose(bindLifecycleOnMainThread())
.subscribe(event -> handleEvent(event));
Now, where does this observable come from? Well, there's still no magic, and if you want an Observable to have a longer lifespan than the Activity, that means the Observable must be held by a component that lives longer than the Activity. There are many, many ways to do this, but your particular use case maps well to the new ViewModel library included in the Android Architecture framework. If you were to use ViewModels, your ViewModel would have a method that begins the network request, and would have a PublishSubject or PublishRelay that would emit SongsAvailableEvent objects (though I recommend exposing it to your Activity as only an Observable<SongsAvailableEvent>, not a Subject, for good encapsulation!). Your ViewModel would make the network call and forward the results to your Subject.
Finally, your Activity, when created, will immediately get its ViewModel from the ViewModel registry and subscribe to the Observable<SongsAvailableEvent> (which is a Subject/Relay) exposed by the ViewModel, and then bind it to the Activity's lifecycle, as in the example above. The ViewModel will survive any orientation changes of the Activity, and therefore so will the observable. The Observable will then never attempt to deliver an event to a destroyed Activity and the new Activity will immediately begin listening for events.
I believe this strategy promotes good encapsulation, since the Activity does not concern itself with how the network request gets made, and does not concern itself with how the source Observable is created. The only way that the Activity manipulates the Observable is by choosing what happens when it receives an event, and binding the subscription to the lifecycle of the Activity.
This can be endlessly tweaked and refined by composing your Observables but this should get you on the way.
I did a reasonable amount of research and can not find the answer I need.
What I DO know: When I attach a ValueEventListener to a database reference, I know I need to remove it later (finding that out the hard way now with some massive memory leakage.
What I DON'T know: Do I also need to detach all other listeners? (This is to include Firebase Database, Storage, and Auth, the three APIs I am using)
Example:
UploadTask uploadTask = ref.putFile(uploadFile);
uploadTask.addOnFailureListener(new OnFailureListener() {
//#Override code here
}).addOnSuccessListener(new OnSuccessListener<UploadTask.TakeSnapshot>() {
//#Override code here
}).addOnProgressListner(new OnProgressListner<UploadTask.TakeSnapshot>() {
//#Override code here
};
I think that's enough to show you the point of what I mean. This is how my actual code is currently structured.
Questions:
Do I need to remove all of those listeners just in case the activity
is terminated (system decision, phone dies, whatever) before that
callback happens?
Can I bundle them up somehow and terminate them all three at once
because I have like 30 of these in my code and really don't feel
like restructuring all of it in order to assign all these listeners
to variables JUST so I can pass them into the
"removeBlahBlahBlahListener(listenerVariable)" over and over.
Slightly off topic, but, I am too lazy to move all my code from
onCreate to onStart... is is bad practice for me to remove all these
listeners, finish things up, call finish() (or whatever it is that
kills off an activity, although I guess this is not guaranteed) and just recreate the activity from scratch?
It's a small simple app so the overhead of re-creating the activity
is no biggie. Just curious what is "right".
I imagine this is just a result of poor planning and lack of knowledge (I only program for fun, not for a job unfortunately) so if I have to take the hard route I guess it's a learning experience, right?
Auto unregistering listeners when an activity stops is a feature on the class "Task" in android and its derived classes (StorageTask).
This means you can do something like this:
UploadTask uploadTask = ref.putFile(uploadFile);
uploadTask.addOnFailureListener(thisActivity, new OnFailureListener() {
//#Override code here
}).addOnSuccessListener(thisActivity, new OnSuccessListener<UploadTask.TaskSnapshot>() {
//#Override code here
}).addOnProgressListner(thisActivity, new OnProgressListner<UploadTask.TaskSnapshot>() {
//#Override code here
};
You can also do this with Task objects returned from the realtime Database such as setValue as in:
databaseReference.setValue("newValue").addOnSuccessListener(thisActivity, ...)
So to answer your questions directly:
Use the activity scoped version to automatically unregister the listeners on activity stop. Note that for storage, you can query running operations when your activity starts using StorageReference.getActiveUploadTasks and StorageReference.getActiveDownloadTasks and re-subscribe.
You shouldn't need to unsubscribe manually if using scoped listeners. I do not know of a way to batch unsubscribe to non-task based listeners.
Well I'm not sure how you can guarantee that the OS will always kill your task instead of stopping/starting it again -- and how your finish code will be guaranteed to run. I would advise you move the code to onStart