RxJava concatMap no response - android

Hope you guys are doing well,
I have been working on a personal Android Project using RxJava and Retrofit. It's a search request for GitHub issues, I am getting input from the user when he clicks Search Button and using a PublishSubject object to emit the search text.
button.setOnClickListener(view -> {
publishSubject.onNext(editText.getText().toString());
});
and I am mapping this emit to an Observable using retrofit like this
publishSubject.concatMap(dataModel::getIssues)
.subscribeOn(Schedulers.computation())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(this::loadData, this::onError);
public Observable<List<Issue>> getIssues(String queryText) {
String[] query_params = queryText.split("/");
return gitHubApiService.getIssues(query_params[0], query_params[1], "open");
}
In result I am expecting List of Issues
public void loadData(List<Issue> issues) {
mProgressDialog.setVisibility(View.INVISIBLE);
if( issues.size() == 0) {
noIssueText.setVisibility(View.VISIBLE);
} else {
mRecyclerView.setVisibility(View.VISIBLE);
mIssuesList.clear();
mIssuesList.addAll(issues);
mAdapter.notifyDataSetChanged();
}
}
But my code seems to have some implementation issue Since it never emits anything from the network, not even on error is called.
I have tested the same example with the Observable I get from Retrofit API, so there is no retrofit error and so I think there is some problem with my concatMap logic.
Any help will be much appreciated

On first parse, I think that you might be making the network call in the main thread. Have you tried the following?
public Observable<List<Issue>> getIssues(String queryText) {
String[] query_params = queryText.split("/");
return gitHubApiService.getIssues(query_params[0], query_params[1], "open")
.subscribeOn(Schedulers.io());
}
Thing is, your onClickListener callback runs on the main thread, and there's no other context switch in the observable pipeline.

Related

RxJava combine database create/update call for a list of objects

