JavaRx doOnNext not called after onErrorResumeNext - android

I've tried to loop throw items and do additional request for each item. the issue is there it a possibility that request returns an error but i need to continue requests and collect all data to list. but it looks like after onErrorResumeNext called no other data adds to list.
Observable<List<TestModel2>> observable = apiTest
.performTest()
.flatMapIterable(items -> items)
.flatMap(testModel -> {
Log.d("TestItemData", "testModel.userId = " + testModel.userId);
if (testModel.userId < 5) {
return apiTest.performTest2(testModel.userId);
} else {
return apiTest.performTest3(testModel.userId);
}
})
.doOnNext(testModel2 -> {Log.d("TestItemData", "doOnNext --- " + testModel2.title);})
.onErrorResumeNext(throwable ->{
Log.d("TestItemData", "onErrorResumeNext -------- ");
return Observable.empty();
})
.toList()
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread());
in this example afret performTest() called I get 10 items that i need to loop throw and call performTest2() that returns data
and for some id's i called performTest3() that returns 404 exception.
a for result in logs i see all 10 testModel.userIds (from 1 to 10) but doOnNext calls until onErrorResumeNext calls only, so in the result list i can got 1 item or 2 or 3 items only

Apply the doOnNext and onErrorResumeNext inside the flatMap:
Observable<List<TestModel2>> observable = apiTest
.performTest()
.flatMapIterable(items -> items)
.flatMap(testModel -> {
Log.d("TestItemData", "testModel.userId = " + testModel.userId);
Observable<TestModel2> obs;
if (testModel.userId < 5) {
obs = apiTest.performTest2(testModel.userId);
} else {
obs apiTest.performTest3(testModel.userId);
}
return obs
.doOnNext(testModel2 -> {
Log.d("TestItemData", "doOnNext --- " + testModel2.title);
})
.onErrorResumeNext(throwable ->{
Log.d("TestItemData", "onErrorResumeNext -------- ");
return Observable.empty();
});
})
.toList()
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread());

Related

My doOnComplete is called before .Map finsihes

