DialogFragment.dismiss崩溃,抛出NullPointerException异常

24

我正在进行一些后台工作,并在此期间显示一个DialogFrament。一旦我的工作完成并调用相关回调函数,我就会关闭对话框。但是当我这样做时,我会收到由于Android源代码中的NPE而导致的崩溃,位置在这里:

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();
            }
        }
    }

具体来说,这是在这一行:FragmentTransaction ft = getFragmentManager().beginTransaction();


2
你要关闭的对话框所在的Fragment/Activity是否已经进入后台,即其onPause方法是否被调用?如果是这种情况,我会预计出现问题,并倾向于通过确保在onResume被调用之前不执行关闭对话框等操作(通过实现此处列出的暂停处理程序方法或此处列出的处理处理程序消息的方法)来解决此类问题。 - PJL
@PJL 有趣的观点。我应该记录onPause。 onPause被调用是有道理的。我会尝试你的方法。 - LuxuryMode
6个回答

15

如Sogger所说,当您在调用show()之前调用dismiss()时也会发生这种情况。

如果Dialog对象已构造但未显示对话框,则可以传递if (mDialog != null),这将导致NullPointerException。

当您检查mDialog是否为空时,

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

添加更多类似以下的条件:

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

我认为mDialog.isAdded()这个条件可能已经足够了...


检查 isAdded()isResumed() 是多余的 - 如果一个片段被恢复,那么它也被添加了。请参阅 片段生命周期文档 - yuval

11
最简单的解决方法是在调用"dismiss()"方法之前检查"getFragmentManager()"是否为"null"。此外,您可以扩展"DialogFragment"类并覆盖方法"dismiss()"以在那里进行检查:
@Override
public void dismiss()
{
    if (getFragmentManager() != null) super.dismiss();
}

7

我知道这条消息已经过时了,但我遇到了一个类似的情况,需要在不重构或更改大量代码的情况下解决。希望对某些人有用。

   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);
    }
}

5

我的猜测是,你所贴的代码来自后台线程......除了UI线程,你不能从任何其他地方更新UI。

如果我对发生的情况猜测正确,你可以使用onPostExecute()或runOnUiThread()来实现你的目标。


3
明白了,请提供待翻译的内容。 - LuxuryMode
我也遇到了同样的问题,而且我已经从UI线程中调用了dismiss() - Jason Robinson
1
当您在调用show()之前调用dismiss()时,也可能会发生这种情况。 - Sogger

1
回调函数可能会在销毁的活动上被调用(在方向更改后),进度对话框也可能已经使用了该活动。这可能会导致NPE。为了防止这些问题,不应该从后台任务中调用活动上的回调。可以将后台任务与活动解耦,例如使用otto,或者防止后台任务调用即将销毁的活动。
这是我的一些代码:
活动的静态内部类:
    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活动中的订阅:

@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()函数:
        mProgressDialog = (ProgressDialogFragment) getFragmentManager().findFragmentByTag(TAG_PROGRESS_DIALOG);
    if (mProgressDialog == null) {
        mProgressDialog = new ProgressDialogFragment();
    }

1
在关闭之前检查它是否可见可以避免出现空指针异常。
    if (mDialog != null && mDialog.isVisible) {
        mDialog.dismiss();
        mDialog = null;
    }

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接