Make Single Observable emit more than once - android

When I receive a push notification, I add the notification payload to my DB in the following way:
personObject.insertObjectIntoDb(searchResult, value, oneOnOneChannel).observeOn(Schedulers.computation()).subscribe(insertSinglePersonSubscriber);
I have a Subscriber instantiated in the onCreate() of my activity.
insertSub = new Subscriber<Long>() {
#Override
public void onCompleted() {
Log.d(TAG, "onCompleted: insertSub complete");
}
#Override
public void onError(Throwable e) {
Log.d(TAG, "onError: insertSub error");
}
#Override
public void onNext(Long aLong) {
Log.d(TAG, "onNext: insertSub next");
}
};
On the first invocation of personObject.insertObjectIntoDb(), the onComplete() of insertSub is called, just as expected.
This would mean the now the subscription has ended and the subscriber gets unsubscribed.
However, I don't want to stop the subscription and want the insertSub callbacks to be called every time there's a new push notification.
I've been reading about share() for one observable and multiple subscribers but that's not what I want. I read about replay() but it the observable never stops emitting (oops). Changing Subscriber to Observer also didn't help and on more thorough reading I found that Subscriber inherits from Observer and the same rules apply (apart from the fact the with a subscriber we need to unsubscribe.
I am not sure if the observer observable stops emitting (after emitting once). How do I make the observable emit multiple times, whenever there's a new notification?
Also, what's the best way to re-establish the subscription?

First of all, according your description it seems that you don't have some basic concepts completely figured out.
Observers don't emit but rather receive emissions.
Singles can't emit more than once. They were designed to emit only one event and complete. If you don't want this kind of behavior, you need to use some other Observable type.
But Single's are not a problem here. To compose a stream that behaves like this, you need to think one level above. If you design a stream that receives signals from push notifications, you can react to each of them by subscribing to your Single and forward its emission back to the main stream of notifications. That way, you funnel all your emissions into one Observer, just like you described. This can be easily achieved with flatMap operator.
notificationStream
.flatMap(notificationPayload ->
personObject
.insertObjectIntoDb(/* Extract arguments from payload */)
.subscribeOn(Schedulers.computation())
)
.subscribe(insertSinglePersonSubscriber)
notificationStream can be created either by using some library designed for it ( i.e. https://android-arsenal.com/details/1/3546 ), writing it yourself or using a Subject. The easiest way is definitely third one, although not cleanest. You just create a subject and call its onNext method right in the place where you subscribe to insert object Single right now.
When composing the stream to insert a value, I changed your observeOn operator to subscribeOn as I guess that you don't completely understand what each of those operators do.
The observeOn operator switches the scheduler on which are emissions handled from that point of the stream.
On the other hand, subscribeOn operator instructs observable to produce items on a particular scheduler. As I hope I can assume that you do the "heavy lifting" in the producing the emission in the Single itself -- that would be the insert itself -- you need to subscribe on a computation scheduler, not observe on it. If I am wrong about this and you need to do computation work in the observer, then add observeOn operator after flatMap on the notification stream.
RxJava is awesome, keep learning. But theoretical knowledge is important :-)

What about use relay. Relay is a subject except without the ability to call onComplete or onError
https://github.com/JakeWharton/RxRelay
Here you can see a practical example
https://github.com/politrons/reactive/blob/master/src/test/java/rx/relay/Relay.java

Answering this as I found a solution to the overall problem.
Basically, I wanted to make a notificationStream as #koperko mentioned in his answer. But he suggested creating a Subject (PublishSubject if I am not wrong).
But that solved only half of my problem. What I wanted to do was to take have a notification Stream that adds the notification to my DB and after it's inserted, update the UI by fetching the last inserted notification.
personObject.insertObjectIntoDb(person)
.observeOn(Schedulers.computation())
.switchMap(new Func1<Long, Observable<PersonObject>>() {
#Override
public Observable<PersonObject> call(Long aLong) {
Log.d(TAG, "call: inserted into DB with ID " + aLong);
Log.d(TAG, "call: Now fetching this item from the DB");
return personObject.getPersonById(aLong).observeOn(Schedulers.computation());
}
}).subscribe(getSinglePersonFromDBSubscriber);
This not only saved me from having to create a Subject, but also saved me from worrying about trying to make Single's emit more than once, which as #koperko mentioned was incorrect. switchMap() was what I was looking for.

