Completable creation NPEs only on Android 4 - android

I am getting a lot of NPE crashes on Crashlytics that are related with an rx-wrapper I've created for FirebaseRemoteConfig.
My code:
public Completable fetch() {
remoteConfig.activateFetched();
return Completable.create(e ->
remoteConfig.fetch(cacheExpiration).addOnCompleteListener(
task -> {
if (task.isSuccessful()) {
Log.d(TAG, "Config fetched successfully");
remoteConfig.activateFetched();
if (!e.isDisposed()) {
e.onComplete();
}
} else {
if (!e.isDisposed()) {
Log.e(TAG, "Config fetch error", task.getException());
e.onError(task.getException());
}
}
}));
}
And then I subscribe to it like this:
composite.clear();
composite.add(
helper.fetch()
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribeWith(new DisposableCompletableObserver() {
#Override
public void onComplete() {
Timber.tag(TAG).d("Remote config fetched");
}
#Override
public void onError(#NonNull Throwable throwable) {
Timber.tag(TAG).d(throwable, "Remote config fetch error");
}
})
);
I get 3 types of crashes:
Crash #1
Fatal Exception: java.lang.NullPointerException
at io.reactivex.internal.operators.completable.CompletableCreate.subscribeActual(CompletableCreate.java:36)
at io.reactivex.Completable.subscribe(Completable.java:1635)
at io.reactivex.internal.operators.completable.CompletableSubscribeOn$SubscribeOnObserver.run(CompletableSubscribeOn.java:64)
at io.reactivex.Scheduler$DisposeTask.run(Scheduler.java:452)
at io.reactivex.internal.schedulers.ScheduledRunnable.run(ScheduledRunnable.java:61)
at io.reactivex.internal.schedulers.ScheduledRunnable.call(ScheduledRunnable.java:52)
at java.util.concurrent.FutureTask.run(FutureTask.java:237)
at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.access$201(ScheduledThreadPoolExecutor.java:152)
at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:265)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1112)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:587)
at java.lang.Thread.run(Thread.java:841)
Crash #2
Fatal Exception: java.lang.NullPointerException
at io.reactivex.internal.operators.completable.CompletableSubscribeOn.subscribeActual(CompletableSubscribeOn.java:36)
at io.reactivex.Completable.subscribe(Completable.java:1635)
at io.reactivex.internal.operators.completable.CompletableObserveOn.subscribeActual(CompletableObserveOn.java:34)
at io.reactivex.Completable.subscribe(Completable.java:
Crash #3
Fatal Exception: java.lang.NullPointerException
at io.reactivex.internal.operators.completable.CompletableObserveOn$ObserveOnCompletableObserver.onSubscribe(CompletableObserveOn.java:68)
at io.reactivex.internal.operators.completable.CompletableSubscribeOn.subscribeActual(CompletableSubscribeOn.java:36)
at io.reactivex.Completable.subscribe(Completable.java:1635)
at io.reactivex.internal.operators.completable.CompletableObserveOn.subscribeActual(CompletableObserveOn.java:34)
at io.reactivex.Completable.subscribe(Completable.java:1635)
The curious thing is that the crashes only happen on Android 4, mainly 4.4 and 4.1.

The thing all these errors have in common is that they appear to be caused by a null where a CompletableObserver is expected.
Given that the subscribeWith gets a valid object the only way for this to occur is through a misconfiguration of the RxJavaPlugin.onSubscribe hook, which can wrap observers and thus for some reason replace the observer with a null.
I suggest inspecting the onCompletableSubscribe field in the RxJavaPlugins class using reflection to see if there is some callback that might be acting weirdly on those java versions.

Related

How to handle Exception in map() RxJava

I'm calling an API which throws 404 status code and due to that app gets crashed. The code snippet is mentioned below:
fun loadAndSavedata(
type: Type,
bookmark: String? = null,
): Single<MetaData> {
return loadFilesByType(type, bookmark)
.map { response ->
if (!response.isSuccessfulAndHasBody()) {
//This line gets pointed as cause
throw OfflineModeException(response)
}
response.body()!!
}
.flatMap { loadNextPages(it, type) }
}
The error, I receive in logcat is:
Process: com.example.app, PID: 23797
io.reactivex.rxjava3.exceptions.UndeliverableException: The exception could not be delivered to the consumer because it has already canceled/disposed the flow or the exception has nowhere to go to begin with. Further reading: https://github.com/ReactiveX/RxJava/wiki/What's-different-in-2.0#error-handling | com.com.example.app.models.OfflineModeException: HTTP 404
at io.reactivex.rxjava3.plugins.RxJavaPlugins.onError(RxJavaPlugins.java:367)
at io.reactivex.rxjava3.internal.operators.single.SingleZipArray$ZipCoordinator.innerError(SingleZipArray.java:139)
at io.reactivex.rxjava3.internal.operators.single.SingleZipArray$ZipSingleObserver.onError(SingleZipArray.java:175)
at io.reactivex.rxjava3.internal.operators.single.SingleFlatMap$SingleFlatMapCallback.onError(SingleFlatMap.java:91)
at io.reactivex.rxjava3.internal.operators.single.SingleFlatMap$SingleFlatMapCallback.onError(SingleFlatMap.java:91)
at io.reactivex.rxjava3.internal.operators.single.SingleFlatMap$SingleFlatMapCallback.onError(SingleFlatMap.java:91)
at io.reactivex.rxjava3.internal.operators.single.SingleFlatMap$SingleFlatMapCallback.onError(SingleFlatMap.java:91)
at io.reactivex.rxjava3.internal.operators.single.SingleMap$MapSingleObserver.onError(SingleMap.java:70)
at io.reactivex.rxjava3.internal.operators.single.SingleMap$MapSingleObserver.onSuccess(SingleMap.java:61)
at io.reactivex.rxjava3.internal.operators.observable.ObservableSingleSingle$SingleElementObserver.onComplete(ObservableSingleSingle.java:110)
at io.reactivex.rxjava3.internal.operators.observable.ObservableSubscribeOn$SubscribeOnObserver.onComplete(ObservableSubscribeOn.java:68)
at retrofit2.adapter.rxjava3.CallExecuteObservable.subscribeActual(CallExecuteObservable.java:52)
at io.reactivex.rxjava3.core.Observable.subscribe(Observable.java:13095)
at io.reactivex.rxjava3.internal.operators.observable.ObservableSubscribeOn$SubscribeTask.run(ObservableSubscribeOn.java:96)
at io.reactivex.rxjava3.core.Scheduler$DisposeTask.run(Scheduler.java:589)
at io.reactivex.rxjava3.internal.schedulers.ScheduledRunnable.run(ScheduledRunnable.java:65)
at io.reactivex.rxjava3.internal.schedulers.ScheduledRunnable.call(ScheduledRunnable.java:56)
at java.util.concurrent.FutureTask.run(FutureTask.java:266)
at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:301)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1167)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:641)
at java.lang.Thread.run(Thread.java:920)
Caused by: com.com.example.app.models.OfflineModeException: HTTP 404
Can anyone please guide me how I can handle this error and avoid crash?
throw OfflineModeException(response)
// you are explicitly throwing exception on unsuccessful response. Remove this line or add a try catch block
You can not use throw directly because map function accept functional Interface Function<T,R>. In Function<T,R> abstract method is R apply(T t) so in apply function no throws present so you can not use throw keyword without try catch block
in map.

