• java.lang.IllegalStateException: Can not perform this action after onSaveInstanceState


    1. 场景:

      在开发过程中遇到这么一个需要,在主页点击按钮进入另一个Activity(ReadActivity),在该ReadActivity中点击一个按钮再返回主页并指定主页选中特定的Tab.主页是用FragmentTabHost + Fragment 实现。思路是通过startActivityForResult以及setResult() 以及requestCode作为标志位,是ReadActivity返回,因为还有其他的requestCode。再通过

    FragmentTabHost的setCurrentTab(int index)方法切换Tab,我把该方法放置在了startActivityForResult()中,出现该问题java.lang.IllegalStateException: Can not perform this action after onSaveInstanceState。

    2.为什么?

      查阅源码,进入到FragmentManager的源码找到这么一段解释:

    /**
         * Start a series of edit operations on the Fragments associated with
         * this FragmentManager.
         * 
         * <p>Note: A fragment transaction can only be created/committed prior
         * to an activity saving its state.  If you try to commit a transaction
         * after {@link FragmentActivity#onSaveInstanceState FragmentActivity.onSaveInstanceState()}
         * (and prior to a following {@link FragmentActivity#onStart FragmentActivity.onStart}
         * or {@link FragmentActivity#onResume FragmentActivity.onResume()}, you will get an error.
         * This is because the framework takes care of saving your current fragments
         * in the state, and if changes are made after the state is saved then they
         * will be lost.</p>
         */
        public abstract FragmentTransaction beginTransaction();

    大意是:一个fragment事务只能在该依附的Activity正在保存状态时创建(created)或者提交(committed ),如果在调用onSaveInstanceState()之后再提交事务,就会导致问题发生—— Can not perform this action after onSaveInstanceState 。

    3. 发生的时机:

      先看FragmentManager中这么一段代码(只展示相关代码):(static final boolean HONEYCOMB = android.os.Build.VERSION.SDK_INT >= 11;

    Parcelable saveAllState() {
            // Make sure all pending operations have now been executed to get
            // our state update-to-date.
            execPendingActions();
    
            if (HONEYCOMB) {
                // As of Honeycomb, we save state after pausing.  Prior to that
                // it is before pausing.  With fragments this is an issue, since
                // there are many things you may do after pausing but before
                // stopping that change the fragment state.  For those older
                // devices, we will not at this point say that we have saved
                // the state, so we will allow them to continue doing fragment
                // transactions.  This retains the same semantics as Honeycomb,
                // though you do have the risk of losing the very most recent state
                // if the process is killed...  we'll live with that.
                mStateSaved = true;
            }
    }

    只看if中的注释:HONEYCOMB -> 大于等于Android3.0 版本的系统,Activity在不可见状态(pausing)后保存其状态,置mStateSaved状态为true。对于3.0更早的设备,则允许在不可见状态后执行fragment事务,所以保存状态就是不确定的了,但是肯定会在stop之前保存状态。

      mStateSaved(布尔值)标志是否保存状态:

    private void checkStateLoss() {
            if (mStateSaved) {
                throw new IllegalStateException(
                        "Can not perform this action after onSaveInstanceState");
            }
            if (mNoTransactionsBecause != null) {
                throw new IllegalStateException(
                        "Can not perform this action inside of " + mNoTransactionsBecause);
            }
        }
    static final boolean HONEYCOMB = android.os.Build.VERSION.SDK_INT >= 11;

    4. 思路整理:

      
    从主页跳转到ReadActivity,MainActivity(首页)已不可见并且保存其状态,此时已经不能再对fragment执行commit操作。而FragmentTabHost的setCurrentTab(int index)中
    调用了commit方法,导致问题出现。setCurrentTab() -> invokeOnTabChangeListener() -> onTabChanged() -> ft.commit(); 一目了然!!!
     1 public void setCurrentTab(int index) {
     2         if (index < 0 || index >= mTabSpecs.size()) {
     3             return;
     4         }
     5 
     6         if (index == mCurrentTab) {
     7             return;
     8         }
     9 
    10         // notify old tab content
    11         if (mCurrentTab != -1) {
    12             mTabSpecs.get(mCurrentTab).mContentStrategy.tabClosed();
    13         }
    14 
    15         mCurrentTab = index;
    16         final TabHost.TabSpec spec = mTabSpecs.get(index);
    17 
    18         // Call the tab widget's focusCurrentTab(), instead of just
    19         // selecting the tab.
    20         mTabWidget.focusCurrentTab(mCurrentTab);
    21 
    22         // tab content
    23         mCurrentView = spec.mContentStrategy.getContentView();
    24 
    25         if (mCurrentView.getParent() == null) {
    26             mTabContent
    27                     .addView(
    28                             mCurrentView,
    29                             new ViewGroup.LayoutParams(
    30                                     ViewGroup.LayoutParams.MATCH_PARENT,
    31                                     ViewGroup.LayoutParams.MATCH_PARENT));
    32         }
    33 
    34         if (!mTabWidget.hasFocus()) {
    35             // if the tab widget didn't take focus (likely because we're in touch mode)
    36             // give the current tab content view a shot
    37             mCurrentView.requestFocus();
    38         }
    39 
    40         //mTabContent.requestFocus(View.FOCUS_FORWARD);
    41         invokeOnTabChangeListener();
    42     }
    43 
    44     /**
    45      * Register a callback to be invoked when the selected state of any of the items
    46      * in this list changes
    47      * @param l
    48      * The callback that will run
    49      */
    50     public void setOnTabChangedListener(OnTabChangeListener l) {
    51         mOnTabChangeListener = l;
    52     }
    53 
    54     private void invokeOnTabChangeListener() {
    55         if (mOnTabChangeListener != null) {
    56             mOnTabChangeListener.onTabChanged(getCurrentTabTag());
    57         }
    58     }
    59 
    60 @Override
    61     public void onTabChanged(String tabId) {
    62         if (mAttached) {
    63             FragmentTransaction ft = doTabChanged(tabId, null);
    64             if (ft != null) {
    65                 ft.commit();
    66             }
    67         }
    68         if (mOnTabChangeListener != null) {
    69             mOnTabChangeListener.onTabChanged(tabId);
    70         }
    71     }
    
    

    5. 解决:

      知道问题所在这就好说了,只要在Activity未保存状态之前执行setCurrentTab()方法,其关键就是执行commit方法。要么在当前未保存状态之前,要么在Activity再次可见并未onPause之前都是不会出问题的,哦了。

     
  • 相关阅读:
    Native RabbitMQ Direct Exchange
    RabbitMQ系列文章导读
    AbstractQueuedSynchronizer
    CountDownLatch和CyclicBarrier
    显示锁Lock
    《SeleniumBasic 3.141.0.0
    《SeleniumBasic 3.141.0.0
    《SeleniumBasic 3.141.0.0
    《SeleniumBasic 3.141.0.0
    《SeleniumBasic 3.141.0.0
  • 原文地址:https://www.cnblogs.com/aimqqroad-13/p/7424053.html
Copyright © 2020-2023  润新知