RxJava map and emit as soon as a single map completed - android

I want to map/convert an object to another object in background thread and have it on main thread as soon as a single conversation is completed.
Observable.just(1,2,3,4,5)
.map(new Func1<Integer, String>() {
#Override
public String call(Integer integer) {
Log.d(TAG, "mapping number " + integer);
return String.valueOf(integer) + " mapped on: " + Thread.currentThread().getName();
}
})
.subscribeOn(Schedulers.newThread())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Observer<String>() {
#Override
public void onCompleted() {
Log.d(TAG, "onCompleted on: " + Thread.currentThread().getName());
}
#Override
public void onError(Throwable e) {
}
#Override
public void onNext(String integer) {
Log.d(TAG, integer + " received on: "+ Thread.currentThread().getName());
}
});
The Result is:
D: mapping number 1
D: mapping number 2
D: mapping number 3
D: mapping number 4
D: mapping number 5
D: 1 mapped on: RxNewThreadScheduler-1 received on: main
D: 2 mapped on: RxNewThreadScheduler-1 received on: main
D: 3 mapped on: RxNewThreadScheduler-1 received on: main
D: 4 mapped on: RxNewThreadScheduler-1 received on: main
D: 5 mapped on: RxNewThreadScheduler-1 received on: main
D: onCompleted on: main
However the conversion may took a while and I expect to receive them as soon as the conversion is done.
D: mapping number 1
D: 1 mapped on: RxNewThreadScheduler-1 received on: main
D: mapping number 2
D: 2 mapped on: RxNewThreadScheduler-1 received on: main
D: mapping number 3
D: 3 mapped on: RxNewThreadScheduler-1 received on: main
D: mapping number 4
D: 4 mapped on: RxNewThreadScheduler-1 received on: main
D: mapping number 5
D: 5 mapped on: RxNewThreadScheduler-1 received on: main
D: onCompleted on: main

There is no need to set the global buffer size, just use the observeOn(Scheduler, int) overload where you can specify the prefetch value to be 1. That will only ask for the next value if the previous value has been processed.

This is due to RxJava applying backpressure on the operators in the chain you are using above. The downstream operators such as ObserveOn request data from upstream by chunks, and not by individual items for efficiency. If you set the buffer size to one, this will effectively achieve of what you would expect with cost of efficiency:
-Drx.ring-buffer.size=1
Specifically that would be quite awful for upstreams that have expensive roundtrip calls.
EDIT:
You can use zip with BehaviorSubject to sort of serialize your down and up stream emissions:
BehaviorSubject<Void> signal = BehaviorSubject.create();
signal.onNext(null); // <- pair up the signal with the first item immediately
Observable.just(1,2,3,4,5)
.zipWith(signal, (item,v)->item) //only emit a next item when there is a "receipt acknowledgement" from the down stream
.observeOn(Schedulers.newThread()) //<- needed to avoid fetching subsequent items in UI thread
.map(new Func1<Integer, String>() {
#Override
public String call(Integer integer) {
Log.d(TAG, "mapping number " + integer);
return String.valueOf(integer) + " mapped on: " + Thread.currentThread().getName();
}
})
.subscribeOn(Schedulers.newThread())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Observer<String>() {
#Override
public void onCompleted() {
Log.d(TAG, "onCompleted on: " + Thread.currentThread().getName());
}
#Override
public void onError(Throwable e) {
}
#Override
public void onNext(String integer) {
Log.d(TAG, integer + " received on: "+ Thread.currentThread().getName());
signal.onNext(null); //<- acknowledge receipt - allow emitting next item from upstream
}
});

Related

Why removed event called in firestore realtime Listener when new document added in the collection?

