FlatMapIterable and empty lists - android

I want to execute a background task on a set of items from a database using RxJava 2. The list of items may be empty, which means I can't use flatMapIterable as it throws an exception on an empty list:
Observable
.fromCallable(() -> SQLite
.select()
.from(VideoUpload.class)
.where(VideoUpload_Table.status.eq(VIDEO_UPLOAD_IN_PROGRESS))
.queryList())
.flatMapIterable(videoUploads -> videoUploads)
.map(videoUpload -> {
videoUpload.setStatus(VIDEO_UPLOAD_NOT_STARTED);
return videoUpload;
})
.firstElement()
.subscribeOn(Schedulers.io())
.subscribe(/* TODO */);
I can move everything into the callable, do the filtering there and so on, but I was thinking there may be a more elegant solution based on the code above.

Turns out that flatMapIterable does not throw an exception on RxJava-2, I remember it throwing an exception on RxJava-1. Anyway, the way to do it is as follows
Observable
.fromCallable(() -> {
List<VideoUpload> videos = SQLite
.select()
.from(VideoUpload.class)
.where(VideoUpload_Table.status.in(VIDEO_UPLOAD_IN_PROGRESS, VIDEO_UPLOAD_QUEUED))
.queryList();
return videos;
})
.flatMapIterable(videoUploads -> videoUploads)
.map(videoUpload -> {
videoUpload.setStatus(VIDEO_UPLOAD_NOT_STARTED);
return videoUpload;
})
.firstElement()
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(/* TODO */);

Related

Fetching a Single<List> for all elements of another Single<List> in RxKotlin

A, B, C are objects
All function calls are made to a Rooms DB
This code snippet is inside a ViewModel
repo = Repository
So I'm making an android app (can't provide details) and for a particular screen I need to do the following.
My first call is repo.getInfo, which returns a Single Observable ListOfA: Single<List<A>> //perform some operations
for every element of ListOfA I need to call another function repo.getB(A) which returns a Single Observable ListOfB: Single<List<B>> //perform some operations
for every element of ListOfB I need to call another function repo.getC(B) which returns a Single Observable ListOfC: Single<List<C>> //perform some operations
after I have the required data I need to call another function that combines the data to display on the UI.
Now I can't get this to work. Here's what I've tried. But the flow stops at the line marked THIS LINE and jumps to subscribe block.
Individual calls to the functions work so the data is not the problem.
I'm pretty new at this and quite frankly out of my depth. Any help or hint is appreciated. Thanks
localListOfA = emptyList<A>()
localListOfB = emptyList<B>()
localListOfC = emptyList<C>()
compositeDisposable.add(
getInfo.map{listOfA ->
localListOfA.addAll(listofA)
listOfA.map {elementA -> ////THIS LINE
getB(elementA.id).map{listOfB ->
listOfB.filter {
//some logic to select a few objects
}
}.map { it // filtered list of B
localListofB.addAll(it)
localListOfB.last() //I only need the top element of this list
}.map{elementB ->
getC(elementB.id).map{ listOfC ->
localListOfC.addAll(listOfC)
//do some operations
}
}
}
}
.subscribeOn(DEFAULT_CACHED_SCHEDULERS)
.observeOn(AndroidSchedulers.mainThread())
.doOnError(/*take log*/)
.subscribe{
prepareUi()
}
)
You can flatten a List into an Observable using .flattenAsObservable
getInfo // Single<List<A>>
.doOnSuccess { localListOfA.addAll(it) } // Side effect, adding to localListOfA
.flattenAsObservable { it } // Observable<A>
.flatMapSingle { elementA -> getB(elementA.id) } // Observable<List<B>>
.map { it.filter { true } } // Some logic to select a few objects from B
.doOnNext { localListOfB.addAll(it) } // Side effect, adding to localListOfB
.map { it.last() } // Observable<B>, only the last element
.flatMapSingle { elementB -> getC(elementB.id) } // Observable<List<C>>
.doOnNext { localListOfC.addAll(it) } // Side effect, adding to localListOfC
.flatMapIterable { it } // Observable<C>
Now, you mentioned you need to combine this data somehow. In Rx you can nest chains in order to access the intermediate data. For example, if you have a call that returns a Single<Foo> and you need Foo for the function getBar(foo: Foo): Single<Bar>, one way of achieving this is as follows:
getFoo().flatMap { foo -> // .concatMap, .switchMap
getBar(foo).map { bar ->
// Use both foo and bar
}
}

