IllegalStateException when .replace fragment on restart

IllegalStateException when .replace fragment on restart

我正在使用 FragmentTransaction.replace() 来交换片段。 该应用程序首次启动没有问题。 由于 savedInstanceState 之间的冲突,旋转设备时抛出 IllegalStateException 并提交一个新的片段事务。 不涉及 AsyncTask。

一个 Whosebug 问题建议将 setContentView() 调用 在 onResumeFragments() 中,但这似乎没有效果。与 onPostResume().

相同

另一个 Whosebug 问题说要覆盖 onConfigurationChanged()。从这个意义上说,这是有效的,因为它不会发生异常 因为 Activity 没有重新启动。但是,这可以防止具有不同肖像的片段 和横向布局在这些布局之间切换。在 onConfigurationChanged() 中调用 setContentView() 导致类似的错误(IllegalArgumentException: Binary XML file line #25: Duplicate id 0x12345678, tag null, or parent id with another fragment)

使用 fragmentTransaction.commitAllowingStateLoss() 而不是 .commit() 会导致 IllegalStateException:Activity 已被销毁

我如何让它工作?

更多异常信息:

java.lang.RuntimeException: Unable to start activity ComponentInfo{myapp/myap.MainActivity}:
android.view.InflateException: Binary XML file line #25: Error inflating class fragment at myapp.MainActivity.onResumeFragments(MainActivity.java:450)
Caused by: java.lang.IllegalStateException: Can not perform this action after onSaveInstanceState at > android.support.v4.app.FragmentManagerImpl.checkStateLoss(FragmentManager.java:1533) at myapp.fragments.FragmentChange.onFragmentChange(FragmentChange.java:128) at myapp.MainActivity.onNavigationDrawerItemSelected(MainActivity.java:490) at myapp.fragments.NavigationDrawerFragment.selectItem(NavigationDrawerFragment.java:197) at myapp.fragments.NavigationDrawerFragment.onCreate(NavigationDrawerFragment.java:78) at myapp.MainActivity.onResumeFragments(MainActivity.java:450)

旋转设备时代码中的顺序是:

MainActivity.onPause()  
MainActivity.saveInstanceState()  
NavigationDrawerFragment.onSaveInstanceState()  
MainActivity.onStop()
MainActivity.onDestroy()
MainActivity.onCreate()
    super.onCreate(savedInstanceState);
MainActivity.onResumeFragments()
    setContentView()
NavigationDrawerFragment.onCreate()
MainActivity.onNavigationDrawerItemSelected()
     fragmentTransaction.commit();

主要Activity:

public class MainActivity extends AppCompatActivity implements
        NavigationDrawerFragment.NavigationDrawerCallbacks {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        ...
    }


    @Override
    public void onNavigationDrawerItemSelected(int position) {
        ...
        FragmentChangeEvent fragmentChangeEvent = new FragmentChangeEvent(null);
        FragmentChange fragmentChange = FragmentChange.getInstance( getSupportFragmentManager());
        fragmentChange.onFragmentChange(fragmentChangeEvent);
        ...
    }

    @Override
    public void onSaveInstanceState(Bundle outState) {
        outState.putString("WORKAROUND_FOR_BUG_19917_KEY", "WORKAROUND_FOR_BUG_19917_VALUE");
        super.onSaveInstanceState(outState);
    }

    @Override
    public void onResumeFragments() {
        super.onResumeFragments(); 

        // causes onNavigationDrawerItemSelected() to be called, exception thrown 
        setContentView(myapp.R.layout.activity_main);

        mNavigationDrawerFragment = (NavigationDrawerFragment)
                getSupportFragmentManager().findFragmentById(myapp.R.id.navigation_drawer);

        mNavigationDrawerFragment.setUp( // Set up the drawer
                myapp.R.id.navigation_drawer,
                (DrawerLayout) findViewById(myapp.R.id.drawer_layout));
    }

    @Override
    public void onConfigurationChanged(Configuration newConfig) {
        super.onConfigurationChanged(newConfig);

        if current fragment is "Individual" { // pseudocode
            setContentView(R.layout.activity_main); // causes IllegalArgumentException
        }
    }
}

NavigationDrawerFragment

public class NavigationDrawerFragment extends Fragment {

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

         SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(getActivity());
        mUserLearnedDrawer = sp.getBoolean(PREF_USER_LEARNED_DRAWER, false);

        if (savedInstanceState != null) {
            mCurrentSelectedPosition = savedInstanceState.getInt(STATE_SELECTED_POSITION);
            mFromSavedInstanceState = true;
        }