I am trying to read the messages in real time, the problem is when Person A sends a message to Person B the Person B real-time Listener called with an Added event which is totally I get it but why the Removed event also called?
fun readMessagesRealTimeListener(documentID: String, date: Date, senderUser: ChatUser, reciverUser: ChatUser): LiveData<List<ChatMessage>> {
if (mutableChatMessageList == null) {
mutableChatMessageList = MutableLiveData()
val chatMessageList = ArrayList<ChatMessage>()
db = FirebaseFirestore.getInstance()
readMessageListener = db.collection(Constants.CONVERSATION)
.document(documentID)
.collection("0")
.orderBy("data", Query.Direction.DESCENDING)
.limit(5)
.addSnapshotListener(EventListener<QuerySnapshot> { querySnapshot, e ->
if (e != null) {
Log.d(TAG, "error in real time listerner: " + e.code);
}
val source = if (querySnapshot != null && querySnapshot.metadata.hasPendingWrites())
"Local"
else
"Server"
Log.d(TAG, "source of $source")
chatMessageList.clear()
if (!querySnapshot?.isEmpty!!) {
for (changedSnapshot in querySnapshot.documentChanges) {
when (changedSnapshot.type) {
DocumentChange.Type.ADDED -> Log.d(TAG, "New message: " + changedSnapshot.getDocument().getData())
DocumentChange.Type.MODIFIED -> Log.d(TAG, "Modified message: " + changedSnapshot.getDocument().getData())
DocumentChange.Type.REMOVED -> Log.d(TAG, "Removed message: " + changedSnapshot.getDocument().getData())//this log also called when new document added in firestore but why?
}
var chatMessage = changedSnapshot.document.toObject(ChatMessage::class.java)
Log.d(TAG, "message " + chatMessage.message)
chatMessageList.add(chatMessage)
}
mutableChatMessageList?.value = chatMessageList
} else {
Log.d(TAG, "no message found in real time")
mutableChatMessageList?.setValue(null)
}
})
}
return mutableChatMessageList!!
}
A quick guess is that you have more than 5 messages in the collection. In that scenario this listener shows the latest 5 messages only:
readMessageListener = db.collection(Constants.CONVERSATION)
.document(documentID)
.collection("0")
.orderBy("data", Query.Direction.DESCENDING)
.limit(5)
So now when you add a new message, that new message becomes the latest. And the previously first message is removed from the QuerySnapshot.
It's easiest to see if we quickly draw up up the messages. Say you start with these 5:
1
2
3
4
5
So your listener gets called with these 5 messages. And now we add a new message 6. This new message will be added to the QuerySnapshot. But that would lead to it having 6 messages, and you only asked for 5. So you get an ADDED for 6, and a REMOVED for `1.
1 REMOVED
2
3
4
5
6 ADDED
And end up with:
2
3
4
5
6

Room flowable cache

I am using Room (1.0.0.rc1) with RX, my Dao is defined is this way:
#Dao
interface AccountDao {
#Query("SELECT * FROM Account ORDER BY name")
fun all(): Flowable<List<Account>>
}
I am subscribing this way:
dao
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe { accounts = it }
I have more than one place in the code who subscribes to the flowable. The first to subscribe gets the data, the other ones don't.
How can I make an observable that will emit the actual content every time someones subscribes and will also notify every subscriber when the data changes?
You can use replay to emit lastest value every time someone subscribes. And use distinctUntilChanged to notify only when data changes.
Here is the sample:
import io.reactivex.Observable;
import io.reactivex.subjects.BehaviorSubject;
public class Q47000608 {
public static void main(String[] args) {
BehaviorSubject<Integer> bs = BehaviorSubject.createDefault(1);
Observable<Integer> o = bs.replay(1).autoConnect().distinctUntilChanged();
o.subscribe(i -> System.out.println("s1 accept " + i));
bs.onNext(2);
o.subscribe(i -> System.out.println("s2 accept " + i));
o.subscribe(i -> System.out.println("s3 accept " + i));
bs.onNext(3);
o.subscribe(i -> System.out.println("s4 accept " + i));
bs.onNext(4);
}
}
And output:
s1 accept 1
s1 accept 2
s2 accept 2
s3 accept 2
s1 accept 3
s2 accept 3
s3 accept 3
s4 accept 3
s1 accept 4
s2 accept 4
s3 accept 4
s4 accept 4

How does subscribeOn work

I have some doubts regarding the working on the subscribeOn operator. I read some article regarding this.
The observeOn is quite easy to understand, it changes only the downstram, and change affects to all the downstream.
But as told in the article subscribeOn can be put in any place in the stream because it affects only the time of subscription.:
To understand this , I did a samlpe and tried logging the thread at each point of time.
Observable.just("Hello")
.map(s -> {
Log.d(TAG, s + " in " + Thread.currentThread());
return 1;
})
.subscribeOn(Schedulers.newThread())
.map(integer -> {
Log.d(TAG, integer + " in " + Thread.currentThread());
return true;
})
.map(aBoolean -> {
Log.d(TAG, aBoolean + " in " + Thread.currentThread());
return 11.0;
})
.subscribeOn(Schedulers.computation())
.subscribe(aDouble -> {
Log.d(TAG, "accept in " + Thread.currentThread());
Log.d(TAG, "accept: " + aDouble);
});
The result is
Hello in Thread[RxNewThreadScheduler-1,5,main]
1 in Thread[RxNewThreadScheduler-1,5,main]
true in Thread[RxNewThreadScheduler-1,5,main]
accept in Thread[RxNewThreadScheduler-1,5,main]
accept: 11.0
Here twice I'm applying subscribeOn, but everytime the first added one seem to be applied throughout the stream.
Can anyone please explain in simple words how does it actually work, since I'm a beginner and hard to digest this!
Thanks in advance
subscribeOn: If you have multiple subscribeOn then the first one takes effect. If you want to change the Scheduler on the stream after making a subscribeOn, then take a look at observeOn
observeOn: It changes the Scheduler going downstream.
For example:
just("Some String") // Computation
.subscribeOn(Schedulers.computation()) // it changes scheduler to computation beginning from source to observer.
.map(str -> str.length()) // Computation
.observeOn(Schedulers.io) //change the scheduler from here till the observer
.map(length -> 2 * length) // io
.subscribe(number -> Log.d("", "Number " + number));// io

How to implement requests sequently in RxAndroid?

I need to check if it exits in DB load and pass to view
if not
load from Server ,Store in DB ,then pass to view
I confused in methods doOnNext(), flatMap() ,I don't which one to use
I couldn't understand the meaning of methods of doOnNext(), flatMap()
I will try to help you with this operators.
As documentation states about doOnNext()
modifies the source Observable so that it invokes an action when it
calls
So check this sample out:
public class ExampleUnitTest {
#Test
public void testSample() throws Exception {
Observable.just(1,2,3,4,5)
.doOnNext(number -> System.out.println("doOnNext: " + number))
.subscribe(number -> System.out.println("onNext: " + number));
}
}
It will print:
doOnNext: 1
onNext: 1
doOnNext: 2
onNext: 2
doOnNext: 3
onNext: 3
doOnNext: 4
onNext: 4
doOnNext: 5
onNext: 5
What about flatMaps? Let's check documentation again:
Returns an Observable that emits items based on applying a function
that you supply to each item emitted by the source Observable, where
that function returns an Observable, and then merging those resulting
Observables and emitting the results of this merger.
So, flatMap creates a new stream based on each item emitted by original stream and them merge those new streams into a single one.
Let's see some code again:
public class ExampleUnitTest {
#Test
public void testSample() throws Exception {
Observable.just(1, 2, 3, 4, 5)
.flatMap(number -> {
final String first = number + "A";
final String second = number + "B";
return Observable.just(first, second);
})
.subscribe(flattedItem -> System.out.println("onNext: " + flattedItem));
}
}
It will print:
onNext: 1A
onNext: 1B
onNext: 2A
onNext: 2B
onNext: 3A
onNext: 3B
onNext: 4A
onNext: 4B
onNext: 5A
onNext: 5B
Hope that it helps you bro!
Best regards.

ReplySubject from array

In the context of a android application I want to create an ReplaySubject from an array that I retrieve from the saved bundle.
So far I have:
subject = ReplaySubject.create();
for (SomeDto dto : dtos) {
subject.onNext(dto);
}
I was hoping for a more straight forward way that will avoid the iteration.
Do you mean ReplaySubject?
If the DTO list you are retrieving from the bundle is constant and the subscribers don't need to be notified of changes after being retrieved from the bundle you can just use a standard Observable no need to use a Subject.
List<SomeDto> dtos; // Get list of SomeDto from bundle.
Observable<SomeDto> dtoObservable = Observable.from(dtos);
Each time a subscription to dtoObserbale is made it will replay all the items.
If your subscribers need to notified of changes to the DTO list in addition to the initial values then the way you are doing it is just fine.
EDIT: Another idea you might like. Create an observable from the DTOs retrieved from the Bundle. Then concatenate it with the DTO subject.
List<SomeDto> dtos; // Get list of SomeDto from bundle.
ReplaySubject<SomeDto> subject = ReplaySubject.create();
Observable<SomeDto> dtoObservable =
Observable
.from(dtos)
.concatWith(subject);
// At some point later.
subject.onNext(anotherDto);
Each subscriber will see all values. Here's an example with strings:
final List<String> strings = Arrays.asList("1", "2");
final ReplaySubject<String> stringsSubject = ReplaySubject.create();
final Observable<String> stringObservable =
Observable
.from(strings)
.concatWith(stringsSubject);
stringObservable
.subscribe(new Action1<String>() {
#Override
public void call(String s) {
logger.v("stringObservable1 - " + s);
}
});
stringsSubject.onNext("3");
stringObservable
.subscribe(new Action1<String>() {
#Override
public void call(String s) {
logger.v("stringObservable2 - " + s);
}
});
stringsSubject.onNext("4");
stringObservable
.subscribe(new Action1<String>() {
#Override
public void call(String s) {
logger.v("stringObservable3 - " + s);
}
});
stringsSubject.onNext("5");
Outputs the following:
stringObservable1 - 1
stringObservable1 - 2
stringObservable1 - 3
stringObservable2 - 1
stringObservable2 - 2
stringObservable2 - 3
stringObservable1 - 4
stringObservable2 - 4
stringObservable3 - 1
stringObservable3 - 2
stringObservable3 - 3
stringObservable3 - 4
stringObservable1 - 5
stringObservable2 - 5
stringObservable3 - 5

Categories

Resources