App falling down when Internet restored, RxJava & Retrofit

I need to get data from Api for each list element also I need to implement Internet connection checker.
Then program starts everything works great.
I disable Internet on live, and run method again (using swipeRefreshLayout), I receive InternetErrorToast, great!
I restored Internet on live, and run method again (using swipeRefreshLayout), app crashed with error.
Now I have following method.
Note! dataManager.getCityConditionsResponse() return Single
#Override
public void updateCitiesList() {
List<City> citiesList = dataManager.getCitiesFromDb();
if (!dataManager.isInternetConnected()) {
view.showCitiesList(citiesList);
view.showInternetErrorToast();
} else {
compositeDisposable.add(Observable.fromIterable(citiesList)
.doOnNext(city -> dataManager.getCityConditionsResponse(city.getQuery())
.subscribe(
response -> {
city.setTemp(response.getTemp());
city.setIcon(response.getIcon());
},
error -> view.showServerErrorToast()))
.toList()
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(
list -> view.showCitiesList(list),
error -> view.showServerErrorToast()
));
}
view.hideRefreshingStatus();
}
On step 3 I have this error
018-10-12 16:34:54.033 19013-19046/mike.weather E/AndroidRuntime: FATAL EXCEPTION: RxCachedThreadScheduler-1
Process: mike.weather, PID: 19013
io.reactivex.exceptions.CompositeException: 2 exceptions occurred.
at io.reactivex.internal.observers.ConsumerSingleObserver.onError(ConsumerSingleObserver.java:49)
at io.reactivex.internal.operators.observable.ObservableSingleSingle$SingleElementObserver.onError(ObservableSingleSingle.java:93)
at retrofit2.adapter.rxjava2.BodyObservable$BodyObserver.onError(BodyObservable.java:72)
at retrofit2.adapter.rxjava2.CallExecuteObservable.subscribeActual(CallExecuteObservable.java:56)
at io.reactivex.Observable.subscribe(Observable.java:12090)
at retrofit2.adapter.rxjava2.BodyObservable.subscribeActual(BodyObservable.java:34)
at io.reactivex.Observable.subscribe(Observable.java:12090)
at io.reactivex.internal.operators.observable.ObservableSingleSingle.subscribeActual(ObservableSingleSingle.java:35)
at io.reactivex.Single.subscribe(Single.java:3438)
at io.reactivex.Single.subscribe(Single.java:3424)
at mike.weather.ui.main.MainActivityPresenter.lambda$updateCitiesList$3(MainActivityPresenter.java:49)
at mike.weather.ui.main.-$$Lambda$MainActivityPresenter$UqLPaAef0SB9PT-Rz654tgX3dnA.accept(Unknown Source:4)
at io.reactivex.internal.operators.observable.ObservableDoOnEach$DoOnEachObserver.onNext(ObservableDoOnEach.java:93)
at io.reactivex.internal.operators.observable.ObservableDoOnEach$DoOnEachObserver.onNext(ObservableDoOnEach.java:101)
at io.reactivex.internal.operators.observable.ObservableFromIterable$FromIterableDisposable.run(ObservableFromIterable.java:98)
at io.reactivex.internal.operators.observable.ObservableFromIterable.subscribeActual(ObservableFromIterable.java:58)
at io.reactivex.Observable.subscribe(Observable.java:12090)
at io.reactivex.internal.operators.observable.ObservableDoOnEach.subscribeActual(ObservableDoOnEach.java:42)
at io.reactivex.Observable.subscribe(Observable.java:12090)
at io.reactivex.internal.operators.observable.ObservableDoOnEach.subscribeActual(ObservableDoOnEach.java:42)
at io.reactivex.Observable.subscribe(Observable.java:12090)
at io.reactivex.internal.operators.observable.ObservableToListSingle.subscribeActual(ObservableToListSingle.java:58)
at io.reactivex.Single.subscribe(Single.java:3438)
at io.reactivex.internal.operators.single.SingleDoOnSuccess.subscribeActual(SingleDoOnSuccess.java:35)
at io.reactivex.Single.subscribe(Single.java:3438)
at io.reactivex.internal.operators.single.SingleSubscribeOn$SubscribeOnObserver.run(SingleSubscribeOn.java:89)
at io.reactivex.Scheduler$DisposeTask.run(Scheduler.java:578)
at io.reactivex.internal.schedulers.ScheduledRunnable.run(ScheduledRunnable.java:66)
at io.reactivex.internal.schedulers.ScheduledRunnable.call(ScheduledRunnable.java:57)
at java.util.concurrent.FutureTask.run(FutureTask.java:266)
at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:301)
........................
It would be more Rx idiomatic if you rewrite your observable like that
compositeDisposable.add(Observable.fromArray(citiesList)
.flatMap(Observable::fromIterable)
.flatMapSingle(city ->
dataManager.getCityConditionsResponse(city.getQuery())
.map(response -> {
city.setTemp(response.getTemp());
city.setIcon(response.getIcon());
return city;
})
)
.toList()
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(
list -> view.showCitiesList(list),
error -> view.showServerErrorToast()
));
After that I think you problem will disappear.

