-2

I am sending API request to send email and on successful response I am calling onBackPressed() in my EmailActivity class.

I have also AbstractBaseActivity class which is extended by my EmailActivity class.

I am getting this crash very often.

Fatal Exception: java.lang.NullPointerException: Attempt to invoke virtual method 'android.os.Handler android.app.FragmentHostCallback.getHandler()' on a null object reference
       at android.app.FragmentManagerImpl.ensureExecReady + 2003(FragmentManager.java:2003)
       at android.app.FragmentManagerImpl.execPendingActions + 2056(FragmentManager.java:2056)
       at android.app.FragmentManagerImpl.popBackStackImmediate + 870(FragmentManager.java:870)
       at android.app.FragmentManagerImpl.popBackStackImmediate + 831(FragmentManager.java:831)
       at android.app.Activity.onBackPressed + 3111(Activity.java:3111)
       at android.support.v4.app.FragmentActivity.onBackPressed + 187(FragmentActivity.java:187)
       at com.test.test.core.AbstractBaseActivity.onBackPressed + 71(AbstractBaseActivity.java:71)
       at com.test.test.emails.EmailActivity.displayEmailSent + 283(EmailActivity.java:283)
       at com.test.presentation.emails.EmailPresenter.lambda$sendEmail$2$EmailPresenter + 100(EmailPresenter.java:100)
       at com.test.presentation.emails.EmailPresenter$$Lambda$2.accept + 4(:4)
       at io.reactivex.internal.observers.ConsumerSingleObserver.onSuccess + 62(ConsumerSingleObserver.java:62)
       at io.reactivex.internal.operators.single.SingleObserveOn$ObserveOnSingleObserver.run + 81(SingleObserveOn.java:81)
       at io.reactivex.android.schedulers.HandlerScheduler$ScheduledRunnable.run + 124(HandlerScheduler.java:124)
       at android.os.Handler.handleCallback + 873(Handler.java:873)
       at android.os.Handler.dispatchMessage + 99(Handler.java:99)
       at android.os.Looper.loop + 193(Looper.java:193)
       at android.app.ActivityThread.main + 6718(ActivityThread.java:6718)
       at java.lang.reflect.Method.invoke(Method.java)
       at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run + 493(RuntimeInit.java:493)
       at com.android.internal.os.ZygoteInit.main + 858(ZygoteInit.java:858)

This is code inside my EmailActivity.

  @Override public void displayEmailSent() {
    Toast.makeText(this, R.string.email_sent, Toast.LENGTH_SHORT).show();
    onBackPressed();
  }

Here is my code inside AbstractBaseActivity.

  @Override public void onBackPressed() {
    if(this instanceof AddTaskActivity ||
        this instanceof AddCustomerActivity ||
        this instanceof AddContactsActivity ||
        this instanceof NoteDetailsActivity ||
        this instanceof AddEstimateActivity ||
        this instanceof AddAgreementsActivity ||
        this instanceof AddAppointmentActivity ||
        this instanceof EditServiceLocationActivity) {
      showExitDialogForFormScreens();
    } else {
      super.onBackPressed();
    }
  }

Here is the code from EmailPresenter.

 public void sendEmail(int entityId, EmailType emailType, String emailBody, String subject, List<String> recipients) {
    disposables = RxUtil.initDisposables(disposables);

    if(TextUtils.isEmpty(subject)) {
      view.displaySubjectInvalid();
      return;
    }

    if(TextUtils.isEmpty(emailBody)) {
      view.displayEmailBodyInvalid();
      return;
    }

    if(recipients == null || recipients.isEmpty()) {
      view.displayRecipientsEmpty();
      return;
    }

    if(!Utils.isValidEmailAddress(recipients.get(0))) {
      view.displayEmailToInvalid();
      return;
    }

    String modelType = getModelType(emailType);

    view.disableEmailButton();

    Disposable disposable = emailsRepository.sendEmail(entityId, modelType, emailBody, subject, recipients)
        .subscribeOn(Schedulers.io())
        .observeOn(AndroidSchedulers.mainThread())
        .subscribe(response -> {
          view.enableEmailButton();
          if (response.isSuccessful()) {
            view.displayEmailSent();
          } else {
            view.displayEmailError();
          }
        }, throwable -> {
          view.enableEmailButton();
          view.handleError(throwable);});

    disposables.add(disposable);
  }

From EmailActivity I am returning to another activity, but if I would return two or three screens it would be return to Fragment,

How I can solve this?

Zookey
  • 2,637
  • 13
  • 46
  • 80
  • You need to figure out why the `FragmentHostCallback` is null. – Code-Apprentice Jul 16 '19 at 16:13
  • Do you have an idea how I can do that? – Zookey Jul 16 '19 at 16:15
  • show us your emailActivity presenter impl class – Nanda Z Jul 16 '19 at 16:17
  • Edited with the code from presenter class. – Zookey Jul 16 '19 at 16:19
  • Debugging NPE in Android code is tricky. I wonder why are you calling `onBackPressed()` directly? This seems very strange to me since it is a callback that should only be called when the back key is actually pressed. If you are trying to restore a previous fragment, I suggest popping the backstack instead. Or if you are stopping the current activity to return to the previous activity, you can call `finish()`. – Code-Apprentice Jul 16 '19 at 16:20
  • context of view has been destroyed then callback has no reference – Nanda Z Jul 16 '19 at 16:24
  • @Code-Apprentice Calling finish() instead of onBackPressed() sounds like a good idea. is there any edge case that I should handle if I use finish()? – Zookey Jul 16 '19 at 16:26
  • You can post it as answer and I will test it more and if it works, I will accept it. – Zookey Jul 16 '19 at 16:27
  • I have used `finish()` successfully in an app to kill the current activity. To be fair, that app didn't have any fragments, but I don't think that should be a problem. – Code-Apprentice Jul 16 '19 at 16:27
  • you have to dispose all rxjava method in destroy method – Nanda Z Jul 16 '19 at 16:28
  • I am doing that onStop() of AbstractBaseActivity. – Zookey Jul 16 '19 at 16:31

2 Answers2

5

If you are only trying to kill the current activity and go back to the previous activity, you can call finish() instead of onBackPressed().

Code-Apprentice
  • 81,660
  • 23
  • 145
  • 268
2

My opinion is your rxJava still active, based on this log

io.reactivex.internal.observers.ConsumerSingleObserver.onSuccess + 62(ConsumerSingleObserver.java:62) at io.reactivex.internal.operators.single.SingleObserveOn$ObserveOnSingleObserver.run + 81(SingleObserveOn.java:81) at io.reactivex.android.schedulers.HandlerScheduler$ScheduledRunnable.run + 124(HandlerScheduler.java:124) at android.os.Handler.handleCallback + 873(Handler.java:873) at android.os.Handler.dispatchMessage + 99(Handler.java:99)

Try to invoke something like this

@Override
    protected void onDestroy() {
        super.onDestroy();

        if(disposable != null && !disposable.isDisposed()) {
            disposable.dispose();
        }
    }
Nanda Z
  • 1,604
  • 4
  • 15
  • 37