This is my first time developing in reactive paradigm world, and i started using rxjava2/rxandroid2, as based on videos I've watched and articles I've read, it seems like its better to start with 2 as 1 has so many changes that differs the library in a big scale, but now I'm having some trouble looking for something that acts like the
unsubscribe()
method of the former rxjava/rxandroid library
my goal is just quite simple
perform an API call(network operation)
listen and react on what the observable will emit (happy path)
do not listen or react when app goes to PAUSE state
or, unsubscribe on observable as soon as android goes to the pause life-cycle
, based on the resources around there is
dispose()
method of rx2, what I understand with this is that it disposes any current resources(in my case, base on what i understand, invoking this will make the observable detach itself to any observer).
but that doesn't seem to be what I'm expecting, please have a look at the ff codes:
public class MainActivity extends AppCompatActivity {
final Disposable disposable = new Disposable() {
#Override
public void dispose() {
Log.e("Disposed", "_ dispose called.");
}
#Override
public boolean isDisposed() {
return true;
}
};
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Observer<Object> observer = new Observer<Object>() {
#Override
public void onSubscribe(Disposable d) {
Log.e("OnSubscribe", "On Subscribed Called");
}
#Override
public void onNext(Object value) {
Log.e("onNext", "Actual Value (On Next Called).");
}
#Override
public void onError(Throwable e) {
e.printStackTrace();
}
#Override
public void onComplete() {
Log.e("OnComplete", "On Complete Called.");
}
};
EventsApiService.getInstance().testApi().testCall()
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.doOnDispose(new Action() {
#Override
public void run() throws Exception {
Log.e("Disposed?", "__ Dispose");
}
})
.subscribe(observer);
observer.onSubscribe(disposable);
}
#Override
public void onPause() {
super.onPause();
disposable.dispose();
}
}
I'm having this output:
03-23 09:08:05.979 3938-3938/edu.rx.study E/Disposed: _ dispose called.
03-23 09:08:13.544 3938-3938/edu.rx.study E/onNext: Actual Value (On Next Called).
03-23 09:08:13.544 3938-3938/edu.rx.study E/OnComplete: On Complete Called.
I was expecting that onNext won't be called anymore or maybe both onNext and onComplete, but that doesn't seem to be working, am i missing something here? or theres something i totally don't understand, my thinking with my code is,
"what if onNext is performing something towards a widget(UI)(Observer) and the app goes on pause state?", I don't want that UI(Observer) to react on that particular UI anymore.
Many people are right, and I admit, switching to reactive programming is quite hard especially rxjava2/rxandroid2 has a very steep learning curve.
Any help will be greatly appreciated.
You're handling incorrectly the Observer and the Disposable, the Disposable object should be handing to you by the Observable, you can't just create it by yourself, and call explicitly Observer.onSubscribe() with it, as it's not connected to the Observable and does not terminates it.
(you can also notice that Observer.onSubscribe is called twice, one by the Observable and one by you)
What you should do, is simply use the onSubscribe(Disposable d) method at your Observer to save the Disposable, which will be called automatically by the Observable and will hand you the correct Disposable object, that you can successfully terminate the network operation with it.
Another option is, to not use at all the subscribe(Observer o) method, but other overloads that takes your onNext/onError/onCompleted as parameters, and returns Disposable object, which you can dispose (unsusbcribe) with it, for terminating the network call.
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();
}
Android Studio 3.2 Canary 8
com.squareup:otto:1.3.8
io.reactivex:rxjava:1.3.7
kotlin 1.2.31
I am trying to send an event back to my Activity using the otto EventBus.
However, I am using RxJava to perform some background work and need the event to be sent after the first one completes. However, after post the event. The activity never receives it.
This event must do this on the main thread. The RxJava is on the IO thread. I am not sure what is the best way to do this:
Here is my code for the interactor that does the RxJava and EventBus post
class Interactors(private val eventBus: Bus) {
fun transmitMessage(): Completable {
return insertTransmission()
.andThen(onTransmissionChanged()) /* Send event to the activity */
.andThen(requestTransmission())
}
private fun insertTransmission(): Completable {
return Completable.fromCallable {
Thread.sleep(4000)
System.out.println("insertTransmission doing some long operation")
}
}
private fun requestTransmission(): Completable {
return Completable.fromCallable {
Thread.sleep(2000)
System.out.println("requestTransmission doing some long operation")
}
}
/* Need to send this event back to the activity/fragment */
private fun onTransmissionChanged(): Completable {
return Completable.fromCallable {
System.out.println("onTransmissionChanged send event to activity")
eventBus.post(TransmissionChanged())
}
}
}
Activity:
public class HomeActivity extends AppCompatActivity {
private Bus eventBus = new Bus();
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_home);
eventBus.register(this);
new Interactors(eventBus).transmitMessage()
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe();
}
#Override
protected void onDestroy() {
eventBus.unregister(this);
super.onDestroy();
}
#Subscribe
public void onTransmissionChangedEvent(TransmissionChanged transmissionChanged) {
System.out.println("onTransmissionChangedEvent");
}
}
And the EventBus class:
class TransmissionChanged
This the output when I run the app:
insertTransmission doing some long operation
onTransmissionChanged
I am not sure if the eventBus.post(..) is blocking. Actually this should be done in the main thread as is posting back to the Activity to perform some update in the UI.
Do you really need to mix an EventBus and RxJava? For me this introduces extra complexity without a lot of benefit to it. Your use-case seems like a perfect example to use an Rx stream, doing some work on each emission (in your case updating the UI via onTransmissionChangedEvent()).
I'd change transmitMessage() method to something like this:
fun transmitMessage(): Observable<TransmissionChanged> {
return Observable.create(ObservableOnSubscribe<TransmissionChanged> { emitter ->
insertTransmission()
emitter.onNext(TransmissionChanged()) // perform this after the long running operation from insertTransmission() has finished
requestTransmission()
emitter.onComplete() // after the long running operation from requestTransmission() has finished
})
}
I guess you need some extra data to update your UI accordingly - this is encapsulated in TransmissionChanged class - include whatever you need there. One thing to be aware of - using Observable.create() is dangerous in RxJava 1. I don't remember what was the safe way of doing so and don't have a project with RxJava 1 to experiment with ... but there was a factory method in the Observable class that could do the job safely.
Using the above, your Activity code becomes cleaner as well. There's no need for Otto anymore, as all your operations are handled via the single Rx stream.
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_home);
new Interactors()
.transmitMessage()
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(transmission -> onTransmissionChangedEvent(transmission),
throwable -> handleError(throwable),
() -> handleCompletion()
);
}
Not allowing the receiver to specify which thread it would like to receive events on is a short coming of Otto. It enforces that all calls need to be on the same thread (defaults to the main thread). It is up to the caller to get be on the correct thread. I much prefer EventBus by GreenRobot. You change which thread you want to receive on with an annotation. So, my first suggestion would be, if you are not too invested in Otto yet, is to consider using EventBus instead.
If you are not in a position to rework all your event bus code, you can post back to the main looper by allocating a Handler. It is quick and easy, but feels a little like stepping out of rx framework.
private fun onTransmissionChanged(): Completable {
return Completable.fromCallable {
System.out.println("onTransmissionChanged send event to activity")
Handler(Looper.getMainLooper()).post {
eventBus.post(TransmissionChanged())
}
}
}
If you are calling this a lot, you may want to cache the Handler and pass it into your Interactors constructor.
If you want to stick with RxJava schedulers, you can pass a Scheduler into your constructor to indicate where you want to do your background work instead of using subscribeOn. In transmitMessage, use it schedule the background ops while forcing the eventBus.post to the main thread as follows --
class Interactors(private val eventBus: Bus, private val scheduler: Scheduler) {
fun transmitMessage(): Completable {
return insertTransmission()
.subscribeOn(scheduler)
.observeOn(AndroidSchedulers.mainThread())
.andThen(onTransmissionChanged()) /* Send event to the activity */
.observeOn(scheduler)
.andThen(requestTransmission())
}
// Rest of the class is unchanged
}
in this case, you will use it in HomeActivity as follows --
new Interactors(eventBus, Schedulers.io()).transmitMessage()
.observeOn(AndroidSchedulers.mainThread())
.subscribe();
It is possible that your activity/fragment is not started/attached while posting the event so they haven't registered to the eventBus yet. By that the event was post, but there are no subscribers (or maybe there are other subscribers somewhere else).
Maybe you should use Sticky Events to make that event "awake" so your activity/fragment will still be able to handle it.
For using EventBus events as RxJava code, I do something as follows:
public class EventBusRx {
private static EventBusRx ourInstance = new EventBusRx();
public static EventBusRx getInstance() {
return ourInstance;
}
private EventBusRx() {}
public final Subject<Integer> eventName = PublishSubject.create();`
}`
And then listening to such event:
EventBusRx.getInstance().eventName
.subscribeOn(AndroidSchedulers.mainThread())
.doOnNext(o -> someAction())
.subscribe();
And for posting an event:
public void postSomeEvent(int eventValue) {
EventBusRx.getInstance().eventName.onNext(eventValue);
}
Also read about RxJava's Replay, which might be helpful for you.
Your Activity/Fragment should have this updated code:
#Override
public void onResume() {
super.onResume();
if (!eventBus.isRegistered(this))
eventBus.register(this);
}
#Override
public void onDestroy() {
super.onDestroy();
if (mEventBus.isRegistered(this))
mEventBus.unregister(this);
}
#Subscribe(threadMode = ThreadMode.MAIN)
public void onTransmissionChangedEvent(TransmissionChanged transmissionChanged) {
System.out.println("onTransmissionChangedEvent");
}
Now your code for Interactors should be like this
new Interactors(eventBus).transmitMessage()
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Observer() {
#Override
public void onSubscribe(Disposable d) {
}
#Override
public void onNext(Object o) {
onTransmissionChanged();
}
#Override
public void onError(Throwable e) {
}
#Override
public void onComplete() {
}
});
Use on next on next method to call onTransmissionChanged().
I am trying to create a repeating, background timer with RXAndroid. I have written code that executes at a specified interval in the background, but I cannot find a way to stop it.
Observer<Long> observer = new Observer<Long>() {
#Override
public void onSubscribe(Disposable d) {}
#Override
public void onNext(Long aLong) {
Log.d(LOGTAG, "Interval:" +String.valueOf(aLong));
}
#Override
public void onError(Throwable e) {}
#Override
public void onComplete() {}
};
Observable observable = Observable.interval(1, 1, TimeUnit.SECONDS)
.subscribeOn(Schedulers.io())
.observeOn(Schedulers.io());
observable.subscribe(observer);
I would like to know please:
Is this the correct approach for creating a "timer" in RXAndroid to execute code periodically in the background?
How do I stop it?
Is this the correct approach for creating a "timer" in RXAndroid to execute code periodically in the background?
Yes,
How do I stop it?
To cancel the observer in RxJava you have to use the reference of Disposable.
Which you can find in the onSubscribe(Disposable d).
onSubscribe() will call whenever you subscribe to observable. in your case it is observable.subscribe(observer).
In observer you have method public void onSubscribe(Disposable d) {} which will provide reference of Disposable.
Declare global instance of Disposable, and whenever you want to cancel call Disposable::dispose().
I have a question regarding how to unsubscribe an observable. I have two codes and I'm not really sure about which one is better.
Example 1 -> Unsubscribe the subscriber once the stream has finished:
Subscriber<String> subscriber = new Subscriber<String>() {
#Override
public void onCompleted() {
progressdialog.dissmiss();
unsubscribe();
}
#Override
public void onError(Throwable e) {
progressdialog.dissmiss();
}
#Override
public void onNext(String s) {
// do something with data
}
}
Example 2 -> Unsubscribe the subscription once the activity is destroyed:
private void test(){
Subscriber<String> subscriber = new Subscriber<String>() {
#Override
public void onCompleted() {
progressdialog.dissmiss();
}
#Override
public void onError(Throwable e) {
progressdialog.dissmiss();
}
#Override
public void onNext(String s) {
// do something with data
}
};
subscription = BackendRequest.login(loginRequest)
.subscribeOn(Schedulers.newThread())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(subscriber);
compositeSubscription.add(subscription);
}
#Override
protected void onDestroy() {
super.onDestroy();
this.subscription.unsubscribe();
}
I have to mention that my observables only will emit once, the activity should not wait for more calls from the Observable.
Which one is better?
Thanks in advance
From the two options the second one is better.
In your first example you're unsubscribing in the onComplete() method which is not needed. If you reach the onComplete() of a Subscription you don't have the responsibility of unsubscribing from it anymore.
Your second example is the correct one. The idea behind the CompositeSubscription is that you can add multiple Subscriptions to it and then clean up (unsubscribe) at once. In other words this just saves you from the need of keeping a list of Subscriptions that you need to unsubscribe from.
One tricky part using CompositeSubscription is that if you once unsubscribe it, you can NOT use it again. You can check the documentation for the compositeSubscription.add() method for details why. In short - it will directly unsubscribe the Subscription you're trying to add. That's been a deliberate decision (you can read more about it HERE).
Coming back to your example, calling unsubscribe() in onDestroy() of the Activity is fine and will save you from memory leaks. Regarding your comment that problems occur when you call your test() method multiple times - I'd say your problem is somewhere else. Maybe your use-case shouldn't allow to call it multiple times, maybe you should cleanup old data before using the newly received one, etc. Perhaps if you have explained in details what kind of problems you face we could help more. But as far as the CompositeSubscription is concerned - you're using it and unsubscribing from it correctly!
There is no need to unsubscribe in onCompleted. Take a look at The Observable Contract
When an Observable issues an OnError or OnComplete notification to its
observers, this ends the subscription. Observers do not need to issue
an Unsubscribe notification to end subscriptions that are ended by the
Observable in this way.
On the other hand, you definitely should unsubscribe in onDestroy in order to prevent memory leaks.
I think that depends on your needs. If the activity won't wait for any other calls, I suppose you could unsubscribe inside onCompleted().
I always unsubscribe in onDestroy()
#Override
protected void onDestroy() {
super.onDestroy();
if (subscription != null) {
subscription.unsubscribe();
}
}
EDIT: take a look at http://reactivex.io/RxJava/javadoc/rx/subscriptions/CompositeSubscription.html
private CompositeSubscription mCompositeSubscription = new CompositeSubscription();
private void doSomething() {
mCompositeSubscription.add(
AndroidObservable.bindActivity(this, Observable.just("Hello, World!"))
.subscribe(s -> System.out.println(s)));
}
#Override
protected void onDestroy() {
super.onDestroy();
mCompositeSubscription.unsubscribe();
}
I have an Observable and subscribe to it. I need to not miss any emitted result, so I use onBackpressureBuffer like following:
Observable<Data> observable = observable.onBackpressureBuffer();
if (BuildConfig.DEBUG)
{
observable
.subscribeOn(HandlerScheduler.from(dataManager.getBackgroundHandler()))
.observeOn(HandlerScheduler.from(dataManager.getBackgroundHandler()))
.subscribe(new MeasuringSubscriber(...));
}
// Here is the real observer that I need in my app
observable
.subscribeOn(HandlerScheduler.from(dataManager.getBackgroundHandler()))
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Observer<Data>()
{
#Override
public void onCompleted() {
}
#Override
public void onError(Throwable e) {
}
#Override
public void onNext(Data data) {
}
});
The MeasuringSubscriber is a custom subscriber that just logs how long a task needs, that's all.
Problem
If I add the MeasuringSubscriber, the subscribers do not work anymore and never emit a result. Why? And how can I make that working?
EDIT - NEW PROBLEM
Currently it's working, but the MeasuringSubscriber is somehow blocking, meaning, first all items are emitted one by one to the MeasuringSubscriber and only afterwards all items are emitted one by one to the main subscriber... Any ideas what could cause that?
I have a solution for that - I can extend my main observalbe from the MeasuringObservable - but I rather would like to know why this happens and how to avoid this...
I tried using publish + connect, but still it does emit all items to the first subscriber before emitting them to the second one...