How can I handle multiple queries used to populate models using RXJava Observables?

We use ReactiveX and Retrofit in our network stack to process all API requests asynchronously.

Our goal is to create one method that will return a fully populated collection of User models. Each User model has a list of Pet objects. We can get all User models with one request. However, Pet models must be requested for User .

Getting users is simple:

 // Service.java @GET("users/?locationId={id}") Observable<List<User>> getUsersForLocation(@Path("id") int locationId); @GET("pets/?userId={id}") Observable<List<Pet>> getPetsForUser(@Path("id") int userId); // DataManager.java public Observable<List<User>> getUsersForLocation(int locationId) { return api.getUsersForLocation(locationId); } public Observable<List<Pet>> getPetsForUser(int userId) { return api.getPetsForUser(userId); } 

We would like to find a convenient way (RX-style) for cycling through the User list, choosing Pet for each user, assigning it to User and, ultimately, returning the Observable<List<User>> .

I am new to RX. I looked through the documentation and tried to use various methods like flatMap() and zip , however I still have to find the exact combination of transformations or combinators to make this happen.

+6
source share
1 answer

I wrote a small sample application that does what you are trying to achieve. Here are the components:

 public class Datasource { public Observable<List<User>> users() { return Observable.just(Arrays.asList( new User("1", "Foo"), new User("2", "Bar") )); } public Observable<List<Pet>> pets(User user) { return Observable.just(Arrays.asList( new Pet(user.getName() + " cat"), new Pet(user.getName() + " dog") )); } } 

Pet class:

 public class Pet { private String mName; public Pet(String name) { mName = name; } public String getName() { return mName; } @Override public String toString() { return "Pet{" + "mName='" + mName + '\'' + '}'; } } 

User Class:

 public class User { private String mName; private String mId; private List<Pet> mPetList; public User(String id, String name) { this(id, name, Collections.<Pet>emptyList()); } public User(String id, String name, List<Pet> pets) { mName = name; mId = id; mPetList = pets; } public String getName() { return mName; } public String getId() { return mId; } public User copyWithPets(List<Pet> pets) { return new User(mId, mName, pets); } @Override public String toString() { return "User{" + "mName='" + mName + '\'' + ", mId='" + mId + '\'' + ", mPetList=" + mPetList + '}'; } } 

Together:

 datasource.users() .flatMap(new Func1<List<User>, Observable<User>>() { @Override public Observable<User> call(List<User> users) { return Observable.from(users); } }) .flatMap(new Func1<User, Observable<User>>() { @Override public Observable<User> call(final User user) { return datasource.pets(user) .map(new Func1<List<Pet>, User>() { @Override public User call(List<Pet> pets) { return user.copyWithPets(pets); } }); } }) .toList() .subscribe(new Action1<List<User>>() { @Override public void call(List<User> users) { for (User user : users) { Log.d(TAG, "user: " + user.toString()); } } }); 

He produces:

 D/MainActivity: user: User{mName='Foo', mId='1', mPetList=[Pet{mName='Foo cat'}, Pet{mName='Foo dog'}]} D/MainActivity: user: User{mName='Bar', mId='2', mPetList=[Pet{mName='Bar cat'}, Pet{mName='Bar dog'}]} 

If it does not answer your question, send the actual date-data to the user and the user.

+5
source

All Articles