【问题标题】:IllegalStateException when .replace fragment on restart.replace 片段重启时出现 IllegalStateException
【发布时间】:2016-11-29 19:03:59
【问题描述】:

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

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

另一个 StackOverflow 问题说要覆盖 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: 无法启动 活动组件信息{myapp/myap.MainActivity}:
android.view.InflateException:二进制 XML 文件第 25 行:错误 膨胀类片段 myapp.MainActivity.onResumeFragments(MainActivity.java:450)
引起:java.lang.IllegalStateException: Can not perform this action after onSaveInstanceState at > android.support.v4.app.FragmentManagerImpl.checkStateLoss(FragmentManager.java:1533) 在 myapp.fragments.FragmentChange.onFragmentChange(FragmentChange.java:128) 在 myapp.MainActivity.onNavigationDrawerItemSelected(MainActivity.java:490) 在 myapp.fragments.NavigationDrawerFragment.selectItem(NavigationDrawerFragment.java:197) 在 myapp.fragments.NavigationDrawerFragment.onCreate(NavigationDrawerFragment.java:78) 在 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();

主活动:

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
        ...
    }
}

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

【问题讨论】:

  • 我不是要贬低你的问题,但你的问题已经被问过无数次了;授予我没有查看您的代码(感谢您发布它,顺便说一句),但我相信您会从阅读更多有关 FragmentManager、Fragments 和 Lifecycle 工作原理的信息中受益。这让人头疼,很奇怪,它有这么多的错误,以至于谷歌在谷歌 I/O 期间专门做了一个完整的会议(称为什么片段?)(我建议你看一下,这很有趣)。然后重新访问您的代码以减少崩溃的可能性。 :)
  • 感谢您的回复。我知道以前关于 IllegalStateException 的问题。我已经尝试了这些建议,但还没有任何效果。我现在已经看了视频,但我比以前不知道了。
  • 很抱歉您没有找到相关的视频或有帮助的视频。场景总是相似的:当你旋转一个活动时,它会被销毁。在此发生之前,必须调用 onSaveInstance(以便稍后重新创建它)。片段是这些奇怪的东西,它们以某种方式有自己的生命周期,有点与活动相关,但并非总是如此,你试图对没有活动的片段进行操作(被破坏/被重新创建),或者(如果你覆盖某些方法)你'在保存状态后尝试对片段进行操作。我有一个问题要问你,但下一个评论,因为它不适合
  • 所以让我们看看,一旦用户点击导航抽屉,您想要替换片段,或者您想要在发生这种情况时更改现有片段的布局。您的片段是否有一个公共的空无参数构造函数? (需要)

标签: android android-fragments illegalstateexception


【解决方案1】:

一些 cmets
我可以看到你已经“超越”尝试修复它(并且已经扫描了 stackoverflow 以获得许多提示)。
您的代码第一次运行(即使您是在横向 b4 启动应用程序中)。
旋转无法使片段膨胀(第二次)(在activity_main 中),并且还报告“保存后提交”错误(这很奇怪,我们到底怎么会遇到 2 个致命错误?可能在另一个线程上,或者“ “保存”正在杀死充气机,但它怎么能继续?)。
NavigationDrawerFragment 中有很多重要的代码(通常在 Activity 中)。
分析:
你的MainActivity 班级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);

继承: 活动 ' 您需要特别考虑 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

旋转后的风景

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

getSupportFragmentManager().executePendingTransactions();

这里是轮换发布代码的错误日志的链接: link error log

【讨论】:

    【解决方案2】:

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

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

    方法二。 一个。首先不要错过onConfigurationChanged() 中的setContentView() 等默认位置的位置代码。
    乙。用标签或 xml 中的 id 替换片段
    c。如果通过 id 或 tag 找到片段,则不要再次替换。
    d。确保您正在关闭片段 onPause 中的任何 dailog。

    【讨论】:

      【解决方案3】:

      FragmentManager 是一个

      用于与Activity 内部的 Fragment 对象交互的接口

      将它保存在静态字段中并为新活动重用旧 FragmentManager 让我觉得特别糟糕。当新 Activity 与旧 Activity 的管理器交互时,这必然会导致 Activity 已被销毁

      在您的代码中,替换

      FragmentChange.getInstance(getFragmentManager());
      

      通过

      new FragmentChange(getFragmentManager());
      

      【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2016-11-23
      • 1970-01-01
      • 1970-01-01
      • 2018-05-03
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多