Android java.lang.IllegalStateException: 片段已添加

Android java.lang.IllegalStateException: Fragment already added

我正在获取一个正在等待来自网络服务的请求的加载对象。但有时这个对象会导致我的应用程序结束。我无法准确检测到所涉及的错误。

为了防止这个错误,我在传递 activity 的同时调用了对象 "dismiss" 和 "cancel",但这没有用。然后我分配了一个 "null" 的值,它也不起作用。又没工作因为我以为我在和Tag打交道

我的错误代码;

2019-03-27 11:51:20.502 29685-29685/com.xxxx.app E/AndroidRuntime: FATAL EXCEPTION: main
    Process: com.xxxx.app, PID: 29685
    java.lang.IllegalStateException: Fragment already added: DelayedProgressDialog{d8bd442 #1 Delaleyed}
        at androidx.fragment.app.FragmentManagerImpl.addFragment(FragmentManager.java:1916)
        at androidx.fragment.app.BackStackRecord.executeOps(BackStackRecord.java:765)
        at androidx.fragment.app.FragmentManagerImpl.executeOps(FragmentManager.java:2625)
        at androidx.fragment.app.FragmentManagerImpl.executeOpsTogether(FragmentManager.java:2411)
        at androidx.fragment.app.FragmentManagerImpl.removeRedundantOperationsAndExecute(FragmentManager.java:2366)
        at androidx.fragment.app.FragmentManagerImpl.execPendingActions(FragmentManager.java:2273)
        at androidx.fragment.app.FragmentManagerImpl.run(FragmentManager.java:733)
        at android.os.Handler.handleCallback(Handler.java:873)
        at android.os.Handler.dispatchMessage(Handler.java:99)
        at android.os.Looper.loop(Looper.java:214)
        at android.app.ActivityThread.main(ActivityThread.java:6981)
        at java.lang.reflect.Method.invoke(Native Method)
        at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:493)
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1445)

我的使用进度对话框class;

    public class DelayedProgressDialog extends DialogFragment {
    private static final int DELAY_MILLISECOND = 450;
    private static final int MINIMUM_SHOW_DURATION_MILLISECOND = 300;
    private static final int PROGRESS_CONTENT_SIZE_DP = 80;

    private ProgressBar mProgressBar;
    private boolean startedShowing;
    private long mStartMillisecond;
    private long mStopMillisecond;

    private FragmentManager fragmentManager;
    private String tag;
    private Handler showHandler;

    // default constructor. Needed so rotation doesn't crash
    public DelayedProgressDialog() {
        super();
    }

    @NonNull
    @SuppressLint("InflateParams")
    @Override
    public Dialog onCreateDialog(Bundle savedInstanceState) {
        AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
        LayoutInflater inflater = getActivity().getLayoutInflater();

        builder.setView(inflater.inflate(R.layout.dialog_progress, null));
        return builder.create();
    }

    @Override
    public void onStart() {
        super.onStart();
        mProgressBar = getDialog().findViewById(R.id.progress);

        if (getDialog().getWindow() != null) {
            int px = (int) (PROGRESS_CONTENT_SIZE_DP * getResources().getDisplayMetrics().density);
            getDialog().getWindow().setLayout(px, px);
            getDialog().getWindow().setBackgroundDrawable(new ColorDrawable(Color.TRANSPARENT));
        }
    }

    @Override
    public void show(FragmentManager fm, String tag) {
        if (isAdded())
            return;

        this.fragmentManager = fm;
        this.tag = tag;
        mStartMillisecond = System.currentTimeMillis();
        startedShowing = false;
        mStopMillisecond = Long.MAX_VALUE;

        showHandler = new Handler();
        showHandler.postDelayed(new Runnable() {
            @Override
            public void run() {
                // only show if not already cancelled
                if (mStopMillisecond > System.currentTimeMillis())
                    showDialogAfterDelay();
            }
        }, DELAY_MILLISECOND);
    }

    private void showDialogAfterDelay() {
        startedShowing = true;

        DialogFragment dialogFragment = (DialogFragment) fragmentManager.findFragmentByTag(tag);
        if (dialogFragment != null) {
            fragmentManager.beginTransaction().show(dialogFragment).commitAllowingStateLoss();
        } else {
            FragmentTransaction ft = fragmentManager.beginTransaction();
            ft.add(this, tag);
            ft.commitAllowingStateLoss();
        }
    }

    public void cancel() {
        if(showHandler == null)
            return;

        mStopMillisecond = System.currentTimeMillis();
        showHandler.removeCallbacksAndMessages(null);

        if (startedShowing) {
            if (mProgressBar != null) {
                cancelWhenShowing();
            } else {
                cancelWhenNotShowing();
            }
        } else
            dismiss();
    }

    private void cancelWhenShowing() {
        if (mStopMillisecond < mStartMillisecond + DELAY_MILLISECOND + MINIMUM_SHOW_DURATION_MILLISECOND) {
            final Handler handler = new Handler();
            handler.postDelayed(new Runnable() {
                @Override
                public void run() {
                    dismiss();
                }
            }, MINIMUM_SHOW_DURATION_MILLISECOND);
        } else {
            dismiss();
        }
    }

    private void cancelWhenNotShowing() {
        final Handler handler = new Handler();
        handler.postDelayed(new Runnable() {
            @Override
            public void run() {
                dismiss();
            }
        }, DELAY_MILLISECOND);
    }

    @Override
    public void dismiss() {
        FragmentTransaction ft = fragmentManager.beginTransaction();
        ft.remove(this);
        ft.commitAllowingStateLoss();
    }
}

只需更改private void showDialogAfterDelay()。您必须检查对话框是否已添加;

添加:if(dialogFragment != null && dialogFragment.isAdded()) { return; }

该代码应该 运行 在您尝试显示对话框之前。

我终于找到了解决办法。我在我的 "showDialogAfterDelay" 函数中寻找带有 "findFragmentByTag" 的标签。但这从未成真。我无法确定它是否是 "Added"。我用来解决这个问题的代码;

        fragmentManager.executePendingTransactions();

您需要检查dialogFragment是否已经添加;如果 dialogFragment 未添加则显示它:

if (fragmentManager.findFragmentByTag("progress_dialog") == null) 
    dialogFragment.show(getSupportFragmentManager(), "progress_dialog");

您可以在打开片段之前快速检查您的代码:

    if(myFragment.isAdded()){
       return;
    }
// else continue with code