24

I'm doing some background work and showing a DialogFragment while I do that. Once my work is done and the relevant callback is invoked, I dismiss the dialog. When I do, I get a crash caused by a NPE in the android source, here:

void dismissInternal(boolean allowStateLoss) {
        if (mDialog != null) {
            mDialog.dismiss();
            mDialog = null;
        }
        mRemoved = true;
        if (mBackStackId >= 0) {
            getFragmentManager().popBackStack(mBackStackId,
                    FragmentManager.POP_BACK_STACK_INCLUSIVE);
            mBackStackId = -1;
        } else {
            FragmentTransaction ft = getFragmentManager().beginTransaction();
            ft.remove(this);
            if (allowStateLoss) {
                ft.commitAllowingStateLoss();
            } else {
                ft.commit();
            }
        }
    }

specifically at the line: FragmentTransaction ft = getFragmentManager().beginTransaction();

LuxuryMode
  • 33,401
  • 34
  • 117
  • 188
  • 2
    Has the Fragment/Activity from which you are dismissing the dialog gone into the background, i.e. its `onPause` method is called? In this case I'd expect problems and tend to work around such issues by ensuring I don't do things like dismissing dialogs until `onResume` is called (by implementing paused handler approach listed [here](http://stackoverflow.com/questions/7992496/how-to-handle-asynctask-onpostexecute-when-paused-to-avoid-illegalstateexception), or [here](http://stackoverflow.com/questions/8040280/how-to-handle-handler-messages-when-activity-fragment-is-paused). – PJL May 11 '12 at 13:15
  • @PJL interesting point. I should log onPause. Makes sense that onPause would be called. I'll give your approach a shot. – LuxuryMode May 11 '12 at 14:51

6 Answers6

15

This may also occur when you call dismiss() before you have called show() like Sogger said.

After Dialog object is constructed but before dialog is not showed, if (mDialog != null) can be passed and NullPointerException will occur.

When you check if mDialog is null or not,

if (mDialog != null) {
    mDialog.dismiss();
    mDialog = null;
}

Add more conditions like below,

if ((mDialog != null) && mDialog.isAdded() && mDialog.isResumed()) {
    mDialog.dismiss();
    mDialog = null;
}

I think that mDialog.isAdded() condition might be enough...

pretty angela
  • 2,241
  • 1
  • 19
  • 8
  • Checking `isAdded()` and `isResumed()` is redundant - if a fragment is resumed then it is also added. See [fragment lifecycle docs](https://developer.android.com/guide/components/fragments.html#Creating). – yuval Jul 27 '15 at 19:38
11

Simplest solution is to check "getFragmentManager()" for "null" before calling "dismiss()" method. Also you can extend "DialogFragment" class and override method "dismiss()" to check it there:

@Override
public void dismiss()
{
    if (getFragmentManager() != null) super.dismiss();
}
Ruslan Yanchyshyn
  • 2,774
  • 1
  • 24
  • 22
7

I know this message is old but I ran into a similar case that I needed to solvew without refactoring or changing a lot of code. Hope it's useful for somebody

   package com.example.playback;

import android.os.Bundle;
import android.support.v4.app.DialogFragment;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;

public class SaferDialogFragment extends DialogFragment {

    private boolean allowStateLoss = false;
    private boolean shouldDismiss = false;

    public SaferDialogFragment() {
    }

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setRetainInstance(true);
    }

    @Override
    public void onStart() {
        super.onStart();
        //check if we should dismiss the dialog after rotation
        if (shouldDismiss) {
            if (allowStateLoss)
                dismissAllowingStateLoss();
            else
                dismiss();
        }
    }

    @Override
    public void dismiss() {
        if (getActivity() != null) { // it's "safer" to dismiss
            shouldDismiss = false;
            super.dismiss();
        } else {
            shouldDismiss = true;
            allowStateLoss = false;
        }
    }

    @Override
    public void dismissAllowingStateLoss() {
        if (getActivity() != null) { // it's "safer" to dismiss
            shouldDismiss = false;
            super.dismissAllowingStateLoss();
        } else
            allowStateLoss = shouldDismiss = true;
    }

    //keeping dialog after rotation
    @Override
    public void onDestroyView() {
        if (getDialog() != null && getRetainInstance())
            getDialog().setDismissMessage(null);
        super.onDestroyView();
    }



    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
            Bundle savedInstanceState) {
        /** omitted code **/
        return super.onCreateView(inflater, container, savedInstanceState);
    }
}
oferiko
  • 1,927
  • 2
  • 16
  • 17
5

My bet would be that the code you posted is from the background thread... you aren't allowed to update the UI from anywhere other than the UI thread.

You can use onPostExecute() or runOnUiThread() to achieve your goal (if my guess is right about what is happening)

Barak
  • 16,318
  • 9
  • 52
  • 84
1

Checking if it's Visible before dimissing could avoid this null pointer exception

    if (mDialog != null && mDialog.isVisible) {
        mDialog.dismiss();
        mDialog = null;
    }
Mayank Mehta
  • 689
  • 1
  • 8
  • 12
1

The callback which is invoked is probably on the activity which is or should be destroyed (after orientation change), also the progress dialog might have been instantiated with that same activity. This might cause the NPE. Callbacks on activities should not be invoked from background tasks, to prevent these kinds of problems. Decouple the background task from the activity, for example using otto, or prevent the background task from invoking the (to be) destroyed activity.

This is some code of mine:

static inner class of activity:

    public static class ProgressDialogFragment extends DialogFragment {
    ProgressDialog dialog;

    public ProgressDialogFragment() {
    }

    @Override
    public Dialog onCreateDialog(Bundle savedInstanceState) {
        dialog = new ProgressDialog(getActivity(), getTheme());
        dialog.setTitle(getString(R.string.please_wait));
        dialog.setMessage(getString(R.string.uploading_picture));
        dialog.setIndeterminate(true);
        dialog.setProgressStyle(ProgressDialog.STYLE_SPINNER);
        return dialog;
    }

}

Otto subscription in activity:

@Subscribe
public void onUploadEvent(UploadAvatarEvent uploadAvatarEvent) {
    switch (uploadAvatarEvent.state) {
        case UploadAvatarEvent.STATE_UPLOADING:
            if (!mProgressDialog.isAdded()) {
                mProgressDialog.show(getFragmentManager(), TAG_PROGRESS_DIALOG);
            }
            break;
        case UploadAvatarEvent.STATE_UPLOAD_SUCCES:
            mProgressDialog.dismiss();
            break;
        case UploadAvatarEvent.STATE_UPLOAD_ERROR:
            mProgressDialog.dismiss();
            break;
    }
}

onCreate() in activity:

        mProgressDialog = (ProgressDialogFragment) getFragmentManager().findFragmentByTag(TAG_PROGRESS_DIALOG);
    if (mProgressDialog == null) {
        mProgressDialog = new ProgressDialogFragment();
    }
Tekmology
  • 41
  • 5