im currently struggeling with my first more complex rx chain, maybe you can help me out.
I have a list of objects, which i get from an api call. I need to check if they exists local, if they do, update the dataset, if not create them. My current approach is something like this:
private fun insertUpdateFromServer(objectsToInsert: List<Model>): Completable {
return Observable.fromIterable(objectsToInsert).flatMapCompletable { atl ->
dao.getByServerId(atl.id).subscribeOn(Schedulers.io()).observeOn(AndroidSchedulers.mainThread())
.switchIfEmpty { obs: SingleObserver<in Model> -> createNewObject(atl) }.flatMapCompletable {
updateObject(atl, it).applySchedulers()
}
}
Its important, that i have to wait for all the subtasks to complete. This is working if i only have objcts to update, but if there is a new object, the whole thing will not complete.
Note that im only interested if the operation is completed, the emitted object doesnt matter for me. The function "getByServerId" returns a Maybe.
So im asking you if you can point out my logical mistakes and push me in the right direction, thanks in advance!
The problem is with this line:
switchIfEmpty { obs: SingleObserver<in Model> -> createNewObject(atl) }
Kotlin's automatic SAM conversion is trying to make your life easier by generating an overload of switchIfEmpty that implements void subscribe from SingleSource. In not all cases is this helpful however, what you want to be using instead is this:
switchIfEmpty(createNewObject(atl))
A better explanation of why this is happening can be found here.
So i ended up with a workaround which i found here.
TL;DR: Maybe emits null and calls then "onComplete". So i can chain this if i use "onSuccess" and "onComplete".
The code for someone who is interested:
private fun insertUpdateFromServer(objects: List<Model>): Completable {
return Observable.fromIterable(objects)
.flatMapCompletable { obj ->
dao.getByServerId(obj.id).subscribeOn(Schedulers.io()).observeOn(AndroidSchedulers.mainThread())
.doOnComplete { createNewObject(obj).subscribe() }
.doOnSuccess { updateObject(obj, it).applySchedulers().subscribe() }
.flatMapCompletable { Completable.complete() }
}
}
This expression waits for all it subtasks to terminate and terminates then completely.

Is it possible to load several data asynchronously with Retrofit and wait for all data before doing something else?

Here is my current code. The problem with this code is I need to wait get the data sequentially. The loading time is poor because of this. I want to use something like .enqueue() to get asynchronously several data at once, but I want to wait until I get all the data before continuing the process. Is it possible to do it with Retrofit?
List<Data> datas = new ArrayList<>();
for (long dataId : mDataIds) {
Response<T> response = resource.getData(dataId).execute();
if (response.isSuccessful()) {
datas.add(data.body());
}
}
//do something else
You can solve this problem very elegantly using RxJava.
If you never heard of RxJava before, it is a solution to many of your problems.
If you don't use java8 or retrolambda I recommend you to start using it, as it makes working with RxJava a piece of cake.
Anyway here's what you need to do:
// 1. Stream each value from mDataIds
Observable.from(mDataIds)
// 2. Create a network request for each of the data ids
.flatMap(dataId -> resource.getData(dataId))
// 3. Collect responses to list
.toList()
// Your data is ready
.subscribe(datas -> {}, throwable -> {});
1) First add RxJava2 dependencies to your project
2) Define retrofit api interface methods which return RxJava observable types
public interface DataApi {
#GET("dataById/")
Observable<Data> getData(#Query("id") String id);
}
3) Call api passing input data like below.
Observable.fromIterable(idList).subscribeOn(Schedulers.computation())
.flatMap(id -> {
return retrofitService.getData(id).subscribeOn(Schedulers.io());
}).toList().
.observeOn(AndroidSchedulers.mainThread()).subscribe( listOfData -> {// do further processing }, error -> { //print errors} );
For reference : http://www.zoftino.com/retrofit-rxjava-android-example
Define interface with callback Model type.
public interface LoginService {
#GET("/login")
Call<List<Login>> getLogin();
}
In you calling method override the callback method.
LoginService loginService = ServiceGenerator.createService(LoginService.class);
Call<List<Login>> call = loginService.getLogin();
call.enqueue(new Callback<List<Login>>() {
#Override
public void onResponse(Call<List<Login>> call, Response<List<Login>> response) {
if (response.isSuccessful()) {
// Login successful
} else {
// error response, no access to resource?
}
}
#Override
public void onFailure(Call<List<Login>> call, Throwable t) {
// something went completely south (like no internet connection)
Log.d("Error", t.getMessage());
}
}
I would recommend using RxJava and try it. You have something called FlatMap to combine the results.
To Start here is the tutorial start for RxJava2 and Retrofit2.

How to use RxJava to wait the end of two lists of Retrofit requests

Let me describe my situation:
I want to register new records via an API.
I want to update some records via an API.
I need to be notified when all of these requests have finished, to start another task.
Specifically I have two ArrayList:
ArrayList<Report> createdReports = myHelper.getOfflineCreatedReports();
ArrayList<Report> editedReports = myHelper.getOfflineEditedReports();
Each report can use methods to get Observable instances from my ApiService (Retrofit implementation).
Observable<NewReportResponse> createdReportsObs = Observable.from(createdReports) // .just() != .from()
.flatMap(new Func1<Report, Observable<NewReportResponse>>() {
#Override
public Observable<NewReportResponse> call(Report report) {
return report.postToServer();
}
});
Observable<NewReportResponse> editedReportsObs = Observable.from(editedReports)
.flatMap(new Func1<Report, Observable<NewReportResponse>>() {
#Override
public Observable<NewReportResponse> call(Report report) {
return report.updateInServer();
}
});
I am using the flatMap operator to get one Observable for each report.
But I am not sure how to wait until all of the requests have finished.
I was thinking in using the zip operator.
Observable.zip(createdReportsObs, editedReportsObs, new Func2<NewReportResponse, NewReportResponse, Boolean>() {
#Override
public Boolean call(NewReportResponse justOneResponse, NewReportResponse justOneResponse2) {
return false;
}
});
Unfortunately I saw some examples where zip is used to create pairs of Observables.
Please suggest me what operator I can use to achieve it. Or how to do it using rxJava with a different approach.
Thank you in advance.
Are you using RxJava 2? If so you can use the new completable api. This is assuming you don't need to know any of the server results, just need to wait for them to complete.
Completeable.merge(createdReportsObs.toCompleteable(),
editedReportsObs.toCompleteable())
.subscribe()
This is my way. May not best practice.
Observable.merge(createdReportsObs, editedReportsObs)
.toList()
.flatMap(Observable::from)
.xxx //Now they are completed, do what you want
.subscribe();

Get one result from 2 observables with RxJava

I am new at RxJava and I have some pain to execute my first 'difficult' query.
I have two Observables generated from Retrofit, one that 'ping' a new api, the other the old one. The first one will query 'http://myurl.com/newapi/ping', the second one 'http://myurl.com/oldapi/ping'. Result from this request doesn't matter, I just want to know if the server is using the new or old api.
So I would like to call both observables at the same time, and finally have a boolean at the end to know if I'm using old or new api.
I tried something like that
Observable.mergeDelayError(obsOldApi,obsNewApi)
.observeOn(AndroidSchedulers.mainThread(), true)
.subscribeOn(Schedulers.io())
.subscribe(new Subscriber<String>() {
#Override
public void onCompleted() {
}
#Override
public void onError(Throwable e) {
}
#Override
public void onNext(String s) {
}
});
But onError will be called once (I would like it to be called only if both request failed) and when onNext is called, I don't know from which request it came (old or new api ?).
Thank you for you help
For simplicity, let say that you'll received "NEW" or "OLD" regarding which api is available.
The difficulty of your operation is to manage errors : RxJava deals errors as terminal state. So you'll have to ignore this error, using .onErrorResumeNext() for example.
Observable<String> theOld = oldApi.map(r -> "OLD")
// ignore errors
.onErrorResumeNext(Obervable.empty());
Observable<String> theNew = newApi.map(r -> "NEW")
.onErrorResumeNext(Obervable.empty());
Observable.merge(theOld, theNew)
.first() // if both api are in errors
.subscribe(api -> System.out.println("Available API : "+api));
I added the operator first : it will take only the first result ("OLD" or "NEW") but trigger an error if the previous Observable is empty, which is the case if both API are unavaible.

Rxjava, retrofit and multiple calls

I've recently started using Rxjava and retrofit, and looking for any ideas on how to perform n number of retrofit post calls and track them via rxjava. Once all actions have been completed a UI event will then occur.
I found this article: http://randomdotnext.com/retrofit-rxjava/ however it uses a for loop for initiating multiple request observables. Maybe there is a more elegant way besides a for loop? What is the best rxjava operator for this kind of effort?
Instead of using for loop, you can create an Observable sequence from the List/Array then use flatMap/concatMap operator.
Using for loop:
GithubService service = ServiceFactory.createRetrofitService(GithubService.class, GithubService.SERVICE_ENDPOINT);
for(String login : Data.githubList) {
service.getUser(login)
.subscribeOn(Schedulers.newThread())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Subscriber<Github>() {
#Override
public final void onCompleted() {
// do nothing
}
#Override
public final void onError(Throwable e) {
Log.e("GithubDemo", e.getMessage());
}
#Override
public final void onNext(Github response) {
mCardAdapter.addData(response);
}
});
}
Pure Rx:
GithubService service = ServiceFactory.createRetrofitService(GithubService.class, GithubService.SERVICE_ENDPOINT);
Observable.from(Data.githubList)
.flatMap(login -> service.getUser(login))
.subscribeOn(Schedulers.newThread())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(...);
RxJava provides a lot operators to combine multiple observables.
In your situation, you can use operator merge, and do UI work at onComplete()
When multiple call depend on the same thing you can use flat map or concat map to utilize your call. Then finally update your view.
Use the zip operator.
For Example :
you have 3 Retrofit Api and they are all return a string , and what you need is a long string merge by the 3 string.
So you need wait for the 3 api call are all return . and merge the return string with zip operator.
Code will be like:
Observable.zip(
api1,
api2,
api3,
(resp1, resp2, resp3) -> resp1 + resp2 + resp3
)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(resp -> {
// do something
});

Categories

Resources