        // Select either the default item (0) or the last selected item.
        selectItem(mCurrentSelectedPosition);
    }

    private void selectItem(int position) {

        mCurrentSelectedPosition = position;
        if (mDrawerListView != null) {
            mDrawerListView.setItemChecked(position, true);
        }
        if (mDrawerLayout != null) {
            mDrawerLayout.closeDrawer(mFragmentContainerView);
        }
        if (mCallbacks != null) {
            // calls MainActivity.onNavigationDrawerItemSelected() 
            mCallbacks.onNavigationDrawerItemSelected(position);
        }
    }

    @Override
    public void onSaveInstanceState(Bundle outState) {
        super.onSaveInstanceState(outState);
        outState.putInt(STATE_SELECTED_POSITION, mCurrentSelectedPosition);
    }

}

片段变化

public class FragmentChange implements FragmentChangeListener {

      public static FragmentChange getInstance(FragmentManager fragmentManager) {
        if (instance == null) {
            instance = new FragmentChange(fragmentManager);
        }
        return instance;
    }

    // constructor
    private FragmentChange(FragmentManager fragmentManager) {
        mFragmentManager = fragmentManager;
    }

    @Override
    public void onFragmentChange(FragmentChangeEvent fragmentChangeEvent) {
        ...
        mPosition = fragmentChangeEvent.getPosition();

        FragmentTransaction fragmentTransaction = mFragmentManager.beginTransaction();
        Fragment fragment = EmployeesVerticalFragment.newInstance();
        fragmentTransaction.replace(myapp.R.id.container, fragment);

        fragmentTransaction.commit(); // IllegalState exception here
        ...
    }
}

大大减少了重现 IllegalStateException 的 form of the project on github

一些评论
我可以看到你已经"above and beyond" 尝试修复它(并扫描了 Whosebug 以获取许多提示)。
您的代码第一次工作(即使您在横向 b4 启动应用程序)。
旋转未能使片段膨胀(第二次)(在 activity_main 中),并且还报告了 "commit after save" 错误(这很奇怪,我们到底是怎么得到 2 个致命错误的?也许在另一个线程上,或“"save" 正在杀死充气机,但它如何继续运行?”。
NavigationDrawerFragment 中有很多重要代码(通常在 Activity 中)。
分析:
你的 MainActivity class extends AppCompatActivity:

public class MainActivity extends AppCompatActivity implements
        NavigationDrawerFragment.NavigationDrawerCallbacks {

通常你扩展 FragmentActivity:

public class MainActivity extends FragmentActivity implements
NavigationDrawerFragment.NavigationDrawerCallbacks {

这是因为您膨胀了 activity_main,其中包含一个 fragment

setContentView(com.example.replacefragments.R.layout.activity_main);

继承: Activity <- 片段Activity <- AppCompatActivity <- ActionBarActivity
'<-'表示这里继承。
您需要特别考虑 FragmentActivity 的一个原因是,如果您想使用嵌套片段(一个片段包含另一个片段),因为在 API 级别 17 之前,原生片段不支持它。
我想这就是你在这里所做的(在 activity_main):

<fragment android:id="@+id/navigation_drawer

这是一个不错的模板应用程序,应该会让您的生活更轻松:
android-sliding-menu-using-navigation-drawer/
修复它:
activity_main.xml 中的片段替换为 ListView.
我已经丢弃了NavigationDrawerFragment,并从上面的模板中注入了一些代码,然后从NavigationDrawerFragment中一点一点地重新引入你的代码,直到它倒下。 它现在可以旋转,但不是你或我想要的(保留片段状态,NavigationDrawerFragment 一些功能),所以我仍在努力。

protrait

landscape after rotation

热点提示: .commit 听起来是最终的,不是吗?听起来好像应该同步?两者都不是真的。
.commit 将事务放入待处理队列中,要真正执行它,您需要:

getSupportFragmentManager().executePendingTransactions();

这里有一个 link 轮换发布代码的错误日志: link error log

方法 1. 避免更改配置。在清单中添加 android:configChanges="orientation|screenSize|screenLayout|keyboardHidden|keyboard" activity.

<activity
            android:name=".ui.accounts.YouActivity"
          android:configChanges="orientation|screenSize|screenLayout|keyboardHidden|keyboard"
            android:theme="@style/AppTheme.NoActionBar" />

方法二。 一种。首先不要错过默认位置的位置代码,例如 onConfigurationChanged() 中的 setContentView()
b。用标签或 xml 中的 id 替换片段
c。如果通过 id 或标签找到片段,请不要再次替换。
d.确保您正在关闭片段 onPause.

内的任何 dailog

FragmentManager 是一个

Interface for interacting with Fragment objects inside of an Activity.

我觉得将其保存在静态字段中并为新的 activity 重用旧的 FragmentManager 是一个特别糟糕的主意。这必然会导致Activity被销毁,当新的activity与旧的activity的管理器进行交互时

在您的代码中,替换

FragmentChange.getInstance(getFragmentManager());

来自

new FragmentChange(getFragmentManager());