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());
我正在使用 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
.
FragmentManager 是一个
Interface for interacting with Fragment objects inside of an Activity.
我觉得将其保存在静态字段中并为新的 activity 重用旧的 FragmentManager 是一个特别糟糕的主意。这必然会导致Activity被销毁,当新的activity与旧的activity的管理器进行交互时
在您的代码中,替换
FragmentChange.getInstance(getFragmentManager());
来自
new FragmentChange(getFragmentManager());