Related

Is it possible to implement an operator like delay but that also delays errors?

I'm trying for some time now to implement an extension function (just becuse it's easier to me) that is capable of delaying both normal item emissions and errors. The existing delay operators only delays normal item emissions, errors are delivered ASAP.
For context, I'm trying to immitate an Android LiveData's behavior (kinda). LiveDatas are a observable pattern implementation that is lifecycle aware. Their observers are only notified if they are in a state where they can process that emission. If they are not ready, the emission is cached in the livedata and delivered as soon as they become ready.
I created a BehaviourSubject that emits the state of my Activities and Fragments when it changes. With that I created a delay operator like this:
fun <T> Flowable<T>.delayUntilActive(): Flowable<T> = delay { lifecycleSubject.toFlowable(BackpressureStrategy.LATEST).filter { it.isActive } }
and then use it like this
myUseCase.getFlowable(Unit)
.map { it.map { it.toDisplayModel() } }
.delayUntilActive()
.subscribe({
view.displaySomethings(
}, { }).addTo(disposables)
So even if myUseCase emits when the view is not ready to display somethings, the emission won't reach onNext() until the view does become ready. The problem is that I also want the view to displayError() when onError is triggered, but that too is lifecycle sensitive. If the view isn't ready, the app will crash.
So I'm looking for a way to delay both emissions and errors (onComplete would be good too). Is this possible?
I tried some things with zip, onErrorReturn, delay inside delay, but nothing seemed right. I'd be equally unimpressed if this had a really easy solution I'm overlooking, or is impossible. Any ideas are welcome.
Bonus: any better way to do that for Single and Completable too? currently I'm just converting them to flowable.
Thanks in advance!
You can handle the error via onErrorResumeNext, then taking the same error and delaying it via delaySubscription until your desired signal to emit said error happens:
source
.onErrorResumeNext({ error ->
Observable.error(error)
.delaySubscription(lifecycleSubject.filter { it.Active } )
})

Using Consumer interface of Reactivex

I'm new to ReactiveX. I was learning it from reading source-code. Everything was so clear but suddenly I got this word named "Consumer" which was an Interface. It was used in place of Observer.
Can someone let me know what it exactly does?
I followed several links but they just all said just one statement Consumer is a functional interface (callback) that accepts a single value.
I want to know the exact working of it.
What is it?
Why do we need it?
How do you use it?
Does it take the place of Observer? If YES, how and why?
Consumer is a simple Java interface that accepts variable of type T. Like you said it is used for callbacks.
Example:
import io.reactivex.functions.Consumer;
Flowable.just("Hello world").subscribe(new Consumer<String>() {
#Override public void accept(String s) {
System.out.println(s);
}
});
Why does it work? How can we use a Consumer instead of an Observer?
RxJava simply creates an Observer, passes the Consumer to it an it gets called in onNext
Update
You call Observable.subscribe(Consumer onNext)
Observable.subscribe(Consumer onNext, Consumer onError, Action onComplete, Consumer onSubscribe) gets called
LambdaObserver is created
LambdaObserver is a kind of observer that is created out of four functional interfaces and uses them as callbacks. It's mostly for using java 8 lambda expressions. It looks like this:
Observable.just(new Object())
.subscribe(
o -> processOnNext(o),
throwable -> processError(throwable),
() -> processCompletion(),
disposable -> processSubscription()
);
A Consumer consumes the values you receive when subscribing. It's like a Subscriber who passes the emitted data as callback.
The Consumer is a simple Interface which has a callback for a generic Type and is needed to receive the emitted items by the Observable.
Take care that if you only have a Consumer that you don't catch errors and you may get problems by debugging.
You can solve that by using another Consumer as second parameter which receives a Throwable.
Flowable.just("Hello world")
.subscribe(
emittedData -> System.out.println(emittedData), // onNext
throwable -> throwable.printStackTrace() // onError
);
In my humble opinion, consumer is for reverse / bi-directional streams.
For example, your data source emits a "Y" of complex time-dependent operations executed from a parameter "X" in the form of a "hot" flowable (HF).
Suppose the parameter X is emitted through a "hot" observable (HO), so, your data source can be a consumer that subscribes to the "HO" and emits the result of the complex operations through the HF.
In this case, you have the bi-directional stream and you used the consumer to push the date provided through the HO in the data source.
I am not sure if my answer is really correct... rx is a little bit complex :B

