How to implement requests sequently in RxAndroid? - android

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.

Related

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

Attaching listeners in a for loop [duplicate]

I have an array of keys which lead to post objects for my social network like so /posts/id/(post info)
When I load the posts I load /posts/0 and then /posts/1 etc using the observeSingleEventOfType(.Value) method.
I use a lazyTableView to load 30 at a time and it is quite slow. Is there any way I can use one of the query methods or another way of making it faster even if I have to restructure the data in my JSON tree.
I am coming from Parse re-implementing my app and so far the experience as been quite good. Just this one thing I am a bit stuck on.
EDIT:
func loadNext(i: Int) {
// check if exhists
let ideaPostsRef = Firebase(url: "https://APPURL")
ideaPostsRef.childByAppendingPath(i.description).observeSingleEventOfType(.Value, withBlock: {
(snapshot) in
if i % 29 == 0 && i != 0 && !self.hitNull { return }
// false if nil
// true if not nil
if !(snapshot.value is NSNull) {
let postJSON = snapshot.value as! [String: AnyObject]
print("GOT VALID \(postJSON)")
let post = IdeaPost(message: postJSON["message"] as! String, byUser: postJSON["user"] as! String, withId: i.description)
post.upvotes = postJSON["upvotes"] as! Int
self.ideaPostDataSource.append(post)
self.loadNext(i + 1)
} else {
// doesn't exhist
print("GOT NULL RETURNING AT \(i)")
self.doneLoading = true
self.hitNull = true
return
}
}
}
This recursive function essentially runs getting the value for key number i from firebase. If it is NSNULL it knows that is the last possible post to load and never does again. If NSNULL doesn't get hit but i % 29 == 0 then it returns as a base case so only 30 posts are loaded at a time (0 indexed). When I set doneLoading to true, tableView.reloadData() is called using a property observer.
Here is a sample of what the array I am fetching looks like
"ideaPosts" : [ {
"id" : 0,
"message" : "Test",
"upvotes" : 1,
"user" : "Anonymous"
}, {
"id" : 1,
"message" : "Test2",
"upvotes" : 1,
"user" : "Anonymous"
} ]
Update: we now also cover this question in an AskFirebase episode.
Loading many items from Firebase doesn't have to be slow, since you can pipeline the requests. But your code is making this impossible, which indeed will lead to suboptimal performance.
In your code, you request an item from the server, wait for that item to return and then load the next one. In a simplified sequence diagram that looks like:
Your app Firebase
Database
-- request item 1 -->
S L
e o
r a
v d
e i
<- return item 1 -- r n
g
-- request item 2 -->
S L
e o
r a
v d
e i
r n
<- return item 2 -- g
-- request item 3 -->
.
.
.
-- request item 30-->
S L
e o
r a
v d
e i
r n
g
<- return item 30 --
In this scenario you're waiting for 30 times your roundtrip time + 30 times the time it takes to load the data from disk. If (for the sake of simplicity) we say that roundtrips take 1 second and loading an item from disk also takes one second that least to 30 * (1 + 1) = 60 seconds.
In Firebase applications you'll get much better performance if you send all the requests (or at least a reasonable number of them) in one go:
Your app Firebase
Database
-- request item 1 -->
-- request item 2 --> S L
-- request item 3 --> e o
. r a
. v d
. e i
-- request item 30--> r n
g
<- return item 1 --
<- return item 2 --
<- return item 3 --
.
.
.
<- return item 30 --
If we again assume a 1 second roundtrip and 1 second of loading, you're waiting for 30*1 + 1 = 31 seconds.
So: all requests go through the same connection. Given that, the only difference between get(1), get(2), get(3) and getAll([1,2,3]) is some overhead for the frames.
I set up a jsbin to demonstrate the behavior. The data model is very simple, but it shows off the difference.
function loadVideosSequential(videoIds) {
if (videoIds.length > 0) {
db.child('videos').child(videoIds[0]).once('value', snapshot => {
if (videoIds.length > 1) {
loadVideosSequential(videoIds.splice(1), callback)
}
});
}
}
function loadVideosParallel(videoIds) {
Promise.all(
videoIds.map(id => db.child('videos').child(id).once('value'))
);
}
For comparison: sequentially loading 64 items takes 3.8 seconds on my system, while loading them pipelined (as the Firebase client does natively) it takes 600ms. The exact numbers will depend on your connection (latency and bandwidth), but the pipelined version should always be significantly faster.

Does Firebase sdk batch requests? [duplicate]

I have an array of keys which lead to post objects for my social network like so /posts/id/(post info)
When I load the posts I load /posts/0 and then /posts/1 etc using the observeSingleEventOfType(.Value) method.
I use a lazyTableView to load 30 at a time and it is quite slow. Is there any way I can use one of the query methods or another way of making it faster even if I have to restructure the data in my JSON tree.
I am coming from Parse re-implementing my app and so far the experience as been quite good. Just this one thing I am a bit stuck on.
EDIT:
func loadNext(i: Int) {
// check if exhists
let ideaPostsRef = Firebase(url: "https://APPURL")
ideaPostsRef.childByAppendingPath(i.description).observeSingleEventOfType(.Value, withBlock: {
(snapshot) in
if i % 29 == 0 && i != 0 && !self.hitNull { return }
// false if nil
// true if not nil
if !(snapshot.value is NSNull) {
let postJSON = snapshot.value as! [String: AnyObject]
print("GOT VALID \(postJSON)")
let post = IdeaPost(message: postJSON["message"] as! String, byUser: postJSON["user"] as! String, withId: i.description)
post.upvotes = postJSON["upvotes"] as! Int
self.ideaPostDataSource.append(post)
self.loadNext(i + 1)
} else {
// doesn't exhist
print("GOT NULL RETURNING AT \(i)")
self.doneLoading = true
self.hitNull = true
return
}
}
}
This recursive function essentially runs getting the value for key number i from firebase. If it is NSNULL it knows that is the last possible post to load and never does again. If NSNULL doesn't get hit but i % 29 == 0 then it returns as a base case so only 30 posts are loaded at a time (0 indexed). When I set doneLoading to true, tableView.reloadData() is called using a property observer.
Here is a sample of what the array I am fetching looks like
"ideaPosts" : [ {
"id" : 0,
"message" : "Test",
"upvotes" : 1,
"user" : "Anonymous"
}, {
"id" : 1,
"message" : "Test2",
"upvotes" : 1,
"user" : "Anonymous"
} ]
Update: we now also cover this question in an AskFirebase episode.
Loading many items from Firebase doesn't have to be slow, since you can pipeline the requests. But your code is making this impossible, which indeed will lead to suboptimal performance.
In your code, you request an item from the server, wait for that item to return and then load the next one. In a simplified sequence diagram that looks like:
Your app Firebase
Database
-- request item 1 -->
S L
e o
r a
v d
e i
<- return item 1 -- r n
g
-- request item 2 -->
S L
e o
r a
v d
e i
r n
<- return item 2 -- g
-- request item 3 -->
.
.
.
-- request item 30-->
S L
e o
r a
v d
e i
r n
g
<- return item 30 --
In this scenario you're waiting for 30 times your roundtrip time + 30 times the time it takes to load the data from disk. If (for the sake of simplicity) we say that roundtrips take 1 second and loading an item from disk also takes one second that least to 30 * (1 + 1) = 60 seconds.
In Firebase applications you'll get much better performance if you send all the requests (or at least a reasonable number of them) in one go:
Your app Firebase
Database
-- request item 1 -->
-- request item 2 --> S L
-- request item 3 --> e o
. r a
. v d
. e i
-- request item 30--> r n
g
<- return item 1 --
<- return item 2 --
<- return item 3 --
.
.
.
<- return item 30 --
If we again assume a 1 second roundtrip and 1 second of loading, you're waiting for 30*1 + 1 = 31 seconds.
So: all requests go through the same connection. Given that, the only difference between get(1), get(2), get(3) and getAll([1,2,3]) is some overhead for the frames.
I set up a jsbin to demonstrate the behavior. The data model is very simple, but it shows off the difference.
function loadVideosSequential(videoIds) {
if (videoIds.length > 0) {
db.child('videos').child(videoIds[0]).once('value', snapshot => {
if (videoIds.length > 1) {
loadVideosSequential(videoIds.splice(1), callback)
}
});
}
}
function loadVideosParallel(videoIds) {
Promise.all(
videoIds.map(id => db.child('videos').child(id).once('value'))
);
}
For comparison: sequentially loading 64 items takes 3.8 seconds on my system, while loading them pipelined (as the Firebase client does natively) it takes 600ms. The exact numbers will depend on your connection (latency and bandwidth), but the pipelined version should always be significantly faster.

RxJava map and emit as soon as a single map completed

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

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