I'm using Kotlin and I have an Observable<List<FlickPhoto>> model. Each FlickrPhoto contains a List, and each Photos model contains a List<Photo>.
I want to transform a List<FlickrPhoto> to a List<PhotoEntity>, which is basically another model with less data, and wrap it into an Observable (in order to finally get an Observable<List<PhotoEntity>>).
How can I do this in the best RxJava way?
I have created a PhotoEntityMapper class with the following method that I'm sure could be properly used for the purpose:
override fun transform(photoCollection: List<Photo>): List<PhotoEntity>
Thanks in advance!
The solution in your comment does work:
// original
remoteDataSource
.getPhotos(photoTag)
.flatMap({ t: FlickrPhoto ->
Observable
.just(serviceMapper.transform(t.photos.photo)) // <-- not lazy
})
Here you have slightly different options:
// v1
remoteDataSource
.getPhotos(photoTag)
.flatMap {
Observable
.just(it.photos.photo)
.map { serviceMapper.transform(it) } // <-- lazy transformation
}
// v2
remoteDataSource
.getPhotos(photoTag)
.flatMapIterable { it }
.map { serviceMapper.transform(it.photos.photo) }
No option is that much better than the others. Normally I would go with v2 because it is shorter.
But, I don't know what the serviceMapper does. For example, if it involved network and I wanted to execute it in another Scheduler, then v1 is nice because you can subscribe the transformations in another thread (which you can't do in the original version because the transformation is pre-computed eaguerly). Example:
// v1 - with scheduler
remoteDataSource
.getPhotos(photoTag)
.flatMap {
Observable
.just(it.photos.photo)
.map { serviceMapper.transform(it) } // <-- lazy transformation
.subscribeOn(Schedulers.io()) // <-- schedule on io()
}
Related
I'm trying to learn to use Rxjava to make Api calls with retrofit. I have to make multiple api calls in a loop. I'm struggling with getting the value in my subscriber.
#GET("pokemon" + "/{id}")
fun getPokemonData(#Path("id") id: Int):
Observable<Pokemon>
I'm expecting to get a Pokemon object in my Subscriber but instead I get a Observable. How do I transform it to a Pokemon object?
Observable.fromIterable(list)
.flatMap { it ->
Observable
.just(it.url)
.map { PokeApi.retrofitService.getPokemonData(getPokemonIdFromUrl(it))
}
}
.subscribeOn(Schedulers.newThread())
.observeOn(AndroidSchedulers.mainThread())
.subscribe({
//onNext --I'm expecting to get a Pokemon Object here, instead I get a Observable<Pokemon>
}, {//onError} , {// do something when all api calls are done?})
My goal is to make api calls with ids in the "list" and get "notified" when all the api calls are finished. Is this the correct approach to solve this problem ?
The problems lies here:
Observable
.just(it.url)
.map { PokeApi.retrofitService.getPokemonData(getPokemonIdFromUrl(it)) }
When you use map it maps to the return object from getPokemonData. You probably want to flatMap it:
Observable
.just(it.url)
.flatMap { PokeApi.retrofitService.getPokemonData(getPokemonIdFromUrl(it)) }
which not only maps the result but flattens it too so you don't get an observable, but the result of that observable.
I have these piece of code and I want it to make it more optimal
I guess I can use kotlin-flow's flatMapMerge
but I don't think how should I convert my code to flow
val quiries = listof("s","b","s","g","d","r","t")
quiries.map { query ->
viewModelScope.launch {
val results = fetchColumns(query)
resultMutableData.postValue(results)
}
}
and fetchColumns() are suspended functions
I am thinking maybe I need to have flows of queries ???? what is the way of using flatMapMerge()?
https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.flow/flat-map-merge.html
Try using something like this:
listOf("s","b","s","g","d","r","t").asFlow()
.map { fetchColumns(it) }
.onEach { resultMutableData.postValue(it) }
.launchIn(viewModelScope)
Since you don't switch onto another flow, there is no need for any of flatMap* functions, just map will be enough. Moreover, map parameter is already declared as suspend, so you won't block your thread.
But map operator was designed to process data sequentially, so these transformations won't be run in parallel. To achieve parallel processing, a workaround using flatMapMerge can be used:
listOf("s","b","s","g","d","r","t").asFlow()
.onEach { println("onEach: $it") }
.flatMapMerge {
flow {
emit(fetchColumns(it))
}
}
.onEach { resultMutableData.postValue(it)) }
.launchIn(viewModelScope)
I want to implement method to edit a note, save it to local database (cache) and then send it to the server as a POST request. I am learning RxJava and I wanted to create Observable from the note and then apply transformations on it, like to map it to an Entity model and saving. The issue that my method returns Completable and this chain returns Observable<Completable>. How to unwrap the Completable from this Observable which I used only to start RxJava stuff. Each editNote() methods returns a Completable.
override fun editNote(note: Note): Completable {
return Observable.just(note)
.map { mapper.mapToEntity(it) }
.map { noteEntity ->
factory.getCacheDataStore().editNote(noteEntity)
.andThen { factory.getRemoteDataStore().editNote(noteEntity) }
}
}
=======================================================
UPDATE
Finally, I managed to find "a solution" but I am not sure it is correct :-)
override fun editNote(note: Note): Completable {
return Observable.just(note)
.map { mapper.mapToEntity(it) }
.flatMapCompletable { noteEntity ->
factory.getCacheDataStore().editNote(noteEntity)
.andThen { factory.getRemoteDataStore().editNote(noteEntity) }
}
}
You're looking for flatMapCompletable instead of map, because map just intercepts the stream and maps the emissions to another type, while 'flatMap' (or it's siblings), from the docs:
Transform the items emitted by an Observable into Observables, then flatten the emissions from those into a single Observable.
You can see it's marble diagram in Here
I'm using RxKotlin to build out my latest Android app, and I've come up against a familiar issue: how to handle network errors in an Rx-like way.
I have a stream set up for search terms against a TextView like this:
searchBar
.queryTextObservable()
.debounce(500, TimeUnit.MILLISECONDS)
.map { it.trim() }
.filter { it.isNotBlank() }
.observeOn(Schedulers.io())
This is a useful way to listen against changes to text input, so I then extend the code to feed the prepared text into a network request (using the Retrofit library with the RxJava extension) to search against:
searchBar
.queryTextObservable()
.debounce(500, TimeUnit.MILLISECONDS)
.map { it.trim() }
.filter { it.isNotBlank() }
.observeOn(Schedulers.io())
.switchMap { search(it) }
.observeOn(AndroidSchedulers.mainThread())
.subscribeOn(AndroidSchedulers.mainThread())
.subscribe(...)
The problem happens when there's a network error - my entire subscription is cancelled. It seems like I have some options to manage the failure, but none of them seem very clean:
Have an inner observable after text input is complete that makes the network request
Use onErrorResumeNext and pass a sentinel value
This is obviously not exhaustive, but what is the appropriate pattern(s) to gracefully handle network errors while preserving the stream (and hence the usefulness) of user input from the search bar?
Using operators such as onErrorReturn is a pretty standard approach if you look at reactive patterns which provide a unidirectional data flow such as MVI.
Following such patterns you typically map the state of your network call into an object which represents the state of the call.
A simple example without any MVx patterns would look something like below, where the observable from your RXBinding invokes a call to the API, but instead of just returning the data from the API it returns a state object which can then be rendered on screen.
private val disposables = CompositeDisposable()
override fun onStart() {
super.onStart()
disposables.add(
RxView.clicks(load_data_button)
.flatMap { requestData() }
.observeOn(AndroidSchedulers.mainThread())
.subscribe(this::renderRequestState)
)
}
override fun onStop() {
disposables.clear()
super.onStop()
}
private fun requestData(): Observable<RequestState> {
return myApi.requestData()
.toObservable()
.subscribeOn(Schedulers.io())
.map<RequestState>(RequestState::Success)
.onErrorReturn(RequestState::Error)
.startWith(RequestState.InFlight)
}
private fun renderRequestState(requestState: RequestState) {
when (requestState) {
RequestState.InFlight -> showProgress()
is RequestState.Success -> showResult(requestState.result)
is RequestState.Error -> showError(requestState.error)
}
}
sealed class RequestState {
object InFlight : RequestState()
data class Success(val result: MyData) : RequestState()
data class Error(val error: Throwable) : RequestState()
}
Hannes Dorfmann wrote a great set of articles on the MVI pattern which utilises this approach.
http://hannesdorfmann.com/android/model-view-intent
http://hannesdorfmann.com/android/mosby3-mvi-1
My intention is to create an Observable that can be made to emit an item inside a class.
My goal is to create a ViewModel, which can expose a stream to a View in Android, reactively delivering items.
My approach is as follows:
class MyMVVM {
private lateinit var timeSelectedEmitter: ObservableEmitter<Int>
val timeSelectedObservable: Observable<Int> = Observable.create { emitter -> timeSelectedEmitter = emitter }
}
This should, in theory and as far as I understand it currently, give me access to the emitter, which then can be called at any point to deliver time-events to subscribers of timeSelectedObservable (subscribers which are inside the View)
1) Is this a correct approach or should this be done in another way?
2) Is there a way for the emitter to be created on Observable creation instead of when a subscriber is subscribing to the Observable (to avoid nullpointer exceptions or Kotlin exceptions)?
Thanks.
I usually create the Observable once and return the same Observable to anything that wants to subscribe to it. This way, if there are multiple subscribers, a new Observable doesn't need to be created each time.
So something like:
class ViewModel {
private val timeObservable = PublishSubject.create<Int>()
fun timeObservable(): Observable<Int> = timeObservable
fun update() {
// ...
timeObservable.onNext(time)
}
}
class Activity {
...
viewModel.timeObservable()
.subscribe {
time -> ...
})
}