5

I'm getting the following crash reports:

Fatal Exception: java.lang.IllegalStateException: Can not perform this action after onSaveInstanceState at android.support.v4.app.FragmentManagerImpl.checkStateLoss(FragmentManager.java:1832) at android.support.v4.app.FragmentManagerImpl.enqueueAction(FragmentManager.java:1850) at android.support.v4.app.BackStackRecord.commitInternal(BackStackRecord.java:643) at android.support.v4.app.BackStackRecord.commit(BackStackRecord.java:603) at android.support.v4.app.DialogFragment.show(DialogFragment.java:143)

This following code is causing the crash, I cut out some trivial setup code for clarity. I have read up on this error and as I understand it the .show() should be safe within a user interaction such as onClick(). The only thing I can think of is that the query() is taking a long time and the user swaps out. Is that a sensible explanation for this error? It's instantaneous on my devices even with large DB. Any other possibilities? Thanks!

    foldersButton.setOnClickListener(new View.OnClickListener()
    {
        @Override
        public void onClick(View v)
        {
            final List<String> paths = new ArrayList<>();

            try(Cursor c = getActivity().getContentResolver().query(Meta.CONTENT_URI,
                    new String[]{"DISTINCT " + Meta.PARENT}, null, null,
                    Meta.PARENT + " ASC"))
            {
                while (c != null && c.moveToNext())
                {
                    String path = c.getString(c.getColumnIndex(Meta.PARENT));

                    // We place the excluded folders at the end
                    if (!mExcludedFolders.contains(path))
                        paths.add(path);
                }
            }

            [setup...]

            int[] position = new int[2];
            foldersButton.getLocationOnScreen(position);
            FolderDialog dialog = FolderDialog.newInstance(
                    paths.toArray(new String[paths.size()]),
                    visible,
                    excluded,
                    position[0],
                    position[1]);
            dialog.setStyle(DialogFragment.STYLE_NO_TITLE, R.style.FolderDialog);

            [setup...]

            dialog.show(getFragmentManager(), TAG);
        }
    });
Anthony
  • 7,638
  • 3
  • 38
  • 71

4 Answers4

6

If this code of yours is within an activity, wrap the call to show with:

if(!isDestroyed() && !isFinishing())

and if this code is within a fragment , wrap the call to show with:

if (isResumed() && !isRemoving())

This more or less would solve the issue of landing in an inconsistent UI state

Angel Solis
  • 511
  • 5
  • 20
Dibzmania
  • 1,934
  • 1
  • 15
  • 32
0

You can either show a progress dialog till your query executes and make user wait on the screen and at the same time inform him that something is being processed.

Or if you don't want to inform user about processing in that case, check if activity is sill in foreground only then show the dialog.

You can use one boolean variable isInForeground and set/reset it in onResume and onPause respectively.

 @Override
    protected void onPause() {
        super.onPause();
       isInForeground=false;
    }

    @Override
    protected void onResume() {
        super.onResume();
       isInForeground=true;
    }

Then wrap dialog show logic in if condition

if(isInForeground){
  dialog.show(getFragmentManager(), TAG);
}
Nayan Srivastava
  • 3,655
  • 3
  • 27
  • 49
0

The more I looked at it the more it bothered me that I had a query in UI code. This was likely my initial code to test the concept and I carelessly left it when it worked.

There's a LoaderManager for the app using the wrong query, but I used the existing onLoadFinished to fire an async query that will update the path structure on any change to the db. Now when the user clicks the dialog is ready to go.

Since I can't reproduce the error it will be a week or so before I can make a verdict on this one, but either way the prior code was sloppy.

Anthony
  • 7,638
  • 3
  • 38
  • 71
0

It seems your activity is paused/your app is not in the foregraound. You can do one thing before adding a fragment check whether your activity is not destroyed/finishing and your application is in foreground(It is not minimized)

if(!isDestroyed()&&!isFinishing()&&isAppInForground(this)){
//add your fragment
}

Write code to check whether your app is in foreground or not

 /**
         *
         * @param context
         * @return boolean
         * tells whether user is currently viewing app or not if not return false otherwise true
         */
        public static boolean isAppInForground(Context context){
            boolean isInForeground = false;
            ActivityManager am = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);

                List<ActivityManager.RunningAppProcessInfo> runningProcesses = am.getRunningAppProcesses();
                for (ActivityManager.RunningAppProcessInfo processInfo : runningProcesses) {
                    if (processInfo.importance == ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND) {
                        for (String activeProcess : processInfo.pkgList) {
                            if (activeProcess.equals(context.getPackageName())) {
                                isInForeground = true;
                            }
                        }
                    }
                }

            return isInForeground;
        }
Mansuu....
  • 1,206
  • 14
  • 27