android app Using Flow/coroutine on top of Rxjava2 - android

I want to use coroutine in my kotlin app for must of it. but this app depends a lot on a service which return rxjava2 type object (so our input will be all the rxjava type). How can we still use coroutine in this model app instead of rxjava2. should we create a layer only convert rxjava object to normal object (or coroutine object?). or can we use both two together like:
SomeObservable
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe { value->
viewmodelScope.launch{
// send a flow or coroutine suspend fonction with value
}
},
Thanks,

You could wrap the RxJava functions with suspend functions.
See https://github.com/Kotlin/kotlinx.coroutines/issues/869

I want to answer my own question, It's not a good approche to use flow and rx at the same time. because it resolves the same problem. Rxjava + livedata is a clean architecture to go. or just use flow. so for resume:
Rxjava + livedata (a little bit coroutine to do some general
operation)
flow + coroutine
Two clean ways to make a good architecture. so if I have already rx type there is no need to convert to flow and reuse.

Related

Android Convert RXJava 2 Maybe to a coroutine?

My project intends to replace RxJava with coroutines.
Use RxJava Maybe.concat to access network and database data.
First search for the data in the database, if there is no data in the database, download the data from the Internet.
If the database has data, the network data will not be downloaded.
Is there a way for coroutine to achieve a similar function?
And, Is it necessary to use 'kotlinx-coroutines-rx2'?
fun getMovieImg(): Flowable<MovieBean> {
return Maybe.concat(
CacheModelFactory.getMovieImgDisk().firstElement(), // database
getMovieImgNetwork().firstElement() // net
).firstElement().toFlowable()
}
Thanks for help!
Yes, this is possible with coroutines and because coroutines were invented specifically to allow regular, sequential programming even with IO, it is actually much easier than with RxJava. We don't need any maybes, streams, flows or whatever. We implement this the same way as for the regular, non-IO code:
suspend fun getMovieImg(): MovieBean = getMovieImgDisk() ?: getMovieImgNetwork()
suspend fun getMovieImgDisk(): MovieBean? { ... }
suspend fun getMovieImgNetwork(): MovieBean { ... }
Assuming you replace RxJava entirely in your application, you don't need kotlinx-coroutines-rx2 for this. But if you only create a coroutine adapter on top of the existing code using RxJava, then you will probably need some utils that will help you to convert RxJava objects to coroutines/suspendable code.

RxJava chaine of consecutive operations

I am a newbie in RxJava. I want to combine three consecutive asynchronous operations in to chain by RxJava2:
fun getDataFromRESTApi(): Observable<TheData>
saveDataToCache(theData: TheData): Completable
getDataFromCache(): Observable<TheData>
How can I do this? What rxjava methods I could to use?
I'm not sure what you trying to achieve exactly but there's the concatMap operator :
getDataFromRESTApi()
.concatMap(theData -> saveDataToCache(theData).toObservable())
.concatMap(cachedData -> getDataFromCache()) //maybe not needed
In the other hand if saveDataToCache returns the saved data (cached) you don't need the third line.

Kotlin Flow vs LiveData

In the last Google I/O, Jose Alcerreca and Yigit Boyar told us that we should no longer use LiveData to fetch data. Now we should use suspend functions for one-shot fetches and use Kotlin's Flow to create a data stream. I agree that coroutines are great for one-shot fetching or other CRUD operations, such as inserting, etc. But in cases where I need a data stream, I don’t understand what advantages Flow gives me. It seems to me that LiveData is doing the same.
Example with Flow:
ViewModel
val items = repository.fetchItems().asLiveData()
Repository
fun fetchItems() = itemDao.getItems()
Dao
#Query("SELECT * FROM item")
fun getItems(): Flow<List<Item>>
Example with LiveData:
ViewModel
val items = repository.fetchItems()
Repository
fun fetchItems() = itemDao.getItems()
Dao
#Query("SELECT * FROM item")
fun getItems(): LiveData<List<Item>>
I would also like to see some examples of projects using coroutines and Flow to work with the Room or Retrofit. I found only a Google's ToDo sample where coroutines are used for one-shot fetching and then manually refetch data on changing.
Flow is sort of a reactive stream ( like rxjava ). There are a bunch of different operators like .map, buffer() ( anyway less no. Of operator compared to rxJava ). So, one of the main difference between LiveData and Flow is that u can subscribe the map computation / transformation in some other thread using
flowOn(Dispatcher....).
So, for eg :-
flowOf("A","B","C").map { compute(it) }.flowOn(Dispatchers.IO).collect {...} // U can change the execution thread of the computation ( by default its in the same dispatcher as collect )
With LiveData and map , the above can't be achieved directly !
So its recommended to keep flow in the repository level , and make the livedata a bridge between the UI and the repository !
The main difference is that
Generally a regular flow is not lifecycle aware but liveData is lifecyle aware. ( we can use stateFlow in conjunction with repeatOnLifecycle to make it lifecycle aware )
flow has got a bunch of different operators which livedata doesn't have !
But again , Its up to u how do u wanna construct your project !
As the name suggests, you can think of Flow like a continuous flow of multiple asynchronously computed values. The main difference between LiveData and Flow, from my point of view, is that a Flow continuously emits results while LiveData will update when all the data is fetched and return all the values at once. In your example you are fetching single values, which is not exactly what Flow was dsigned for [update: use StateFlow for that].
I don't have a Room example but let's say you are rendering something that takes time, but you wanna display results while rendering and buffering the next results.
private fun render(stuffToPlay: List<Any>): Flow<Sample> = flow {
val sample = Sample()
// computationally intensive operation on stuffToPlay
Thread.sleep(2000)
emit(sample)
}
Then in your 'Playback' function you can for example display the results where stuffToPlay is a List of objects to render, like:
playbackJob = GlobalScope.launch(Dispatchers.Default) {
render(stuffToPlay)
.buffer(1000) // tells the Flow how many values should be calculated in advance
.onCompletion {
// gets called when all stuff got played
}
.collect{sample ->
// collect the next value in the buffered queue
// e.g. display sample
}
}
An important characteristic of Flow is that it's builder code (here render function) only gets executed, when it gets collected, hence its a cold stream.
You can also refer to the docs at Asynchronous Flow
Considering that Flow is part of Kotlin and LiveData is part of the androidx.lifecycle library, I think that Flow is used as part of the uses cases in clean architecture (without dependencies to the framework).
LiveData, on the other hand, is lifecycle aware, so is a match with ViewModel
I have all my architecture using livedata at this moment, but Flow looks like an interesting topic to study and adopt.

