1

We already know that Observable can only have one argument as its generic type. Observable I assume that I have 2 network calls which return 2 data type: UserResponse and WorkResponse. And I want to call 2 these APIs step by step, getUser then getWork. Finally I subscribe to them and got only one data type, obviously that's WorkResponse because getWork is the last API call in the upper stream Observable<WorkResponse>. But in the subscribe code block, I want to get both UserResponse and WorkResponse. So how can I achieve that?

1 - Some people say that I should create a container class to contain both UserResponse and WorkResponse then I can get these data types from that container in subcribe code block.

2 - Create a temporary variable to hold userResponse then access to it from subscibe code block, like the following:

class MainActivity : AppCompatActivity() {

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    setContentView(R.layout.activity_main)

    var tempUserResponse: UserResponse? = null
    Observable.just("Prepare call API")
            .flatMap {
                apiGetUser()
            }.flatMap { userResponse ->
                tempUserResponse = userResponse // Save temporarily userResponse to pass it to subscribe code block
                apiGetWork()
            }.subscribeOn(Schedulers.io())
            .observeOn(AndroidSchedulers.mainThread())
            .subscribe { workResponse ->
                Log.e("TAG", "userResponse = " + tempUserResponse)
                Log.e("TAG", "workResponse = " + workResponse)
            }

}

private fun apiGetUser(): Observable<UserResponse> {
    // Fake API to get User
    return Observable.just(UserResponse())
}

private fun apiGetWork(): Observable<Work> {
    // Fake API to get Work
    return Observable.just(Work())
}

class Work
class UserResponse

}

3 - Is there another way? Please give me answer, thanks so much!

EDIT: Thanks for all your answers, guys! All your answers, may be different at the ways to implement (using nested flatMap or using zip) but we all need to use a 3rd class as a container class for all objects we need. Built-in container classes, we have: Pair<A, B> and Triple<A, B, C> . If we need more arguments, we must create our own ones

LuongPham
  • 81
  • 10
  • use Observable.zip http://reactivex.io/documentation/operators/zip.html – Rahul Sep 26 '18 at 08:01
  • Yeah, some people told me that, but I haven't had any examples about using zip. Luckily, guys below gave me that. Thanks – LuongPham Sep 27 '18 at 06:41

2 Answers2

1

You can use zip to get one object from your 2 results:

public class MergedObject{
    private Work workResponse;
    private UserResponse userResponse;

    MergedObject(final Work workResponse, final UserResponse userResponse){
        this.workResponse= workResponse;
        this.userResponse= userResponse;
    }

    // getter / setter
}

and then

Observable.just("Prepare call API")
    .flatMap {
        apiGetUser()
   }.zipWith(apiGetWork(), ( userResponse, workResponse ) ->
       return new MergedObject(workResponse, userResponse)
   ).subscribeOn(Schedulers.io())
    .observeOn(AndroidSchedulers.mainThread())
    .subscribe { mergedObject->
        Log.e("TAG", "userResponse = " + mergedObject.userResponse)
        Log.e("TAG", "workResponse = " + mergedObject.workResponse)
    }

(Written without any testing you may need to adapt a bit)

Samuel Eminet
  • 4,647
  • 2
  • 18
  • 32
  • Basically, I expected the 3rd way is what I can get both UserResponse and WorkResponse without creating a Container object because it makes me create a little boilerplate code for a Container class (The same with 1st way) Maybe I can't do anything except using Zip with Container object. Thanks so much. – LuongPham Sep 27 '18 at 03:32
  • You can use a [Pair](https://developer.android.com/reference/android/util/Pair) object instead of a custom MergedObject class if you want – Samuel Eminet Sep 27 '18 at 10:29
  • Thanks! I mean here we have a limitation of RxJava that the onNext can just emit one data type, so we don't have anyway to get multiple data types except using an object to composite multiple data types inside of it. – LuongPham Sep 28 '18 at 03:35
1

You could overload flatmap and do the following:

fun test() {
    Observable.just("Prepare call API")
            .flatMap {
                apiGetUser()
            }.flatMap(
                Function<UserResponse, Observable<Work>> {
                    return@Function apiGetWork()
                },
                BiFunction<UserResponse, Work, Pair<UserResponse, Work>> { userResponse, work ->
                    Pair(userResponse, work)
                })
            .subscribeOn(Schedulers.io())
            .observeOn(AndroidSchedulers.mainThread())
            .subscribe { response ->
                Log.e("TAG", "userResponse = " + response.first.name)
                Log.e("TAG", "workResponse = " + response.second.name)
            }

}

private fun apiGetUser(): Observable<UserResponse> {
    // Fake API to get User
    return Observable.just(UserResponse())
}

private fun apiGetWork(): Observable<Work> {
    // Fake API to get Work
    return Observable.just(Work())
}

class Work {
    val name = "world"
}
class UserResponse {
    val name = "hello"
}

Basically returning a Pair<UserResponse, Work> :)

Rene Ferrari
  • 4,096
  • 3
  • 22
  • 28