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
Related
Suppose I have
Disposable disposable = signOutUser()
.subscribe((Response<ResponseBody> response) -> {
if (response.isSuccessful()) {
Intent intent = new Intent(view.getContext(), SignInUserActivity.class);
intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_NO_HISTORY);
view.getContext().startActivity(intent);
((FragmentActivity) view.getContext()).finish();
}
}, (Throwable ex) -> {
Log.e(TAG, "signOutUser: " + ex.getMessage());
});
where signOutUser() returns Single<Response<ResponseBody>>. When signOutUser() is successful, there is an Intent and the current activity is finished(). Otherwise, it fails, possibly due to network error, so there is no intent and the user stays on current activity.
Since this isn't something to observe (it's a one time event, success or fail), and IF the user successfully logs out, then onDestroy will be called which calls compositeDisposable.clear() which clears all the disposables. So then I'm adding a Disposable and immediately that Disposable is being disposed or cleared.
My question is, do I event need to use Composite Disposable? Do I immediately call disposable.dispose() after subscribe? Do I set anything to null? Do I not use Single?
Do I event need to use Composite Disposable?
Yes, you should always use composite disposable (or normal Disposable), and unsubscribe from it when the time comes (onDestroy/onStop whathere you need). The reason for it is that the network call may be finished after you have closed activity, which will result in memory leaks or even crashes (because context will be null).
Do I immediately call disposable.dispose() after subscribe?
No, because this would result in the call to never return a result. If you dispose immediately after calling subscribe, you will never get a response from it. Read about what happens after you dispose an observable.
Do I set anything to null?
No need to. If your single has finished, you don't have to do anything about it. And there won't be any problems that it is still in CompositeDisposable (even if you call dispose on it). Actually, after the Single is finished, RxJava will dispose the observable itself to safe some memory.
Do I not use Single?
Yes, this is perfect situation to use it. You want to perform a single request, no need to use Observable.
Yes, you should use a disposable. Consider the case when the response is received from your API call, but the context is gone for whatever reason. Then, all your code where you are getting the context and calling methods on it would cause an NPE. Disposing properly of this Single would help you avoid this crash.
In my application, some Activities get information from the server and I need to ensure that the connection was already established before trying to retrieve the data. I'm using a BehaviorSubject to notify the subscribers when the connection is established, so the Activity can load the data.
The issue is that more than one activity in the same flow have the same behavior. In our API we must call connect() in the onStart() and disconnect() in onStop, but if the user goes the another Activity that also uses connection, there's no need to recreate the connection, we could use the same.
At the moment, I'm implementing in the following way:
When you call connect() it returns an BehaviorSubject that will be subscribed from the calling class
On method disconnect(), it only actually disconnects if there is no observers in the BehaviorSubject, indicating that no Activities are waiting for the response.
The calling class must dispose the Observable before calling the disconnect(), otherwise the method hasObservers() will never return false
#CheckResult
#Override
public BehaviorSubject<Boolean> connect() {
if (!connectionManager.isConnected()) {
connectionManager.connect(TIMEOUT);
}
return mSubject;
}
#Override
public void disconnect() {
if (connectionManager.isConnected() && !mSubject.hasObservers()){
connectionManager.disconnect();
}
}
In my previous implementation, I was using listeners to achieve this. Every time connect() is called, it must receive the listener as a parameter that will be added in a Array of listeners and later notified one by one when the connection was established.
And every time disconnect() is called it also must receive the listener as a parameter to be removed from the list. The connectionManager.disconnect() will only be called if the Array of listeners is null, indicating that no Activities are waiting for the response.
Is there a better way to handle this?
Where you create your subject, you can specify what needs to happen when someone subscribes/unsubscribes:
subject.doOnUnsubscribe(()-> {
if(!subject.hasObservers()) {
closeConnection();
}
});
subject.doOnSubscribe(() -> {
openConnectionIfNotOpen();
});
This way, you can get rid of the disconnect() call, you just have to unsubscribe
I'm trying to migrate an AsyncTask that sends messages to the server, to use RxJava. Roughly, the task does the following:
1) Creates a message that will be sent (persists to the database)
2) Shows the message to the user (state 'sending')
3) Sends the message to the server (code snippet below)
4) Marks the message as sent or failed (persists to the database)
5) Updates the UI
I've created the required Rx chain which partially looks like this:
public Observable<Message> sendMessage(Message message) {
return mApiClient.sendMessage(message)
.doOnNext(sentMessage -> mDatabase.synchroniseMessage(sentMessage))
.doOnError(e -> {
message.setState(FAILED);
mDatabase.synchroniseMessage(message));
})
.onErrorReturn(e -> Observable.just(message));
When I subscribe to the above, I get a Disposable. Normally I'd add it to the CompositeDisposable object and clear that object then the user has moved to a different view (i.e. fragment). However, in this case, I need to keep running this task to make sure the local database is updated with the task results accordingly.
What would be the most appropriate way to handle this situation? I could simply not add the Disposable into my CompositeDisposable object and therefore it wouldn't be unsubscribed from, but it feels like it could cause issues.
P.S. Showing updates to the user is handled through observing the data in an SQLite table. These events are triggered by the synchroniseMessage method. This is a different subscription which I will simply unsubscribe from, so it's not part of the problem.
One disposes of Disposable as soon as he is no longer interested in it.
In your case you are still interested in the stream regardless user navigates to another screen or no, which means you cannot unsubscribe from it. Which means you cannot add it to CompositeDisposable.
This will result in a situation, when your Activity cannot be garbage collected, because of a implicit reference to it from your Subscription, hence you are creating a memory leak situation.
If you have such a use case, I think you have to perform that request on a component, which will be activity lifecycle independent, like Service.
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.
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.