Android RxJava2 App crashing on calling emitter.onError() from created observable

Viewed 3334

I am creating Observable manually with the help of create(). now inside, I check some conditions & based on that, I would like to notify the subscriber about error. Here's how I am creating observable:

public Observable<User> loginUser(String email, String password) {
    return Observable.create(
        emitter -> {
            myAsynchronousWork.onCompleteListener(
               result -> {
                   if(!result.isSuccess()) {
                      // This causes the crash.
                      emitter.onError(new Throwable(result.getError()));
                   } else {
                      // Process result & create User object & return it. This works as expected.
                      emitter.onNext(user);
                      emitter.onComplete();
                   }
               }
            );

        }
    );

}

& then I subscribe to loginUser() like:

loginUser("", "")
                .subscribeOn(Schedulers.io())
                .observeOn(AndroidSchedulers.mainThread())
                .subscribe(user -> {
                    Log.d("TAG", "logged in user => " + user.getUuid());
                    Log.d("TAG", "user name => " + user.getUserName());
                    Log.d("TAG", "user email => " + user.getEmailId());
                }, throwable -> {
                    Log.e("TAG", "error in login => " + throwable.getMessage());
                }, () -> {

                });

I expect that calling emitter.onError() should go inside onError of subscribe() of loginUser() where I have Logged the exception, but instead the app gets crashes with the Exception returned by emitter.onError() in logcat like there's no one to handle it!

I checked by debugging & found that while it's on line emitter.onError(), emitter was "null". however onNext & onComplete doesn't cause any problem. Please let me know where I am doing wrong?

3 Answers

The reason why my app was getting crashed was, I had actually something like this:

myAsynchronousWork.onCompleteListener(
               result -> {
                   if(!result.isSuccess()) {
                      // This causes the crash.
                      emitter.onError(new Throwable(result.getError()));
                   } else {
                      // Process result & create User object & return it. This works as expected.
                      emitter.onNext(user);
                      emitter.onComplete();
                   }
               },
               exception -> {
                   emitter.onError(exception); // This was the reason of problem!
               }
            );

I didn't posted the full part because I didn't get that it was causing the exception.

What happening here was, my code first was going inside the exception-> part which was notifying the observer about the error & that observer was getting terminated. now my result-> part was getting executed & here when I tried to call again emitter.onError(), it was crashing as there was no emitter to handle this & RxJava2 was throwing it globally.

So for me, I removed the exception -> part as it was any how going to be on result -> where I could check the result & I have also wrapped the emitter.onError with

if(!emitter.isDisposed()) emitter.onError();

because in my case, it was fine to ignore the error if emitter was disposed.

Using emitter.tryOnError works for me and it will call error inside subscribe() as well without UndeliverableException or app stop running.

When you are creating observable you have to handle all possible errors yourself. so your code should look something like below:

public Observable<User> loginUser(String email, String password) {
    return Observable.create(
        emitter -> {
          try{
            myAsynchronousWork.onCompleteListener(
               result -> {
                 try{
                   if(!result.isSuccess()) {
                      // This causes the crash.
                      emitter.onError(new Throwable(result.getError()));
                   } else {
                      // Process result & create User object & return it. This works as expected.
                      emitter.onNext(user);
                      emitter.onComplete();
                   }
                 }catch(Exception e){
                    emitter.onError(e);
                 }
               }
            );
         }catch(Exception e){
            emitter.onError(e);
        }

        }
    );

}

PS: Always ensure error checking when creating observable and disposable too.

Related