Rxjava while loop to get database object and upload to server

I'm confused about how to implement this in RxJava.
I would like to
take an object from my database
upload it
delete it from the database
take the next item from the database and repeat 2 and 3
complete when the database has no objects remaining
I know how to do this via loading all objects from the database at first and creating an Observable like this Observable.fromIterable(allMyDbObjects), however I would like to take objects one at a time, in case the database is updated while I'm uploading.
I can't figure out how to do this. I've looked at repeatUntil but it just seems to repeat instantly. Here is pseudocode for what I'm thinking:
getFirstDbObject()
.flatMapCompletable { obj ->
upload(obj)
.doOnComplete {
deleteFromDb(obj)
}
}
.repeatUntil {
// dbIsEmptyLogic.
// This doesn't work. I need to somehow call getFirstDbObject() again
}
Can anyone help?
Assuming getFirstDbObject() returns a Maybe, you can achieve this by mapping the result to a boolean (true if the database is empty, false if not) and then repeating the sequence until getFirstDbObject() returns empty and the stream completes.
getFirstDbObject()
.toObservable()
.flatMapSingle { obj ->
upload(obj)
.doOnComplete { deleteFromDb(obj) } // probably better to use .andThen(deleteFromDb(obj)) if delete also returns a completable
.toSingleDefault(false)
}
.defaultIfEmpty(true)
.repeat()
.takeUntil { isComplete ->
isComplete
}
This is a working solution in my code base.
val source = HashSet<String>()
source.add("a")
source.add("b")
source.add("c")
source.add("d")
source.add("e")
io.reactivex.Observable.just(Unit)
.flatMap { it ->
io.reactivex.Observable.fromCallable {
println("first flatmap print $it")
// uploadObj()
source.first()
}
}.flatMap {
// delete
io.reactivex.Observable.fromCallable {
source.remove(it)
println("second flatmap remove $it")
// delete object
}
}
.repeatUntil { source.isEmpty() }
.subscribe()

Android multithreading data

How take process multithreaded load for the item list. I get from api list string elements. Next need to get data for items this list. Load need to use rxjava. Result need do getting to the single subscribe.
Next need to get data for items this list
What it has to be? Just method inside your class or separate network request for each of strings?
For first case:
getListFromApi()
.toFlowable()
.flatMap { Flowable.fromIterable(it) }
.map { getSomeData(it) }
.toList()
.subscribe()
Second case:
getListFromApi()
.toFlowable()
.flatMap { Flowable.fromIterable(it) }
.map { requestForSomeData(it) }
.toList()
.flatMap { flowablesList -> Single.zip(flowablesList.map { it.firstOrError() }) { it.toList() } }
.subscribe()

RxJava on error perform onErrorResumeNext() but also take some action

While iterating and fetching web responses the chain stops when it encounters an error.
I used .onErrorResumeNext(Observable.empty()) to keep the iteration going but want to do some error handling too. How can this be done?
.getRepos()
.flatMap { itemList ->
//fetches all repos
Observable.fromIterable(itemList)
}
.concatMapEager { githubItem ->
//fetches commits of each repos
networkModule.getCommits().map { commitItem ->
dataBaseModule.insertRepo(commitItem)
}.onErrorResumeNext(Observable.empty())
//here I want to take some action instead of just passing an empty observable
}
You can use the doOnError() operator just before the onErrorResumeNext() to perform an action.
...
.doOnError( error -> {} )
.onErrorResumeNext( Observable.empty() )
...

RxJava flatmap chaining requests

