How to chain observables in rxjava2 - android

I have two observables in my code. The first one is a merged observable for search button click and text change.
Observable<String> buttonClickStream = createButtonClickObservable();
Observable<String> textChangeStream = createTextChangeObservable();
Observable<String> searchTextObservable
=Observable.merge(buttonClickStream,textChangeStream);
disposable = searchTextObservable
.observeOn(AndroidSchedulers.mainThread())
.doOnNext(s -> showProgressBar())
.observeOn(Schedulers.io())
.map(this::getStarredRepos)
.observeOn(AndroidSchedulers.mainThread())
.subscribe(gitHubRepos -> {
hideProgressBar();
showResults(gitHubRepos);
});
The second observable is for getting response from server.:
private List<GitHubRepo> getStarredRepos(String username) {
RestInterface restService=RestService
.getClient().create(RestInterface.class);
restService.getStarredRepos(username)
.observeOn(AndroidSchedulers.mainThread())
.subscribeOn(Schedulers.io())
.subscribe(this::handleResponse, this::handleError);
return repoList;
}
Now the problem is, hideProgressBar() and showResults() methods are executing before handleResponse() finishes.
I am new to RxJava, so if there is anything wrong in code please rectify.

Your List<GitHubRepo> getStarredRepos(...) should instead be Observable<List<GitHubRepo>> getStarredRepos(...). Don't subscribe to the observable inside of this method, but return the observable you get from restService (if you need to process the response, put a map() before returning, for errors you can use onErrorReturn() or something you need).
Then instead of .map(this::getStarredRepos) do .switchMap(this::getStarredRepos).

Related

Android. RxJava 2: Parallel multiple network calls

I need make two parallel requests with RxJava. For this I use zip operator. Here is my code:
public Disposable getBooksAndAuthors(String id, ReuqestCallback requestCallback) {
return singleRequest(Single.zip(
getBooks(id).subscribeOn(Schedulers.newThread()).observeOn(AndroidSchedulers.mainThread()),
getAuthors(id).subscribeOn(Schedulers.newThread()).observeOn(AndroidSchedulers.mainThread()),
(book, author) -> new ZipResponseWrapper(book, author).getResponse()), requestCallback);
}
private <T extends NetworkResponse> Disposable singleRequest(Single<T> single, RequestCallback requestCallback) {
return single.doOnSubscribe(d -> requestCallback.onStartRequest())
.doOnSuccess(s -> requestCallback.onSuccess(s))
.doOnError(ErrorConsumer.consume((t) -> requestCallback.onError(t)))
.doFinally(() -> requestCallback.onFinish())
.subscribe();
}
But I don’t understand how to receive response separately for each request. That is, I need to, if the answer came to the first request, immediately display the data received from this request and not wait for a response to the second request. And after the answer to the second request arrives, display the data received on the second request.This is necessary due to the fact that the second request fulfills a long time. Please help me.
Here is an example of how you can handle it with the responses for each function:
val disposable = Observable.zip(
firstNetworkCall().subscribeOn(Schedulers.io()),
secondNetworkCall().subscribeOn(Schedulers.io()),
BiFunction{
firstResonse: ResponseOneType,
secondResponse: ResponseTwoType ->
combineResult(firstResponse, secondResponse) }))
.observeOn(AndroidSchedulers.mainThread())
.subscribe { it -> doSomethingWithIndividualResponse(it) }
My suggestion (in Kotlin though):
val id = 0L
Observables.combineLatest(
getBooks(id).startWith(emptyList<Book>()).subscribeOn(Schedulers.io()).observeOn(Schedulers.computation()),
getAuthor(id).startWith(emptyList<Author>()).subscribeOn(Schedulers.io()).observeOn(Schedulers.computation())
) { book: List<Book>, author: List<Author> ->
Pair(book, author)
}.skip(1)
.observeOn(AndroidSchedulers.mainThread())
.subscribe { (books: List<Book>, authors: List<Author>) ->
view.show(books)
view.show(authors)
}

How to get a callback when every observer of a hot like ConnectableObservable complete?

I am searching for a doOn... callback for a ConnectableObservable that is invoked when every observer terminates
val gatewayItems = viewModel.getGatewayItems(gateways!!)
.observeOn(Schedulers.io())
.take(1)
.publish()
.autoConnect(2)
gatewayItems.subscribe { sharedGateways -> sharedGatewaysAdapter.submitList(sharedGateways) }
gatewayItems.subscribe { sharedGateways -> privateGatewaysAdapter.submitList(privateGateways) }
I would like to get a callback to my multicasted hot observable when both of my observers signal a terminal event
I have tried to put doOnTerminate and doOnComplete operators on my parent multicasted observable but, it seems that these callbacks are invoked 2 times (one for each observer)
val gatewayItems = viewModel.getGatewayItems(gateways!!)
.observeOn(Schedulers.io())
.take(1)
.doOnComplete { ... }
.doOnTerminate { ... }
.publish()
.autoConnect(2)
Both .doOnComplete and .doOnTerminate work for me.
Edit: the chances are you might be attaching the do... operators in incorrect order. For example, neither of these doOnComplete will work:
val gatewayItems = viewModel.getGatewayItems(gateways!!)
.observeOn(Schedulers.io())
.doOnComplete { ... }
.take(1)
.publish()
.autoConnect(2)
.doOnComplete { ... }