Editing data in repository pattern using RxJava

I'm refactoring the implementation of my repositories using RxJava so i want to know some ways to edit, for example, a user.
My getUser(email: String), with email as id, is returning an observable and in the repository implementation i either get the data from database or server, all good by now.
What i want to achieve is editing a user. For that i would have and update(user: User) function, and the naive way to use it would be
userRepository.getUser(email)
.subscribeOn(Schedulers.io())
.subscribe { user ->
user.name = "antoher name"
userRepository.update(user)
.subscribeOn(Schedulers.io())
.subscribe {
//handle response
}
}
Is there a way to avoid this type of call of an observer inside an observer? It is not very readable for me and i guess there's a better way but i'm not getting it.
NOTE: I'm using clean architecture, so i think an update for every field, making me get user in data module is not correct as i would have subscribe to an observer in data, and that difficult the dispose when activity destroys
For me is not the same question as When do you use map vs flatMap in RxJava? because, despite of flatMap being the thing that answer the question, it is not the same question, so anyone who has the same problem/question but don't know that flatmap is the answer, will never reach to use flatmap.
One strength of using RxJava is that you can chain as many async operations (method that would return Observable or Single, repository methods in your case) as you want without falling into callback hells. You see in your code that there are nested subscribe blocks. What if you had to chain more async network operations? You fall into callback hells and the code will become harder to follow and maintain.
Removing nested callbacks and making code more functional, compositional, and readable is one thing RxJava is really good at. In the intro part of ReactiveX website , they mention about this in the intro part of ReactiveX website (http://reactivex.io/intro.html).
Callbacks solve the problem of premature blocking on Future.get() by
not allowing anything to block. They are naturally efficient because
they execute when the response is ready.
But as with Futures, while callbacks are easy to use with a single
level of asynchronous execution, with nested composition they become
unwieldy.
Flatmap operator is to the rescue here. You can look into the definition of flatMap operator in the link below.
http://reactivex.io/documentation/operators/flatmap.html
Below is the code I would use in your case.
userRepository.getUser(email)
.subscribeOn(Schedulers.io())
.map { user -> user.name = "another name"; return user; }
.flatMap { user -> userRepository.update(user) }
.doOnSuccess { /* handle response here */ } // doOnNext if you are using observable
.subscribe({ /* or handle response here */ }, { /* must handle error here */})
Flatmap operator flattens Single of update response which will be returned by your repository's update method and pass just the response downstream. Above code is not only easier to read but also makes your code reusable because update logic is now part of the chain.
Distinguishing between map and flatMap is really important in exploiting the full benefit of RxJava so it will be really beneficial to get used to it!

How to use Subject (AsyncSubject, BehaviorSubject...) in RxJava2 like the way RxJava1 did?

In Rxjava1 we can subscribe Subject to an Observable like this:
val asyncSubject = AsyncSubject<T>.create()
Observable<T>.subscribe(asyncSubject);
asyncSubject.subscribe(...)
Can't figure out how to achieve the same thing in Rxjava2 Flowable?
I'm thinking about something like this
Flowable<T>
.doOnComplete { t -> asyncSubject.OnComplete() }
.subscribe { t -> asyncSubject.onNext(t) }
Is there better/more concise way?
RxJava2 Separated things out into Flowable and Observable. Flowables are used when you want back-pressure.
Due to this break-out, we have two different types of Subject objects, your regular old BehaviorSubject, etc. which work with Observable, and a new set of classes called Processors, such as BehaviorProcessor, etc.
These classes work in mostly the same way as subjects, but are utilizable with Flowables instead of Observables.
In your case, you would want to use a Processor instead of a subject:
val proc = AsyncProcessor.create<Int>()
val flowable = Flowable.just(1)
flowable.subscribe(proc)

Categories

Resources