OnSubscribe position causes OnComplete event to fire prematurely - android

I'm feeling a bit curious about how .subscribeOn() actually works on RxJava.
I have this piece of code that works as intended:
return endpoints.getRecentConversations(page)
.map().flatMap().doOnNext() //etc etc...
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
I was reading this article, trying to understand what the difference between subscribeOn and observeOn when this line caught my eye:
Position does not matter
subscribeOn can be put in any place in the stream because it affects
only the time of subscription
Which sounds perfectly fine. But I was feeling a bit skeptical I decided to test it. So I changed the code above (switched lines 2 and 3):
return endpoints.getRecentConversations(page)
.subscribeOn(Schedulers.io())
.map().flatMap().doOnNext() //etc etc...
.observeOn(AndroidSchedulers.mainThread())
As a result, I get an premature onComplete() on my subscriber. onNext() is never called and no errors are present in my logcat.
I can leave things the way they were, but I'd like to know why this is happening. Is it true that position doesn't matter? Is it something wrong with my code? Here's how my code looks

Yes, the position does matter a lot in RxJava, It's called upstream & downstream.
It's because you are subscribing to a hot observable
Watch this video to understand it better: Common RxJava Mistakes
https://www.youtube.com/watch?v=QdmkXL7XikQ&t=768s
There are two types of observables: Hot & cold.
A “hot” Observable may begin emitting items as soon as it is created,
and so any observer who later subscribes to that Observable may start
observing the sequence somewhere in the middle. A “cold” Observable,
on the other hand waits until an observer subscribes to it before it
begins to emit items, and so such an observer is guaranteed to see the
whole sequence from the beginning.

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 } )
})

Starting an asynchronous operation using RxJava without subscribing to an observable?

Let's say your DAO has this method that updates user records in the DB:
#Update
fun update(user: User): Single<Int>
Recently I started learning RxJava and so far I have seen lots examples like following:
// Example 1
disposable.add(dao.updateUser(user)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe {
Log.d(TAG, "response received")
}
In the above example, I understand that as soon as the subscription starts, updateUser() will be executed on a worker thread and the subscriber will be notified and run in the main thread once the execution completes.
But what if you are not interested in the result of updateUser(), and all you want is just to execute updateUser() on a worker thread?
So far I have tried doing:
// Example 2
dao.updateUser(user)
or
// Example 3
dao.updateUser(user).subscribeOn(Schedulers.io())
But they didn't work. It seems the update requests are never executed, nothing was logged and records didn't change. I am guessing that's because there isn't any subscriber attached to it.
For now I am forcing it to work by attaching a random subscriber that doesn't really do anything like the one in Example 1. One of the problems with the approach is that I might need to make this request a lot and that might create a lot of dummy subscribers, not to mention that the code looks really bad.
Could you help me find a better way of handling this?
But You already wrote answer for Your question.
You can just call:
dao.updateUser(user).subscribe()
If You want manipulate / jump between thread you are doing something like in Example 1.

Retrofit2/RxJava2, valve(), FlowableTransformers.valve not working

I tried to make a shorter code of what I have , but it seems not enough to make it readable so I ended up with the following:
Flowable
.defer (return new outer observable upon subscribing)
.retryWhen ( ->
Flowable.flatMap throwable when recieve a 400
valve.onNext(false)
-> Flowable.defer ( return new Network_A_Observable)
.retryWhen ( ->
Flowable.flatmap throwable when receive 500)
valve.onNext(true)
)
.compose(flowable valve (i intentionally put a false here))
.subscribe(new subscriber)
This is the very short version of my long-non-lambda code that performs a series of network calls and retrying appropriately on certain conditions.
I have no problems with retrying and emitting a new outer observable for each retry(as this was solved on my other post, but not sure if it has something to do with my current issue), now I noticed that when I perform, say, 2 asynchrounous network calls, it returns two different values, although those two values are valid, and ofcourse the stream of observable works as expected and that was what I intended to do(retry when some error happens), now I realize that I should "PAUSE" the next stream of calls, searching like "how to pause an observable, until I found FlowableTransformer.valve(), which is available on RxJava2Extensions, I ran a code snippet from a blog where it pauses/continues a stream, but when I tried it on the above code, event if the valve is set to false, it keeps on finishing the whole stream of flowable.
Am i missing something?
Any help would be greatly appreciated.

Make Single Observable emit more than once

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.

RxJava onBackpressureBuffer not emitting items

I've witnessed a weird behavior with onBackpressureBuffer, I'm not sure if it is a valid behavior or a bug.
I'm having a tcp call that is emitting items in a certain rate (using streaming and inputStream but that just for some info)
On top of it I've created an observable using create that will emit an item each time it is ready.
Let's call it messages().
Then I'm doing this:
messages()
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe({//do some work});
I've noticed using analytics tools that MissingBackPressureException is thrown rarely, so I've added onBackpressureBuffer to the call.
If I'm adding it after observeOn:
messages()
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.onBackpressureBuffer()
.subscribe({//do some work})
everyting works fine, but it means it will buffer only after it get's to the UI Main thread, so I prefered it to be like this:
messages()
.onBackpressureBuffer()
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe({//do some work});
And that where things start to get weird.
I've noticed the while messages() keeps emitting item, at some point they will stop being delivered to the subscriber.
More precisely after exactly 16 items, what is apparently happing is that the buffer will start holding items without passing them forward.
Once I cancel the messages() with some sort of timeout mechanism, it will cause messages() to emit onError() and the buffer will emit immediately all the items it has kept (they will be handled).
I've checked to see if it is the subscriber fault of doing too much work but it not, he is finished and still he doesn't get the items...
I've also tried using the request(n) method in the subscriber asking for one item after onNext() is finished but the buffer doesn't behave.
I suspect that the messaging system of the Main Android UI Thread with the backpressure causing this, but I can't explain why.
can someone explain why this is happening? is this a bug or a valid behaviour?
Tnx!
Not knowing how messages(), based on the behavior described, this is a similar same-pool deadlock as with this question
The workaround, what you didn't try, is to put the .onBackpressureBuffer between the subscribeOn and observeOn.
messages()
.subscribeOn(Schedulers.io())
.onBackpressureBuffer() // <---------------------
.observeOn(AndroidSchedulers.mainThread())
.subscribe({//do some work});
Question is different but answer comes down to the same thing.
The implementation of observeOn constructor: OperatorObserveOn(Scheduler scheduler, boolean delayError, int bufferSize):
public OperatorObserveOn(Scheduler scheduler, boolean delayError, int bufferSize) {
this.scheduler = scheduler;
this.delayError = delayError;
this.bufferSize = (bufferSize > 0) ? bufferSize : RxRingBuffer.SIZE;
}
The last line points to the buffer size.
The buffer size on Android is 16.
The solution is simply passing a bigger buffer size to observeOn(Scheduler scheduler, int bufferSize) operator:
messages()
.observeOn(AndroidSchedulers.mainThread(), {buffer_size})
Be careful not to put too high value, as Android has limited memory.

Categories

Resources