I'm using the new Android persistance lib, Room, with RxJava2. The following code is causing an infinite loop. If I comment out the line that updates the user in the second observable it works fine. If I leave it there, the onNext method of the first observable will be called on and on again.
Does Room requery the table when an entity is updated? If so, why is it publishing the message again on the same stream? Is this intended behavior? Is it a bug in the library?
val userDao = HeyHeyApp.database.userDao();
userDao.getAll()
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe({ listOfUsers ->
if (!listOfUsers.isEmpty()) {
HeyHeyApp.currentUser = listOfUsers.first()
HeyHeyApp.currentUser.fcmDeviceId = getDeviceId()
Single.fromCallable({
HeyHeyApp.database.userDao()
.updateUser(HeyHeyApp.currentUser)
}).subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe({ _ ->
})
}
})
When you subscribes for userDao.getAll() event - Room will trigger your observer onNext() method each time when databases data is changed. And next in your onNext() method you change the data in database:
Single.fromCallable({
HeyHeyApp.database.userDao()
.updateUser(HeyHeyApp.currentUser)
}).subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe({ _ ->
})
and after that Room call your onNext() method again... and so on.
Related
I have a list of Items I want to map and then insert into a Room table:
Room
#Insert(onConflict = OnConflictStrategy.REPLACE)
fun insert(vararg projectLocal: ProjectLocal): Completable
The FIRST approach to save data:
Observable.fromIterable(remoteProjects)
.map { project ->
...
mProjectMapper.mapRemoteToLocal(project)
}
.observeOn(AndroidSchedulers.mainThread())
.subscribeOn(Schedulers.io())
.subscribe {
mProjectRepository.saveProject(it)
}
As you may see I'm observing on -> main thread and subscribing on -> io
The Second approach to save data:
remoteProjects.forEach { remote ->
...
val project = mProjectMapper.mapRemoteToLocal(remote)
mProjectRepository.saveProject(project)
.observeOn(AndroidSchedulers.mainThread())
.subscribeOn(Schedulers.io())
.subscribe()
}
Which one makes more sense? Is there any better way to save all this data inside a Room database using RxJava?
I think this is what #Mwasz means:
Observable.fromIterable(remoteProjects)
.map { project ->
mProjectMapper.mapRemoteToLocal(project)
}
.toList()
.flatMapCompletable {
mProjectRepository.saveProject(it)
.subscribeOn(Schedulers.io())
}
.subscribe()
You could also use reduce or collect instead of toList but toList() is the simplest.
I'm using rxJava and I want to do a forEach of a list, and for every item, make a call, and then once those calls are finished, call another one.
This is my code
val flowableList = answerListCreated.map {
questionService.addAnswerToQuestion(
questionId,
it.id,
MyUtils.getAccessTokenFromLocalStorage(context = mContext!!)
)
}
disposable = Flowable.concat(flowableList)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe {
addCorrectsAnswersToQuestion(questionId)
}
But it's joining in the subscribe twice, and it should join in the subscribe once.
What I'm missing? I thought concat should be a good option because I've read that it does first the first job, and then when job1 is finished it starts the job2.
Well, also if necessary I can return Observable<T>, from now in my service I'm returning Flowable<T> to test this.
i think you need to do something like:
val disposable = Flowable.fromArray(answerListCreated)
.flatMap {
questionService.addAnswerToQuestion(
questionId,
it.id,
MyUtils.getAccessTokenFromLocalStorage(context = mContext!!)
)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
}
.toList()
.subscribe {
addCorrectsAnswersToQuestion(questionId)
}
Hi i am trying to implement a Single observable that chains two requests together.
In between the two requests i make, i notify a callback to update the UI with the response from request one and then launch the next request in the Schedulaers.io thread.
The issue i am having is that it tries to update the UI from the schedulars.io thread too and results to nothing being updated in the ui thread.
i cold wrap the calback on RunOnUiThread code block in android but wondering if there is a more elegant way of doing it?
i checked couroutines and it seems to just deal with putting a block of code in a seperate thread.
Here is my current code
override fun getHomeScreenInformation() {
delegator.requestOne()
.flatMap { responseOne->
homeScreenCallBack.onResponseOneRecieved(responseOne)
delegator.requestTwo()
}
.observeOn(AndroidSchedulers.mainThread())
.subscribeOn(Schedulers.io())
.subscribe(
{responseTwo-> homeScreenCallBack.onResponseTwoRecieved(responseTwo)},
{error -> homeScreenCallBack.onError()}
)
}
Apply observeOn(AndroidSchedulers.mainThread()) as many times as necessary:
delegator.requestOne()
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread()) // <----------------------
.flatMap { responseOne ->
homeScreenCallBack.onResponseOneRecieved(responseOne)
delegator.requestTwo()
.subscribeOn(Schedulers.io()) // <----------------------
}
.observeOn(AndroidSchedulers.mainThread())
.subscribe(
{responseTwo-> homeScreenCallBack.onResponseTwoRecieved(responseTwo)},
{error -> homeScreenCallBack.onError()}
)
How can I read a flowable list of values from room and convert it to another object which is a combination of more values from room
database.leadsDao().getLeads(leadState.name)
.flatMap {
val len = it.size.toLong()
Flowable.fromIterable(it)
.flatMap {
Flowable.zip(
database.orderDao().getById(it.orderId),
database.orderMedicineDao().getByOrderId(it.orderId),
database.patientDao().getById(it.patientId),
Function3<Order, List<OrderMedicine>, Patient, LeadDetail>
{ order, orderMedicines, patient -> LeadDetail.from(it, patient, order, orderMedicines) })
}
.take(len)
.toList()
.toFlowable()
}
The code above works but I don't like the take(len) part. And without it, the stream never calls onNext of the subscriber. The stream keeps waiting for more items, which shouldn't happen since Flowable.fromIterable gives finite number or items and then ends. I.e., the code below doesn't work
database.leadsDao().getLeads(leadState.name)
.flatMap {
Flowable.fromIterable(it)
.flatMap {
Flowable.zip(
database.orderDao().getById(it.orderId),
database.orderMedicineDao().getByOrderId(it.orderId),
database.patientDao().getById(it.patientId),
Function3<Order, List<OrderMedicine>, Patient, LeadDetail>
{ order, orderMedicines, patient -> LeadDetail.from(it, patient, order, orderMedicines) })
}
.toList()
.toFlowable()
}
Flowable.fromIterable gives finite number or items and then ends.
But the Flowable.zip inside of the flatmap will not end, since Room's DAO objects emit the current value AND all future updates, so the database.*() calls that are zipped together are not finite. If you add a .first() call to the inner Flowable.zip the second version should work as well.
I'm trying to use Realm with RxJava and Retrofit in a way DanLew described here concating input from realm and retrofit but it gets stuck if I adding realm into the chain
Observable.concat(countryStorage.restoreAsObservable(),
networkService.api()
.getCountries()
.doOnNext(countryStorage::save))
.first()
.observeOn(AndroidSchedulers.mainThread())
.subscribe(//never reaching here)
storage
#Override public Observable<List<Country>> restoreAsObservable() {
Realm realm = realmProvider.get();
return realm.where(Country.class)
.findAll()
.asObservable()
.map(countries -> return realm.copyFromRealm(countries))
.first(countries -> return !countries.isEmpty())
.doOnCompleted(realm::close());
}
It seems that this could happen that observable is hot from Realm, but nothing about it in the docs and how I suppose to compose Realm with other observables?
UPDATE:
It turns to be that it works fine in old way. The question still remain about new api.
return Observable.just(
realm.copyFromRealm(realm.where(Country.class).findAll()))
.filter(countries -> !countries.isEmpty())
.doOnCompleted(realm::close);
It is happening because countryStorage.restoreAsObservable() never completes and if you read concat doc, it explicitly states that:
Concat waits to subscribe to each additional Observable that you pass to it until the previous Observable completes.
Instead you can just do something like:
countryStorage.restoreAsObservable()
.doOnSubscribe(() -> {
networkService.api()
.getCountries()
.subscribe(countryStorage::save)
})
.observeOn(AndroidSchedulers.mainThread())
.subscribe(//do smth)