i am using Retrofit with RxJAva for an app that gets Rss Feeds, but the rss doesn't contain all the informations so i use jsoup to parse every item link, to retrieve the image and the article's description. now i am using it this way:
public Observable<Rss> getDumpData() {
return newsAppService.getDumpData()
.flatMap(rss -> Observable.from(rss.channel.items)
.observeOn(Schedulers.io())
.flatMap(Checked.f1(item -> Observable.just(Jsoup.connect(item.link).get())
.observeOn(Schedulers.io())
.map(document -> document.select("div[itemprop=image] > img").first())
.doOnNext(element -> item.image = element.attr("src"))
)))
.defaultIfEmpty(rss)
.ignoreElements()
.observeOn(Schedulers.io())
.subscribeOn(AndroidSchedulers.mainThread());
}
and i am getting an error on this line: defaultIfEmpty(rss)
it doesn't recognize rss of the flatmap. and when i move the defaultIfEmpty(rss) in flatmap brackets i have another error saying that the return type must be changed to Element. is their any solution ?
first of all you need to get rid of all the concurrency with observeOn and use subscribeOn.
.observeOn(Schedulers.io())
Please consider using observeOn with AndroidScheduler if want to sync back data from another thread back to the event-loop. Normally you would use observeOn before subscribing to a observable in order to sync back to ui-loop and change ui-information.
.observeOn(AndroidSchedulers.mainThread())
Secondly it is not recommended to mutate objects in the pipeline. You should return a new object very time.
.doOnNext(element -> item.image = element.attr("src"))
I tried to refactor your solution under consideration of the first two points. I am using RxJava2-RC5
The flatMap operator has many overloades. One of them provides a function to zip together the incoming value and the created value.
Observable<Rss> rssItemObservable = newsService.getDumpData()
.flatMap(rss -> getRssItemInformation(rss).subscribeOn(Schedulers.io()),
(r, rItemList) -> {
Rss rInterim = new Rss();
rInterim.items = rItemList;
return rInterim;
});
Helping-method for retrieving information for each item in Rss. Please consider using the overload with maxConcurrency, because on default it will subscribe to every stream at once. Therefore flatMap would create many http-requests.
private Observable<List<RssItem>> getRssItemInformation(Rss rss) {
return Observable.fromIterable(rss.items)
.flatMap(rssItem -> getImageUrl(rssItem).subscribeOn(Schedulers.io()), (rItem, img) -> {
RssItem item = new RssItem();
printCurrentThread("merge1");
item.image = img;
item.link = rItem.link;
return item;
}).toList().toObservable();
}
Helping-method for retrieving the image url. Returning observable is not opinionated about concurrency. If an error occurs, an empty string will be returned as default value.
private Observable<String> getImageUrl(String link) {
return Observable.fromCallable(() -> Jsoup.connect(link).get())
.map(document -> document.select("div[itemprop=image] > img").first())
.map(element -> element.attr("src"))
.onErrorResumeNext(throwable -> {
return Observable.just("");
});
}
You may look at the full example at github.gist: https://gist.github.com/anonymous/a8e36205fc2430517c66c802f6eef38e
You can't mix internal parameter of one RxJava parameter (flatMap lambda parameter) with another operator parameter (defaultIfEmpty).
First of all, create a helper function to keep main reactive stream cleaner:
private Observable<List<Item>> getDetails(List<Item> items) {
return Observable.from(items)
.observeOn(Schedulers.io())
.flatMap(Checked.f1(item ->
Observable.zip(
Observable.just(item),
Observable.just(Jsoup.connect(item.link).get())
.observeOn(Schedulers.io())
.map(document -> document.select("div[itemprop=image] > img").first()),
(itemInner, element) -> {
itemInner.image = element.attr("src");
return itemInner;
}
)
))
.toList();
}
Then reformat main function:
newsAppService.getDumpData()
.flatMap(rss ->
Observable.zip(
Observable.<Rss>just(rss),
getDetails(rss.channel.items),
(rssInner, items) -> {
rssInner.channel.items = items;
return rss;
}).onErrorResumeNext((throwable -> Observable.just(rss))
)
)
.observeOn(Schedulers.io())
.subscribeOn(AndroidSchedulers.mainThread());
Hope I got your aim properly. It may not work, as I am unable to test it, however I hope you get the idea. The reason I used .zip functions it that you can't loose the reference to the currently parsed item or rss

Categories

Resources