Query RxJava2 Db into another subject

On my Android project I'm heavily relying on RxJava2, SqlBrite(with RxJavaInterop) and SqlDelight.
I got one rx stream that is supposed to go indefinitely (until my service stops) and on it I have a .flatMap of Function<String, ObservableSource<Action>>.
Meaning, this flatMap contains a Subject<Action>, will receive String actionId, do some (irrelevant for the question) processing on those actionId, and depending on condition should query the database for the Action object and dispatch it to the subject
My first approach was to do the query directly:
Cursor c = db.query(...);
if(c.moveFirst()) {
Action a = Action.SELECT_ALL_MAPPER.map(c);
subject.onNext(selectAll);
}
But this blocks the running thread and I rather trigger this on its own stream that should do the following:
query (should return 0 or 1 item)
if there's a value: map to Action object and push the value to the subject
if there's no value: terminate/dispose.
the subject cannot receive terminate or error. It must stay alive for future events.
My current approach is the following code:
RxJavaInterop.toV2Observable(db.createQuery(
Action.TABLE_NAME,
Action.FACTORY.Select_by_id(actionId).statement)
.mapToOne(new Func1<Cursor, Action>() {
#Override public Action call(Cursor cursor) {
return Action.SELECT_ALL_MAPPER.map(cursor);
}
}))
.take(1)
.subscribe(new Consumer<Action>() {
#Override public void accept(Action action) throws Exception {
subject.onNext(action);
}
});
And although this seems to do the trick on the first impression, I see a few errors on it:
I can't dispose it. Even if I get the reference to Disposable object, I cannot call it from inside Consumer<Action> because it "might have not been initialised" (which I understand the reason, it's OK).
if there's no action with the given ID, the observable will hang in there for ever, until the VM is killed.
So the question:
How can I do that?
I rather trigger this on its own stream
Take a look at RxAndroid. This could look like:
yourRxStream
.flatMap(*db request here*)
.subscribeOn(Schedulers.io())
.subcribe(subject);
the subject cannot receive terminate or error. It must stay alive for
future events.
Switch the Subject with a Relay:
Subjects are useful to bridge the gap between non-Rx APIs. However,
they are stateful in a damaging way: when they receive an onComplete
or onError they no longer become usable for moving data. This is the
observable contract and sometimes it is the desired behavior. Most
times it is not.
Relays are simply Subjects without the aforementioned property. They
allow you to bridge non-Rx APIs into Rx easily, and without the worry
of accidentally triggering a terminal state.
Finally for the request than could output 0 or 1 item, use a Maybe.

Perform doOn<...> even if unsubscribed

