I use Retrofit to interact with a REST API and RxJava do manipulate the data I receive.
In the code snippet below I make an API call and use the map operator to save the data I receive before moving on with other operations against the stream.
retrofitApi.registerDevice(mDevice)
.map(new Func1<Device, Device>() {
#Override
public Device call(Device device) {
// The only purpose of the map() operator
// is to save the received device.
magicBox.saveDeviceId(device.getId());
return device;
}
})
.otherOperations()...
My question is: is there a better operator for the job? I feel like I misuse the map operator.
Following Egor's answer I did some research and, based on Dan Lew's blogpost and this question, the correct answer appears to be
doOnNext.
Looks like doOnEach() is what you're looking for, however haven't tried it myself.
Related
I wrote the code as below.
suspend fun getDataByRegion(): Flow<Result?> {
// getRegion() return Flow<Region>
return getRegion().map { region: Region ->
repository.requestDataFromServer(region)
}
}
The problem is that repository.requestDataFromServer(region) called twice.
So I think I should use operators like zip or combine.
When using these operators, how can the second flow use the data of the first flow?
With combine and zip operators you can not depend on the other's result. So in general your chaining approach with map is OK.
There is several options you have:
Assuming your repository method is not called from anywhere else, the reason for it being called twice is that the region Flow is emitting twice. So try to find out why this is the case.
Anyhow if your region Flow method returns the same region twice you can fix it by simply adding
.distinctUntilChanged() after getRegion() like:
getRegion().distinctUntilChanged().map { region: Region ->
repository.requestDataFromServer(region)
}
It will make sure your region Flow doesn't emit redundantly with the same data. Alternatively add distinctUntilChanged() directly to the repository method, if this is always the expected behavior.
Ask yourself if this method really needs to return a stream (Flow). I guess you need a stream since the region can change at runtime and you want something in your app to update automatically? But if not you could simply convert the stream to a single result:
val region = getRegion().first()
repository.requestDataFromServer(region)
I'm quite new to reactive programming and I've introduced myself to RxJava2 in Android. For the time being, I've faced easy problems, such as zipping observables. But now, something new cropped up, I'm trying to explain.
Suppose I've got a list of requests Observable<List<Request>>. What I want to do is to call a web service which returns per each request, the list of routes (wrapped in an Observable). I've checked questions like this, but in this case I think I can't flatMap an observable and a list of observables.
How can I do it? Is there any other operator?
You can flatten the Observable<List<Request>> into Observable<Request> using flatMapIterable. Assuming you have a helper method with the signature Observable<List<Route>> getListOfRoutes(Request request) { ... } you can do this:
Observable<List<Request>> obs = ...;
obs.flatMapIterable(l -> l)
.flatMap(request -> getListOfRoutes(request)
.doOnNext(routes -> request.setRoutes(routes))
.map(ign -> request)
)
...
This is assuming that you ultimately want Observable<Request> to be emitted downstream. If you want a different type, you can do something different in the map operator to suit your needs.
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!
I am using RxJava in my Android project and I'm happy about it. I'm currently using it to make all my DAO methods asynchronous and make UI listens on them.
But I have a big problem, that is, when I retrieve some data from database using Observable<List<User>> getLists(), I need to use List<User> in my ViewModels, but I cannot extract it from the observable.
I would like to know what is the common approach to solve this kind of problem ? I searched on Internet and people said it's not recommended to extract the objects, but in this case how can I use the data from database and at the same time still enable the observers listening ?
Should I create another method using AsyncTask ??
Thanks.
In my UserRepo.java
public Observable<List<User>> getUsers() {
return colisDao.getUsers();
}
In HomeScreenViewModel.java:
public List<User> getUsers() {
return userRepo.getUsers(); // do not work because I need a List<User>
}
In HomeActivity.java:
UserListAdapter userListAdapter = new UserListAdapter(this,
vm.getUsers());
Central idea of reactive extensions is to make use of events' streams observation and timely processing.
So actually, if you need to retrieve data in a straightforward way, I'd say you don't need RxJava2 at all. Still, if you want to stick to the reactive approach, the data stream should be listened to instead.
All RxJava2 types provide a subscribe method that "notifies" the source of data that's lazy by nature that here is an observer that wants to receive the data, so all the data processing flow described by use of RxJava2 operators will become alive.
The most painless approach is to change HomeActivity's code to this:
vm.getUsers()
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(userListAdapter::populateWithNewDataSet);
, assuming that adapter will have the mentioned method that will update the UI data set using something like notifyDataSetChanged() (or DiffUtil, for instance) internally.
By doing that the data source is now observed and every time the update is emitted the UI will be repopulated with the most recent data.
P.S.: I've just demonstrated the simplest way to do the thing, but it is up to the developer where to place RxJava-related code: be it ViewModel, Activity, or even some other component. RxJava is a convenient tool to use and it can make complicated asynchronous flow simple, but the problem with RxJava arises when all the code base is dependent on it. The code base can then quickly become unmanageable, fragile and rigid if the tool was used in an improper place.
Adding on #AndreyIlyunin very good answer, You could also use MutableLivedata in your Viewmodel to save the List in the viewmodel as Livedata and observe changes to it in your Activity. This is suggested by Google as a way to maintain MVVM architecture. Something like:
In HomeScreenViewModel.java:
private final MutableLivedata<List<User>> users = new MutableLivedata<>();
public void getUsers() {
return userRepo.getUsers()
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(this::onUsers)
}
private void onUsers(List<> list){
users.setValue(list);
}
public MutableLivedata<List<User>> getUserList(){
return users;
}
In HomeActivity.java, in onCreate() add:
vm.getUserList().observe(this,this::onUserList);
and add following methods to activity:
private void onUserList(List<> list){
userListAdapter = new UserListAdapter(this,list);
}
and then from your activity call:
vm.getUsers();
The getUsers() call is made asynchronously in the background, and you get the userList reactivly.
I am trying to use RxJava in combination with Retrofit in my Android app.
I came to a point where I need to retrieve (GET) a url from some model class, and if its not available, POST to another endpoint to generate it, then retrieve it with GET again.
The idea is that the end result is always a url (provided there was not an error, of course), so if its not there, we create it and then retrieve it.
I can do this with RxJava by just nesting calls, but this does not seem to "reactive", so I thought maybe the and-when-then sounds like what im looking for? I tried looking for examples but these three words are quite difficult to search due to them being so common.
Is it possible to combine observables to achieve what I describe?
Let's assume you have the following methods:
apiManager.makeGETRequest(); //returns Url or throws an Exception
apiManager.makePOSTRequest(); //returns Url
Use handy onErrorResumeNext operator:
apiManager.makeGETRequest()
.onErrorResumeNext(t -> apiManager.makePOSTRequest())
...
It intercepts an Exception and replaces it with Observable. In your case if makeGETRequest() succeeds, than downstream will ignore onErrorResumeNext, otherwise apiManager.makePOSTRequest() will be called.
P.S. Don't forget to check what kind of exception you receive in onErrorResumeNext.
I hope this can be useful for you.
https://github.com/darylteo/rxjava-promises/tree/master/rxjava-promises
If you need code sample for retrofit + rx usage you can follow this link.
http://randomdotnext.com/retrofit-rxjava/