I have a problem with kotlin observables, I have searched through internet and stackoverflow but I think I am missing something in concepts. I have dashboardRepository which has method called getCallsCountForWeek, this basically returns flowable list for last 7 days and now I need to iterate through all flowables and then update my graph with count of calls user made for that day. Here is my code
fun getCallsCountForWeek(calendar: Calendar) : List<Flowable<Float>> {
val result = ArrayList<Flowable<Float>>()
for(index in 0..6) {
calendar.add(Calendar.DAY_OF_MONTH, -index)
result.add(dashbordDao.getCallsCountForDay(customSharedPreferences.getUser()?.id!!, CustomDateTimeUtil.getStartOfDay(calendar), CustomDateTimeUtil.getEndOfDay(calendar)).subscribeOn(Schedulers.io()).observeOn(AndroidSchedulers.mainThread()))
}
return result
}
Observable.fromArray(dashboardRepository
.getCallsCountForWeek(calendar). map {
items -> kotlin.run {
items.forEach {
it.subscribe({
Log.e("Result", " Count: " + it)
},{
Log.e("Error", "" + it)
})
}
}
}.doOnComplete {
//We will do this when it is completed
Log.e("Result", "Completed")
}.doFinally {
Log.e("Result", "Finally")
}.subscribeOn(Schedulers.io()).observeOn(AndroidSchedulers.mainThread()).subscribe()
The problem is that doFinally and doOnComplete are called before map completes its iteration through all the flowables. I tried to use .zip for flowables but apparently could not make it work too.
According to other posts on stack overflow, doOnComplete is called when subscription is successful but I want that to happen after everything is done inside .map.
You should use flatMap or flatMapIterable instead of map and have only one subscribe call
Observable.fromArray(dashboardRepository
.getCallsCountForWeek(calendar)
.flatMapIterable { it } // iterate over list
.flatMap { it } // use flowables from list
.doOnNext { /* do something with every item */ }
.doOnComplete {
//We will do this when it is completed
Log.e("Result", "Completed")
}
.doFinally {
Log.e("Result", "Finally")
}
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.ignoreElements () // if you already handled everything in the doOnNext
.subscribe()
After looking into answer from Eugene Popovich. I was pointed into right direction and then I did the following and it worked.
So, first thing, I modified my function to return list of Single Observable instead of Flowable as that any who made ore sense. Once done I did following as suggested by Eugene but just using flatMapSingle instead of flatMap.
Observable.fromArray(dashboardRepository.getCallsCountForWeek(calendar))
.flatMapIterable { it } // iterate over list
.flatMapSingle {
it
}
.doOnNext {
barEtries.add( BarEntry(index++, it))
}
.doOnComplete {
//We will do this when it is completed
Log.e("Result", "Completed "+barEtries)
setBarChartData()
}
.doFinally {
Log.e("Result", "Finally")
}
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.ignoreElements () // if you already handled everything in the doOnNext
.subscribe()
The change in getCallsCountForWeek was as below, basically just used single instead of Flowable because it made more sense and flatMapSingle provided out of the box resolution without calling even subscribe.
fun getCallsCountForWeek(calendar: Calendar) : ArrayList<Single<Float>> {
val result = ArrayList<Single<Float>>()
for(index in 0..6) {
calendar.add(Calendar.DAY_OF_MONTH, -index)
result.add(dashbordDao.getCallsCountForDay(customSharedPreferences.getUser()?.id!!, CustomDateTimeUtil.getStartOfDay(calendar), CustomDateTimeUtil.getEndOfDay(calendar)).subscribeOn(Schedulers.io()).observeOn(AndroidSchedulers.mainThread()))
}
return result
}

Avoid Inserting item in a list on Error code

So I have a function which is placing a call using RxJava and then this function is supposed to insert the element returned into a list.
So the code works fine. I am able to retrieve and all items in the cart list. However if an error code generated by the getItem appears (getItem place an API call) such 401 or 404..., I want to continue in the iteration and just bypass the insert. I can't use onErrorReturnItem with a null and filter on null after.
return Observable.just(itemResponses)
.flatMapIterable { it }
.flatMapSingle { itemResponse ->
itemWarehouse.getItem(itemResponse.id)
.map { item ->
itemData(
itemResponse.id,
item,
itemResponse.info,
false,
itemResponse.result)
}
}
.map { itemData -> cartMap[itemData.id]?.insert(itemData) }
.toList()
.map { cartMap.values.toList() }
}
Any idea ?
This should work
return Observable.just(itemResponses)
.flatMapIterable { it }
.flatMap { itemResponse ->
itemWarehouse.getItem(itemResponse.id)
.toObservable()
.map { item ->
itemData(
itemResponse.id,
item,
itemResponse.info,
false,
itemResponse.result)
}
.onErrorResumeNext(Observable.empty())
}
.map { itemData -> cartMap[itemData.id]?.insert(itemData) }
.toList()
.map { cartMap.values.toList() }

Filter Observable<List> with text from an EditText using RxBinding

I have an array of Objects and I want to filter that array based on the text user types on an EditText android view.
What I thought it that I should try and convert the array of POJOs to an Observable of Strings and this is what I did :
Observable<String> professionsObservable = Observable.fromArray(((GetStartedActivity) getActivity()).professions)
.map(profession -> {
if (profession.getName().length() > 0) {
professionsNameList.add(capitalizeFirstLetter(profession.getName()));
}
return professionsNameList;
})
.flatMapIterable(items -> items);
Now I want to combine the text from the EditText with the `professionsObservable I posted above.
This is the code I'm using :
RxTextView.textChangeEvents(etProfession)
.doOnEach(notif -> {
if (etProfession.getText().toString().trim().length() > 0) {
etCompany.setVisibility(GONE);
etIndustry.setVisibility(GONE);
} else {
etCompany.setVisibility(VISIBLE);
etIndustry.setVisibility(VISIBLE);
}
})
.debounce(EDITTEXT_DELAY, TimeUnit.MILLISECONDS)
.skip(1)
.map(textChangeEvent -> textChangeEvent.text().toString())
.switchMap(search -> {
return professionsObservable
.filter(profession -> {
return profession.toLowerCase().startsWith(search);
});
}
)
.toList()
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(
filteredProfessions -> {
Timber.i("NOT ENTERING");
rvProfession.setVisibility(VISIBLE);
professionAdapter.addItems(filteredProfessions);
},
throwable -> Log.i("THROW", "PROFESSIONS ", throwable));
I'm using map operator to turn the text change event to a String and then for each String I get from the stream I'm using switchMap (cause I don't care for results from previous searches). Then I compose all Strings to a List with toList. The problem is that it never reaches the subscribe call while I have a lot of strings in the initial Array I used and I do type text that fits the condition of the filter operator.
Is it something that I might missing here ?
EDIT : I updated my code to :
RxTextView.textChangeEvents(etProfession)
.doOnEach(notif -> {
if (etProfession.getText().toString().trim().length() > 0) {
etCompany.setVisibility(GONE);
etIndustry.setVisibility(GONE);
} else {
etCompany.setVisibility(VISIBLE);
etIndustry.setVisibility(VISIBLE);
}
})
.subscribeOn(AndroidSchedulers.mainThread())
.debounce(EDITTEXT_DELAY, TimeUnit.MILLISECONDS)
.skip(1)
.map(textChangeEvent -> textChangeEvent.text().toString())
.flatMap(search -> {
return Observable.fromArray(((GetStartedActivity) getActivity()).professions)
.map(profession -> {
List<String> professionsList = new ArrayList<>();
if (profession.getName().length() > 0) {
professionsList.add(capitalizeFirstLetter(profession.getName()));
}
return professionsList;
})
.flatMapIterable(items -> items)
.filter(profession -> {
if (profession.toLowerCase().startsWith(search.toLowerCase())) {
}
return profession.toLowerCase().startsWith(search.toLowerCase());
});
})
.toList()
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(
filteredProfessions -> {
rvProfession.setVisibility(VISIBLE);
// professionAdapter.addItems(filteredProfessions);
},
throwable -> Log.i("THROW", "PROFESSIONS ", throwable));
If I remove toList() operator my code works (enters the subscribe call) but if I leave it there it wont. Anyone knows why ?
In my experience, RxBinding requires .subscribeOn(AndroidSchedulers.mainThread()) right after .textChangeEvents() or any other event. So probably you are causing it to fail by adding .subscribeOn(Schedulers.io)
See method .checkMainThread() in https://github.com/JakeWharton/RxBinding/blob/master/rxbinding/src/main/java/com/jakewharton/rxbinding2/internal/Preconditions.java
UPDATE
Also, probably because .onComplete() never comes from upstream, .toList() is never executed.

Why debounce() with toList() doen't working in RxAndroid?

While I'm using debounce() ,then fetch data from backend and the data
I want to convert to another data and lastly use toList().
when I'm using toList() nothing happens no any log not in subscribe and error ,without toList() it works and subscribe() method enters as much as I have list of books, I tested the second part of code it without debounce() just getItems() and using toList() it works.
Below is my code the first part with debounce() and itList() which is not working and the second with toList() which works
public Flowable<List<Book>> getItems(String query) {}
textChangeSubscriber
.debounce(300, TimeUnit.MILLISECONDS)
.observeOn(Schedulers.computation())
.switchMap(s -> getItems(s).toObservable())
.flatMapIterable(items -> items)
.map(Book::convert)
.toList()
.observeOn(AndroidSchedulers.mainThread())
.subscribe(books -> {
Log.i("test", "" + books.toString());
}, error -> {
Log.i("test", "" + error);
});
getItems(query).flatMapIterable(items -> items)
.map(Book::convert)
.toList()
.observeOn(AndroidSchedulers.mainThread())
.subscribeOn(Schedulers.io())
.subscribe(books -> {
Log.i("test", "" + "" + books.toString());
}, error -> {
Log.i("test", "" + error);
});
toList requires the sequence to terminate which doesn't happen on the outer stream that responds to text events. You should move the processing of the books into the switchMap:
textChangeSubscriber
.map(CharSequence::toString) // <-- text components emit mutable CharSequence
.debounce(300, TimeUnit.MILLISECONDS)
.observeOn(Schedulers.computation())
.switchMap(s ->
getItems(s)
.flatMapIterable(items -> items)
.map(Book::convert)
.toList()
.toFlowable() // or toObservable(), depending on textChangeSubscriber
)
.observeOn(AndroidSchedulers.mainThread())
.subscribe(books -> {
Log.i("test", "" + books.toString());
}, error -> {
Log.i("test", "" + error);
});

Update the UI ( Loading and Error Views ) on RecyclerView using RxJava

I have a SearchView that executes a network request to search for some tracks and then populates a RecylerView with the results. I have found this code which works fine.
I have already integrated the RecyclerView EmptyView through the Adapter but now I am trying to integrate the LoadingView(Progress) and ErrorView inside this code. I tried to put the LoadingView(ProgressBar) on Visibility True inside the concatMap but got the error that the “Only the original thread that created a view hierarchy can touch its views.” which can be solved running that on the MainThread but I am sure there is a better way to do this.
Can someone have a better idea where and how the logic about show/hide the ErrorView and LoadingView can be integrated into this code?
I am using also RxBinding. Maybe also using RxRecyclerView would be a good idea?
RxSearchView.queryTextChanges(searchView).
filter(charSequence ->
!TextUtils.isEmpty(charSequence))
.throttleLast(100, TimeUnit.DAYS.MILLISECONDS)
.debounce(200, TimeUnit.MILLISECONDS)
.onBackpressureLatest()
.concatMap(searchTerm ->
{
return searchTracks(searchTerm).
.subscribeOn(Schedulers.io())
.onErrorResumeNext(throwable1 -> {
//handle error somehow, change UI
return Observable.empty();
}
);
}
)
.observeOn(AndroidSchedulers.mainThread())
.subscribe(tracks -> {
populateTracks(tracks);
}
});
}, throwable -> {
//show errorView
});
This here was my complete solution without stripping code for the presentation.
RxSearchView.queryTextChanges(searchView)
.skip(1)
.doOnNext(charSequence -> Log.v(TAG, "searching: " + charSequence))
.throttleLast(100, TimeUnit.MILLISECONDS)
.debounce(200, TimeUnit.MILLISECONDS)
.onBackpressureLatest()
.observeOn(AndroidSchedulers.mainThread())
.filter(charSequence -> {
final boolean empty = TextUtils.isEmpty(charSequence);
if (empty) {
Log.v(TAG, "empty view");
mAdapter.clear();
}
return !empty;
})
.concatMap(charSequence -> {
Log.v(TAG, "requesting " + charSequence);
return onErrorResumeNext(
mGitApiService.searchRepositoriesSingle(charSequence.toString())
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread()),
throwable -> {
try {
throw throwable;
} catch (HttpException httpException) {
showEmptyErrorView(httpException.message());
} catch (Throwable other) {
showEmptyErrorView(other.getMessage());
other.printStackTrace();
}
return Observable.empty();
});
})
.doOnNext(charSequence -> Log.v(TAG, "got data"))
.subscribe(response -> {
showRepositories(response.getItems());
}, throwable -> {
throwable.printStackTrace();
showEmptyErrorView(throwable.getMessage());
});
so basically whenever you touch your view you have to call .observeOn(AndroidSchedulers.mainThread())

Categories

Resources