I have an Observable that can emit events in some time after subscribing (e.g. an Observable from the Retrofit). The subscription to this Observable is strictly View-related, so when the View is destroyed I'm unsubscribing from the Observable. I want to perform some actions in doOnNext even if I unsubscribe the Subscription.
Example code:
final Observable<String> observable = ...; // will emit an event in some time in future
final Subscription subscription =
observable.doOnNext(new Action1<String>() {
#Override
public void call(String s) {
//this should be called even if the subscription is unsubscribed
}
}).subscribe();
subscription.unsubscribe();
Is there a way to make sure doOn<something> will be called even if the Subscription is unsubscribed?
EDIT:
Let me give you a bit clearer example:
final Observable<List<GithubRepo>> observable = getGithubReposFromApi();
subscription = observable
.doOnNext(githubRepos -> cacheGithubReposInDb(githubRepos))
.subscribe(githubRepos -> displayGithubReposInCurrentActivity(githubRepos));
And in Activity's onDestroy:
subscription.unsubscribe();
Now... If githubRepos were received after the Activity has been destroyed, the result wouldn't be cached in the database. And I would like it to be.
Okay, so you want the computation to run but want to cut off the Activity-dependent subscriber. You can publish() the sequence and subscribe to it. When unsubscribed, the original sequence will still go on:
ConnectableObservable<T> co = observable
.doOnNext(githubRepos -> cacheGithubReposInDb(githubRepos))
.publish();
Subscription s = co.subscribe(githubRepos ->
displayGithubReposInCurrentActivity(githubRepos));
co.connect();
// ...
s.unsubscribe();
I think you could do one of two things:
1.) Just have two Subscribers: one for View related stuff and one for the other side-effects that you are currently handling in doOnNext. To still have only one upstream subscription you could use:
Observable sharedObservable = observable.replay().refCount();
or maybe just
Observable sharedObservable = observable.share();
// which is the same as observable.publish().refCount();
2.) Use
Observable observableThatWillNeverTrulyUnsubscribe = observable.doOnNext(/* your doOnNext */).publish().autoConnect();
Now, when your Subscriber subscribes to observableThatWillNeverTrulyUnsubscribe it will subscribe to observable and will start emitting items. However, when the Subscriber unsubscribes, it will not unsubscribe upwards and thus doOnNext will continue to receive items.
I think I would prefer to first option as with the second you are giving up any possibility of ever stopping the work of observable.

Replace listeners with RxJava

Currently I am investigating a migration to RxJava and decided that a manager of mine(accountManager) would be an interesting place to start. Currently the Manager has a list of listeners and sends updates accordingly, both when the account gets updated and when something goes wrong.
private List<WeakReference<ProfileChangeListener>> mListeners = new ArrayList<>();
public interface ProfileChangeListener {
void onProfileUpdated(Account account);
void onProfileFailed(Exception e);
}
My Rx solution involves a Subject
private SerializedSubject<Account, Account> mManagerSubject = new SerializedSubject<>(BehaviorSubject.<Account>create());
public Observable<Account> observe() {
return mManagerSubject;
}
and then when an update happens I call one of the following:
private void onProfileUpdated(Account account) {
mManagerSubject.onNext(account);
}
private void onProfileFailed(final Exception e) {
mManagerSubject.onError(e);
}
Issue
The Issue is that once onError is called anyone listening via observe will never get another update from onNext.
I still want the subscribers to receive onError so they can handle the error state but at a later time onNext could still be called with an updated account and I still want the subscribers to handle the updated account.
I've tried solutions using onErrorResumeNext, onErrorReturn onExceptionResumeNext but none of them propagate the onError.
TLDR: How do I keep the subscribers subscribed after onError is called while still propagating onError?
"Errors" in Rx can be a a little difficult to grasp at first, because they have a slightly different meaning from what most people expect.
From the Error Handling documentation (emphasis mine):
An Observable typically does not throw exceptions. Instead it notifies any observers that an unrecoverable error has occurred by terminating the Observable sequence with an onError notification.
onError() is supposed to be used when an Observable encounters an unrecoverable error- that is when your Observable cannot continue emitting items. When you are subscribing, you might use something like onErrorResumeNext to try some recovery action, but that should be the end of the source Observable.
Instead, you may want to adjust what your Observable emits to support emitting an error item, or include a flag indicating that an error was encountered.
If your error truly is unrecoverable, then you may want to revisit your recovery strategy and try a slightly different approach.

Categories

Resources