Rxjava2 just method - how to run room insertion on another thread?

I have a room persistant database insertion method which looks like this:
#Dao
public interface CountriesDao{
#Insert(onConflict = REPLACE)
List<Long> addCountries(List<CountryModel> countryModel);
}
I realize that this can't be run on the main thread. Here is how I define my database:
Room.inMemoryDatabaseBuilder(context.getApplicationContext(), MyDatabase.class).build();
I am trying to use rxjava2 so that I don't run on main thread. I have created the following method:
public void storeCountries(List<CountryModel> countriesList) {
Observable.just(db.countriesDao().addCountries(countriesList))
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new DefaultSubscriber<List<Long>>(){
#Override
public void onSubscribe(#NonNull Disposable d) {
super.onSubscribe(d);
}
#Override
public void onNext(#NonNull List<Long> longs) {
super.onNext(longs);
Timber.d("insert countries transaction complete");
}
#Override
public void onError(#NonNull Throwable e) {
super.onError(e);
Timber.d("error storing countries in db"+e);
}
#Override
public void onComplete() {
Timber.d("insert countries transaction complete");
}
});
}
For me this is clearly now running on another thread. NOT the main thread but when I run this code i get the following error:
The full stack trace is below. Why is this happening ?
Process: com.mobile.myapp.staging, PID: 12990
java.lang.IllegalStateException: Fatal Exception thrown on Scheduler.
Caused by: java.lang.IllegalStateException: Cannot access database on
the main thread since it may potentially lock the UI for a long period
of time.
at
io.reactivex.android.schedulers.HandlerScheduler$ScheduledRunnable.run(HandlerScheduler.java:111)
at android.os.Handler.handleCallback(Handler.java:751)
at android.os.Handler.dispatchMessage(Handler.java:95)
at android.os.Looper.loop(Looper.java:154)
at android.app.ActivityThread.main(ActivityThread.java:6077)
at java.lang.reflect.Method.invoke(Native Method)
at
com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:866)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:756)
Caused by: java.lang.IllegalStateException: Cannot access database on
the main thread since it may potentially lock the UI for a long period
of time.
at
android.arch.persistence.room.RoomDatabase.assertNotMainThread(RoomDatabase.java:138)
at
android.arch.persistence.room.RoomDatabase.beginTransaction(RoomDatabase.java:185)
at
com.mobile.myapp.data.room.dao.CountriesDao_Impl.addCountries(CountriesDao_Impl.java:165)
at
com.mobile.myapp.data.repositories.CountryRepository.storeCountries(CountryRepository.java:42)
at
com.mobile.myapp.UI.mvp.Presenters.SignUpPresenter.cacheCountries(SignUpPresenter.java:40)
at
com.mobile.myapp.UI.mvp.Presenters.SignUpPresenter$CountriesSubscriber.onNext(SignUpPresenter.java:60)
at
com.mobile.myapp.UI.mvp.Presenters.SignUpPresenter$CountriesSubscriber.onNext(SignUpPresenter.java:49)
at
io.reactivex.internal.operators.observable.ObservableObserveOn$ObserveOnObserver.drainNormal(ObservableObserveOn.java:200)
at
io.reactivex.internal.operators.observable.ObservableObserveOn$ObserveOnObserver.run(ObservableObserveOn.java:252)
at
io.reactivex.android.schedulers.HandlerScheduler$ScheduledRunnable.run(HandlerScheduler.java:109)
at android.os.Handler.handleCallback(Handler.java:751) 
at android.os.Handler.dispatchMessage(Handler.java:95) 
at android.os.Looper.loop(Looper.java:154) 
at android.app.ActivityThread.main(ActivityThread.java:6077) 
at java.lang.reflect.Method.invoke(Native Method) 
at
com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:866) 
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:756)
Not important but if you need to know what defaultSubscriber class looks like here it is:
DefaultSubscriber.java
public class DefaultSubscriber<T> implements Observer<T> {
Disposable disposable;
#Override
public void onSubscribe(#NonNull Disposable d) {
disposable = d;
}
#Override
public void onNext(#NonNull T t) {
}
#Override
public void onError(#NonNull Throwable e) {
Timber.e(e);
}
#Override
public void onComplete() {
}
public void unsubscribe(){
if(disposable!=null && !disposable.isDisposed()){
disposable.dispose();
}
}
}
This is a common mistake: just() won't execute the "code" within its parenthesis as just takes a value, not a computation. You need fromCallable:
Observable.fromCallable(() -> db.countriesDao().addCountries(countriesList))
Even better, you could use a Completable. Its description: Represents a computation without any value but only indication for completion or exception.
Completable.fromAction(() -> db.countriesDao().addCountries(list)).subscribe();
Note: Room doesn't support database access on the main thread unless
you've called allowMainThreadQueries() on the builder because it might
lock the UI for a long period of time. Asynchronous queries - queries
that return instances of LiveData or Flowable are exempt from this
rule because they asynchronously run the query on a background thread
when needed.
So your code can be like this
Completable.fromAction(() -> db.countriesDao()
.addCountries(list))
.subscribeOn(Schedulers.io())
.subscribe();
From room room 2.1.0-alpha02, you can use (Completeable, Single, Maybe) when insertion (
https://medium.com/androiddevelopers/room-rxjava-acb0cd4f3757)
Example
#Dao
interface UserDao{
#Insert
Completable insert(final User user); // currently, we must put final before user variable or you will get error when compile
}
Using
db.userDao().insert(user).subscribeOn(Schedulers.io()).subscribe(new Action() {
#Override
public void run() throws Exception {
// success
}
}, new Consumer < Throwable > () {
#Override
public void accept(Throwable throwable) throws Exception {
// error
}
});
You can also use the Single observable
Single.create(new SingleOnSubscribe<List<Long>>() {
#Override
public void subscribe(SingleEmitter<List<Long>> emitter) throws Exception {
try {
List<Long> ids = db.countriesDao().addCountries(countriesList);
emitter.onSuccess(ids);
} catch (Throwable t) {
emitter.onError(t);
}
}})
.observeOn(AndroidSchedulers.mainThread())
.subscribeOn(Schedulers.io())
.subscribeWith(new DisposableSingleObserver<Long>() {
#Override
public void onSuccess(List<Long> ids) {
}
#Override
public void onError(Throwable e) {
}
});
Ensure you have added both dependencies,
"implementation 'io.reactivex.rxjava3:rxjava:3.0.0'
implementation 'io.reactivex.rxjava3:rxandroid:3.0.0'"

Retrofit2 + RxJava error handling

I am using RxJava and Retrofit2 (with OkHttp as the HTTP client) to do networking and am trying to understand how different errors are handled by Retrofit2 and how they look from the RxJava side. The following code illustrates an RxJava Subscriber callback for a network call (made with Retrofit).
Subscription subscription = observable
.subscribeOn(mScheduler)
.observeOn(mAndroidScheduler)
.subscribe(new Subscriber<User>() {
#Override
public void onCompleted() {
Timber.d("onCompleted called");
mRetainerView.clearUserObservable();
mActivityView.hideProgressBar();
mActivityView.enableUi();
}
#Override
public void onError(Throwable e) {
Timber.d("onError called");
Timber.d(e.toString());
mRetainerView.clearUserObservable();
mActivityView.hideProgressBar();
mActivityView.enableUi();
}
#Override
public void onNext(User user) {
Timber.d("onNext called");
mRetainerView.clearUserObservable();
mActivityView.hideProgressBar();
mActivityView.enableUi();
mActivityView.launchMainActivity();
}
});
My question is, in what cases will onError() be called and once it's been called, how can I interrogate the Throwable to determine the cause?
From the Retrofit source it looks like the only Throwables that are possible to see are IOException and HttpException. Can anyone verify that that is true?
Here's the basics: onError() will be called if:
the observable you're subscribing to throws an exception (e.g. you get an IOException while trying to read a file)
an exception is raised in your onNext() method.
If there's an exception in your onComplete(), RxJava will propagate an rx.exceptions.OnCompletedFailedException and if there's an exception in onError() - you'll get rx.exceptions.OnErrorFailedException.
That said, you can just probe the Throwable you receive in your onError() method for exceptions that you're expecting. For example you know that if your API call results in client error (4xx), Retrofit will wrap it into HttpException. If there's a timeout with the request you'll get a SocketTimeoutException. Here's a rough example:
#Override
public void onError(Throwable e) {
Timber.d("onError called");
Timber.d(e.toString());
handleError(e);
}
private handleError(Throwable throwable) {
if (throwable instanceof HttpException) {
HttpException httpException = (HttpException)throwable;
int statusCode = httpException.code();
// handle different HTTP error codes here (4xx)
} else if (throwable instanceof SocketTimeoutException) {
// handle timeout from Retrofit
} else if (throwable instanceof IOException) {
// file was not found, do something
} else {
// generic error handling
mRetainerView.clearUserObservable();
mActivityView.hideProgressBar();
mActivityView.enableUi();
}
Do not use onError for flow. That'd be as bad as try-catch for flow.
Error HTTP codes, are valid responses and you should not deal with them in onError.
You can wrap the return type of your Retrofit services in Result, that gives you the means to get information about what happen with your call without throwing exceptions.
You can handle the state of your app using this pattern:
service.getSomething()
.map(r -> Model.success(r.response()))
.onErrorReturn(Model::error)
.observeOn(AndroidSchedulers.mainThread())
.startWith(Resource.loading())
.subscribe(r -> {
myProgressBar.setVisible(r.isLoading());
if (r.isSuccess()) {
handleSuccess(); // e.g. 400 is also success but needs handling
}
if (r.isError()) {
handleError();
}
}, OnErrorNotImplementedException::new);
See how I tried to handle all possible states within the stream and deliberately I throw OnErrorNotImplementedException for something I might've missed. This is very personal but I prefer to crash-fast-and-furious rather than being in an unknown state for a while that later will manifest in a crash harder to debug.
In Kotlin I have used bellow like..
disposable.add(apiService.getLogin_service(parment1,parment1)
.subscribeOn(Schedulers.newThread())
.observeOn(AndroidSchedulers.mainThread())
.subscribeWith(object : DisposableSingleObserver<Login_Reg_Data_Model>() {
override fun onSuccess(model: Login_Reg_Data_Model) {
//success
}
override fun onError(e: Throwable) {
if (e is HttpException) {
// We had non-200 http error
Log.e("time exceptionr******>",e.message)
} else if (e is SocketTimeoutException) {
//time exception
Log.e("time exception******>",e.message)
} else if (e is IOException) {
// A network error
Log.e("network error******>",e.message)
} else {
//unknown error
Log.e("unknown error******>",e.message)
}
}
})
)

Exception thrown. Add onError handling

I've already added onErrorReturn and doOnError, but I still get fatal error. Here's my code:
apiInterface.submitDataToAnalyze("dataToAnalyze", HelperFunctions.authenticationData, 1, uuid, dataToAnalyze.toString(), todayDate)
.onErrorReturn(new Func1<Throwable, BasicResponse>() {
#Override
public BasicResponse call(Throwable throwable) {
Log.e(FILE_NAME, "submitDataToAnalyze throwable??");
Log.e(FILE_NAME, throwable.getMessage().toString());
return null;
}
})
.flatMap(new Func1<BasicResponse, Observable<LocationSuggestion>>() {
#Override
public Observable<LocationSuggestion> call(BasicResponse basicResponse) {
Log.v(FILE_NAME, "basicResponse: " + basicResponse.toString());
if (basicResponse.getResult() == 1) {
//update TABLE_REQUEST_SUGGESTIONS with serverResponse = 1
dbc.updateRequestSuggestionLog(strTodayDate, strDataToAnalyze, basicResponse.getResult());
return apiInterface.getSuggestion("getSuggestion", HelperFunctions.authenticationData, HelperFunctions.requestVersion, uuid,
strLocationLat, strLocationLng, radius, requestForDate);
} else {
return Observable.just(null);
}
}
}).doOnError(new Action1<Throwable>() {
#Override
public void call(Throwable t) {
Log.e(FILE_NAME, "masuk doOnError??");
Log.e(FILE_NAME, t.getMessage().toString());
}
}).subscribeOn(Schedulers.io()).observeOn(AndroidSchedulers.mainThread()).subscribe();
}
Specifically, here's the error message:
E/ISRS: masuk doOnError??
E/ISRS: masuk 22??
E/AndroidRuntime: FATAL EXCEPTION: main
java.lang.IllegalStateException: Exception thrown on Scheduler.Worker thread. Add `onError` handling.
at rx.internal.schedulers.ScheduledAction.run(ScheduledAction.java:60)
Additional information: the fatal error would occur if submitDataToAnalyze() has problem connecting to my server due to bad connection etc
In onErrorReturn method you return null, and then this null is propagated further along the chain. So it causes NPE (eg basicResponse.toString()). In this case doOnError is invocated, but your subscriber still get Throwable, that stays unhandled.
So you have to pass Subscriber instance with onError implementation.
Another option: in your chain you can use something like onErrorResumeNext(Observable.empty()), so it'll emit nothing and then complete. In this case you are have not to pass Subscriber implementation to subscribe.

Categories

Resources