I am using RxJava for listening an event of Bluetooth adapter. The bluetooth listener may be active for long duration like for few hours. My question is how long the emitter is valid and I can send events though it?
My code is:
Class BlutoothObserver{
ObservableEmitter emitter;
BlutoothObserver(){
...
starListeningBluetoothDevice();
}
public Observer getObserver(){
return Observable.create(e -> {
emitter =e;
});
}
public void bluetoothCallback(){
...
emitter.onNext();
...
}
}
It should stay alive as long as you do not call onComplete() on emitter.
You will want to be careful with how you define getObserver() though. Right now you are creating a new Observable every time. So if you called getObserver() twice in a row it would return different Observable instances and only the last one returned would be of use. I would just create a field for the Observable and set it at construction time.
Related
I am trying to implement the RxJava event bus, where my use case is to get triggered when subscribed and when the event is sent. But with my code even when I send the event once, I am receiving multiple events. It is working fine for the first time, it is behaving weirdly from the second time I login into my application. i.e; For the first time desired code implemented once, for the second time it implemented two time and so on.
public class RxBus {
public RxBus() {
}
private PublishSubject<String> bus = PublishSubject.create();
public void send(String str) {
bus.onNext(str);
}
public Observable<String> toObservable() {
return bus;
}
}
The code to subscribe RxBus is below:
public void sendEvents(){
rxBus.send("Trigger event");
}
public void startListener(){
rxBus.toObservable().subscribe(str -> {
//do action//This is executing multiple lines
});
}
In the above code, even though when the sendEvents() is executed once the line containing "do action" is executing multiple times. So, is something I am doing wrong here. When I went through some blogs they are asking to unsubscribe the subscription when we visit that screen a second time. But how can I unsubscribe from that?
Help here is greatly appreciated!
Easy solution is to declare a field:
SerialDisposable busDisposable = new SerialDisposable();
Modify you startListener method:
public void startListener() {
busDisposable.set(rxBus.toObservable().subscribe(str -> {
// ...
}));
}
In that way, when you add new subscription the previous one will be disposed, so you will end up with only one subcription at a time. This is good if your startListener call is not determined by the lifecycle. (Remember to call busDisposable.dispose() when you no longer want to recieve events. )
But if you call your startListener in onResume/onStart/onCreate, you should better use Disposable instead of SerialDisposable and simply call stopListener method in onPause/onStop/onDestroy.
public void stopListener() {
busDisposable.dispose();
}
I have a function that creates Observable:
void getData() {
Observable.create(emitter -> {
// call webservice
.......
emitter.onNext(myData);
}).subscribe(data -> {
// Process data here
});
}
I don't want to use Disposable here. I think the observable is local variable, so it will be released after the function is done.
Is the observable released automatically after I call getData() function?
Observable will automatically dispose they called onComplete() or onError()
Ex: You have a method to load exactly data from 10 files Observable<String> loadFiles() which return Observable.create(). Then every time you want to emit value you call e.onNext(), after count 10 times you will call e.onComplete() to mark that your Observable has finish it's work, then it will auto dispose.
You only need to call dispose() method to indicate that the Subscriber is no longer interested in any of the Observables it is currently subscribed to. Those Observables can then (if they have no other interested observers) choose to stop generating new items to emit.
Call dispose() when activity stopped to make sure that no more emission will come after that. So it's a good practice because it can prevent memory leaks and waste of resources, network calls.
Observables do not dispose them-selfs.
It's a good practice to dispose your observable to avoid memory leaks and crashes of your app.
you either use disposable.dispose() or compositeSubscribtion.clear().
I have made a simple test and after I exited the app(back btn) observable continued to emit data.
btn.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
Observable.create(emitter -> {
for (; ; ) {
emitter.onNext("data");
Thread.sleep(3000);
}
}).subscribeOn(Schedulers.io()).observeOn(AndroidSchedulers.mainThread()).
subscribe(data -> {
Log.d(tag, (String) data);
});
}
});
OUTPUT :
onStart() is called
onResume() is called
data
data
onPause() is called
onStop() is called
data
data
Keeping it simple and short, how to subscribe to an observable in another subscriber's onNext() method so that we can only make nested subscription when the current observable completes its execution. Both the subscriptions will be made on separate threads and the requirement is that the first thread must complete its execution before the second thread is started.
makeObservable()
.subscribeOn(Schedulers.newThread())
.subscribe(new Subscriber<User> {
#override
void onNext(User user){
//do something
//make another subscription here
});
Don't make a new subscription, return another Observable and subscribe to it.
apiCall()
.subscribeOn(<scheduler>)
.observeOn(<scheduler>)
.flatMap(new Func1<User, Observable<Something>() {
#Override
public Observable<Something> call(User user) {
return Observable.just(<example>);
}
});
edit: when the api call returns, flatMap will intercept the stream, and from there, either return an Observable or call a function that returns an Observable (ie another api call).
If they're both emitting the same items, use Observable.concat(), which subscribes to observable N+1 after observable N completes. But it might be worth it to describe your use case in more detail.
Edit: You should be able to do something like:
userClient
.saveUser(user)
.flatMap(userSaveResult ->
userClient
.saveUserDetails(userSaveResult.id, seuser.getDetails))
.onError(...)
I am looking for a way, hopefully using RxJava for consistency, to monitor the progress of multiple subscribers that may be fired at different times. I am aware of how to merge or flatMap subscribers together when they are all fired from one method but I am unaware of a way to do it when they are fired at different times from different methods.
For example, if I have 2 long running tasks attached to button presses. I push button 1 and fire off the observable/subscriber, half way through running I push button 2 to fire off the second observable/subscriber.
I want to enable a button when no tasks are running and disable it when one or more tasks are running.
Is this possible? I am trying to avoid setting instance variable flags as well.
I would use a separate BehaviorSubject and scan to monitor execution status. This is quite similar to an instance variable, but probably it can inspire you to a better solution. Something like this:
private final BehaviorSubject<Integer> mProgressSubject = BehaviorSubject.create(0);
public Observable<String> firstLongRunningOperations() {
return Observable.just("First")
.doOnSubscribe(() -> mProgressSubject.onNext(1))
.finallyDo(() -> mProgressSubject.onNext(-1)));
}
public Observable<String> secondLongRunningOperations() {
return Observable.just("Second")
.doOnSubscribe(() -> mProgressSubject.onNext(1))
.finallyDo(() -> mProgressSubject.onNext(-1));
}
public Observable<Boolean> isOperationInProgress() {
return mProgressSubject.asObservable()
.scan((sum, item) -> sum + item)
.map(sum -> sum > 0);
}
Usage will be like this:
isOperationInProgress()
.subscribe(inProgress -> {
if (inProgress) {
//disable controls
} else {
//enable controls
}
});
With this approach you can have any number of long running operation and you do not have to fire them all. Just don't forget to call doOnSubscribe and finallyDo.
PS. Sorry, I didn't test it, but it should work.
To make this possible, let both long running operations emit an onNext event on a PublishSubject. Combine both Subjects with a zip or combineLatest function and subscribe to this. Once the combine function receives an event, this means that both Subjects have emitted an onNext event, thus both long running operations have finished and you can enable the 3rd button.
private PublishSubject<Boolean> firstSubject = PublishSubject.create();
private PublishSubject<Boolean> secondSubject = PublishSubject.create();
#Override
public void onStart() {
super.onStart();
subscribeToResult();
}
private Observable<Integer> firstOperation() {
return Observable.just(100)
.delay(1000) // takes a while
.subscribe(tick -> firstSubject.onNext(true));
}
private Observable<Integer> firstOperation() {
return Observable.just(200)
.delay(1000) // takes a while
.subscribe(tick -> secondSubject.onNext(true));
}
private void subscribeToResult() {
Observable.zip(
firstSubject,
secondSubject,
(firstResult, secondResult) -> return true
).subscribe(
tick -> thirdButton.setEnabled(true)
)
}
Definitely take a look at the RxJava combine functions.
I'm trying to make a simple "button debouncer" which will count filtered clicks and display it thru a TextView. I want to filter rapid/spam clicks in a way that clicks with less than 300ms time-gap in-between are ignored.
I did my research and stumbled upon Rx's awesome debounce() which in theory should do the exact thing I wanted..
..or so I thought. As the app seemed to only register the first click; the counter won't increment no matter how long I tried to wait.
Here's a piece of my code:
...
RxView.clicks(mButton)
.debounce(300, TimeUnit.MILLISECONDS)
.subscribe(new Subscriber<Object>() {
public int mCount;
#Override
public void onCompleted() {
}
#Override
public void onError(Throwable e) {
}
#Override
public void onNext(Object o) {
mText.setText(String.valueOf(++mCount));
}
});
...
What am I doing wrong? I've tried to run the thing without debounce() and it worked flawlessly (the counter will increment everytime the button got clicked).
Thanks in advance!
Note the following in the documentation on the debounce operator:
This variant operates by default on the computation Scheduler (...)
Or, code-wise, this currently happens:
public final Observable<T> debounce(long timeout, TimeUnit unit) {
return debounce(timeout, unit, Schedulers.computation());
}
As a result, the subscriber's callbacks are invoked on that same computation scheduler, since nothing is explicitly instructing otherwise.
Now, attempting to update a view (that's what's happening in onNext()) from any other thread than the main/ui thread, is a mistake and it will lead to undetermined results.
Fortunately, the remainder of the quote above provides the solution too:
(...) but you can optionally pass in a Scheduler of your choosing as a third parameter.
This would lead to:
RxView.clicks(mButton)
.debounce(300, TimeUnit.MILLISECONDS, AndroidSchedulers.mainThread())
.subscribe(...);
Alternatively, you can still let the debounce happen on the computation scheduler, but receive the notifications on the main/ui thread:
RxView.clicks(mButton)
.debounce(300, TimeUnit.MILLISECONDS)
.observeOn(AndroidSchedulers.mainThread())
.subscribe(...);
Either way will ensure that the notifications are received on the main/ui thread and thus that the view is updated from the correct thread.