美文网首页
java.lang.IllegalStateException:

java.lang.IllegalStateException:

作者: CZKGO | 来源:发表于2018-01-17 00:27 被阅读0次

    问题:

    java.lang.IllegalStateException: Can not perform this action after onSaveInstanceState
              at android.app.FragmentManagerImpl.checkStateLoss(FragmentManager.java:1434)
              at android.app.FragmentManagerImpl.enqueueAction(FragmentManager.java:1452)
              at android.app.BackStackRecord.commitInternal(BackStackRecord.java:707)
              at android.app.BackStackRecord.commit(BackStackRecord.java:671)
              at android.app.DialogFragment.show(DialogFragment.java:230)
              at my.test.app.TimerActivity.e(Unknown Source)
              at my.test.app.TimerActivity.onTimerFinsh(Unknown Source)
              at my.test.app.view.TimerView.dispatchDraw(Unknown Source)
              at android.view.View.updateDisplayListIfDirty(View.java:16052)
              at android.view.ViewGroup.recreateChildDisplayList(ViewGroup.java:3748)
              at android.view.ViewGroup.dispatchGetDisplayList(ViewGroup.java:3728)
              at android.view.View.updateDisplayListIfDirty(View.java:16020)
              at android.view.ViewGroup.recreateChildDisplayList(ViewGroup.java:3748)
              at android.view.ViewGroup.dispatchGetDisplayList(ViewGroup.java:3728)
              at android.view.View.updateDisplayListIfDirty(View.java:16020)
              at android.view.ViewGroup.recreateChildDisplayList(ViewGroup.java:3748)
              at android.view.ViewGroup.dispatchGetDisplayList(ViewGroup.java:3728)
              at android.view.View.updateDisplayListIfDirty(View.java:16020)
              at android.view.ViewGroup.recreateChildDisplayList(ViewGroup.java:3748)
              at android.view.ViewGroup.dispatchGetDisplayList(ViewGroup.java:3728)
              at android.view.View.updateDisplayListIfDirty(View.java:16020)
              at android.view.ThreadedRenderer.updateViewTreeDisplayList(ThreadedRenderer.java:656)
              at android.view.ThreadedRenderer.updateRootDisplayList(ThreadedRenderer.java:662)
              at android.view.ThreadedRenderer.draw(ThreadedRenderer.java:770)
              at android.view.ViewRootImpl.draw(ViewRootImpl.java:2791)
              at android.view.ViewRootImpl.performDraw(ViewRootImpl.java:2599)
              at android.view.ViewRootImpl.performTraversals(ViewRootImpl.java:2198)
              at android.view.ViewRootImpl.doTraversal(ViewRootImpl.java:1246)
              at android.view.ViewRootImpl$TraversalRunnable.run(ViewRootImpl.java:6302)
              at android.view.Choreographer$CallbackRecord.run(Choreographer.java:871)
              at android.view.Choreographer.doCallbacks(Choreographer.java:683)
              at android.view.Choreographer.doFrame(Choreographer.java:619)
              at android.view.Choreographer$FrameDisplayEventReceiver.run(Choreographer.java:857)
              at android.os.Handler.handleCallback(Handler.java:751)
              at android.os.Handler.dispatchMessage(Handler.java:95)
              at android.os.Looper.loop(Looper.java:159)
              at android.app.ActivityThread.main(ActivityThread.java:6097)
              at java.lang.reflect.Method.invoke(Native Method)
              at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:865)
              at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:755)
    

    原因

      在使用DialogFragment,可能会出现这个bug。
      从堆栈信息可以看到,错误来自FragmentManagerImpl 类的checkStateLoss()方法。该方法如下:

     private void checkStateLoss() {
            if(mStateSaved)
                throw new IllegalStateException("Can not perform this action after onSaveInstanceState");
            if(mNoTransactionsBecause != null)
                throw new IllegalStateException((new StringBuilder()).append("Can not perform this action inside of ").append(mNoTransactionsBecause).toString());
            else
                return;
    }
    

      从代码可以看出,当mStateSaved对象为true时,就会抛出该异常。在FragmentManagerImpl 类中查找mStateSaved,发现只有在saveAllState()中该值才会被置为false。继续查找saveAllState()方法,我在Activity的onSaveInstanceState(Bundle bundle)方法找到了其调用。

    protected void onSaveInstanceState(Bundle bundle) {
            bundle.putBundle("android:viewHierarchyState", mWindow.saveHierarchyState());
            android.os.Parcelable parcelable = mFragments.saveAllState();
            if(parcelable != null)
                bundle.putParcelable("android:fragments", parcelable);
            getApplication().dispatchActivitySaveInstanceState(this, bundle);
    }
    

      当找到了问题的来源后,就可以开始去解决了。

    解決方案

    方案一:重写onSaveInstanceState(Bundle outState)方法
      从上文得知只有调用了onSaveInstanceState(Bundle outState)方法,才会抛出该异常,所以只要我们在acitivity中重写该方法,然后注释或删掉super.onSaveInstanceState(outState)方法就行。

        @Override
        protected void onSaveInstanceState(Bundle outState) {
            //super.onSaveInstanceState(outState);
        }
    

    方案二:反射调用showAllowingStateLoss(FragmentManager manager, String tag)方法
      从上文的堆栈信息可以看到在调用checkStateLoss()方法之前是先调用了enqueueAction(Runnable runnable, boolean flag)方法,该方法中有以下代码:

     public void enqueueAction(Runnable runnable, boolean flag) {
            if(!flag)
                checkStateLoss();
            ......
        }
    

      由此可以看出,只要传入的flag是true,该方法便不会被调用。继续从堆栈信息向上跟踪到类BackStackRecord,其有如下两个方法:

      public int commit() {
            return commitInternal(false);
      }
      public int commitAllowingStateLoss() {
            return commitInternal(true);
      }
    

      再向上跟踪可以看到DialogFragment类中的showAllowingStateLoss(FragmentManager manager, String tag)方法,该方法被@hide标记,我们可反射调用它来替换方法,最终解决代码如下:

            try {
                Class aClass = Class.forName("android.app.DialogFragment");
                Class[] argsClass = new Class[2];
                argsClass[0] = FragmentManager.class;
                argsClass[1] = String.class;
    
                Object[] params = new Object[2];
                params[0] = getFragmentManager();
                params[1] = "MyDialog";
                Method method = aClass.getMethod("showAllowingStateLoss", argsClass);
                method.invoke(dialog,params);
            } catch (ClassNotFoundException e) {
                e.printStackTrace();
            } catch (NoSuchMethodException e) {
                e.printStackTrace();
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            } catch (InvocationTargetException e) {
                e.printStackTrace();
            }
    

    方案三:越过showAllowingStateLoss(FragmentManager manager, String tag)方法直接调用FragmentTransaction类的commitAllowingStateLoss()方法
      从方案二的分析可知,调用了DialogFragment类中的showAllowingStateLoss(FragmentManager manager, String tag)方法可以避免该问题,而该方法代码如下:

        /** {@hide} */
        public void showAllowingStateLoss(FragmentManager manager, String tag) {
            mDismissed = false;
            mShownByMe = true;
            FragmentTransaction ft = manager.beginTransaction();
            ft.add(this, tag);
            ft.commitAllowingStateLoss();
        }
    

      根据该代码内容,我们可以直接在展示DialogFragment时做出如下调用:

    FragmentTransaction ft = getFragmentManager().beginTransaction();
    ft.add(dialog, "MyDialog");
    ft.commitAllowingStateLoss();
    

    相关文章

      网友评论

          本文标题:java.lang.IllegalStateException:

          本文链接:https://www.haomeiwen.com/subject/rdleoxtx.html