How to wait until one list of calls are done to call the following ones

I'm using rxJava and I want to do a forEach of a list, and for every item, make a call, and then once those calls are finished, call another one.
This is my code
val flowableList = answerListCreated.map {
questionService.addAnswerToQuestion(
questionId,
it.id,
MyUtils.getAccessTokenFromLocalStorage(context = mContext!!)
)
}
disposable = Flowable.concat(flowableList)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe {
addCorrectsAnswersToQuestion(questionId)
}
But it's joining in the subscribe twice, and it should join in the subscribe once.
What I'm missing? I thought concat should be a good option because I've read that it does first the first job, and then when job1 is finished it starts the job2.
Well, also if necessary I can return Observable<T>, from now in my service I'm returning Flowable<T> to test this.
i think you need to do something like:
val disposable = Flowable.fromArray(answerListCreated)
.flatMap {
questionService.addAnswerToQuestion(
questionId,
it.id,
MyUtils.getAccessTokenFromLocalStorage(context = mContext!!)
)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
}
.toList()
.subscribe {
addCorrectsAnswersToQuestion(questionId)
}

RxJava2 - execute call synchronously

I've a TestService, where I do an async task to get my data. I would like to wait for the response before I continue.
public List<Data> getData() {
List<Data> data = new ArrayList<>();
Disposable disposable = repository.getDataFromApi(false)
.observeOn(AndroidSchedulers.mainThread())
.subscribe( newData -> {
data.addAll(newData);
}, __ -> { });
mCompositeDisposable.add(disposable);
//Here I want to stop till "Data" arraylist is filled with data
... do something with data
}
In Volley I could just call req.executeSynchronously(); to make it happen. As getData() have to return data already, I've to somehow make it wait till I get response. How to do it? I'm using Single.
My approach using getBlocking();
public List<Data> getData() {
List<Data> data = new ArrayList<>();
Disposable disposable = repository.getDataFromApi(false)
.observeOn(AndroidSchedulers.mainThread())
.blockingGet();
.subscribe( newData -> {
data.addAll(newData);
}, __ -> { });
mCompositeDisposable.add(disposable);
//Here I want to stop till "Data" arraylist is filled with data
... do something with data
}
It says cannot resolve method subscribe, so I'm probably calling it wrong..
fun getDataFromApi(): Single<List<Data>> {
return service.getData()
.map { jsonApiObject ->
...
return#map data
}
}
Hopefully you are aware that blocking is a strong antipattern in RxJava and you should avoid blocking whenever you can.
Saying that, if you really need to block, you have two options:
use blockingGet() which - as the name indicates - blocks current thread and directly returns value of publisher (Single in your case). This is probably what you were looking for. In your case:
newData = repository.getDataFromApi(false).blockingGet();
data.addAll(newData);
synchronize with Java classes, like CountDownLatch - more complicated and I would use blockingGet() because it's more straightforward. But it's a possibility.

Managing thread schedulers when concat observables

I have a code like this:
service.getUserById(10)
.subscribeOn(Schedulers.newThread())
.observeOn(AndroidSchedulers.mainThread())
.concatMap(getFullUserFromDto())
.subscribe(doSomehingWithUser());
private Func1<UserDto, Observable<User>> getFullUserFromDto() {
return new Func1<UserDto, Observable<User>>() {
#Override
public Observable<User> call(final UserDto dto) {
return dao.getUserById(dto.getUserId());
}
};
}
and in my DAO, I have:
public Observable<User> getUserById(final Long id) {
return api.getUserById(id).map(//more things...
}
Note there are two levels of "concatenation": service -> dao -> api. Method api.getUserById(id) make a network call.
I'm getting NetworkOnMainThreadException error. Why? I'm using and subscribeOn and observeOn operators, but it seems that it is not applied to the "final" built Observable.
If I use this operators in the API call, in the DAO, it works:
return api.getUserById(id)
.subscribeOn(Schedulers.newThread())
.observeOn(AndroidSchedulers.mainThread())
.map(//more things...
Is there a way to use just once in the "root" Observable?
So, concatMap subscribes on Observables. What thread is used to perform this operation? Well, the thread that called onNext for the concatMat, given that it doesn't change threads/schedulers. So, one simple transposition should help with this:
service.getUserById(10)
.subscribeOn(Schedulers.newThread())
.concatMap(getFullUserFromDto())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(doSomehingWithUser());
I'd also suggest to use Schedulers.io(), as it will re-use threads.
Short answer: use observeOn before chained operations to controll on which schedulers they are executed:
service.getUserById(10)
.subscribeOn(Schedulers.newThread())
.observeOn(Schedulers.io())
.concatMap(getFullUserFromDto())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(doSomehingWithUser());
In the example above, .concatMap will be executed in Schedulers.io()
More details can be found here:
http://tomstechnicalblog.blogspot.com/2016/02/rxjava-understanding-observeon-and.html

Categories

Resources