nha*_*man 472 android illegalstateexception android-fragments android-viewpager fragmenttransaction
我从我的应用程序中获取用户报告,提供以下异常:
java.lang.IllegalStateException: Can not perform this action after onSaveInstanceState
at android.app.FragmentManagerImpl.checkStateLoss(FragmentManager.java:1109)
at android.app.FragmentManagerImpl.popBackStackImmediate(FragmentManager.java:399)
at android.app.Activity.onBackPressed(Activity.java:2066)
at android.app.Activity.onKeyUp(Activity.java:2044)
at android.view.KeyEvent.dispatch(KeyEvent.java:2529)
at android.app.Activity.dispatchKeyEvent(Activity.java:2274)
at com.android.internal.policy.impl.PhoneWindow$DecorView.dispatchKeyEvent(PhoneWindow.java:1803)
at android.view.ViewGroup.dispatchKeyEvent(ViewGroup.java:1112)
at android.view.ViewGroup.dispatchKeyEvent(ViewGroup.java:1112)
at android.view.ViewGroup.dispatchKeyEvent(ViewGroup.java:1112)
at com.android.internal.policy.impl.PhoneWindow$DecorView.superDispatchKeyEvent(PhoneWindow.java:1855)
at com.android.internal.policy.impl.PhoneWindow.superDispatchKeyEvent(PhoneWindow.java:1277)
at android.app.Activity.dispatchKeyEvent(Activity.java:2269)
at com.android.internal.policy.impl.PhoneWindow$DecorView.dispatchKeyEvent(PhoneWindow.java:1803)
at android.view.ViewGroup.dispatchKeyEvent(ViewGroup.java:1112)
at android.view.ViewGroup.dispatchKeyEvent(ViewGroup.java:1112)
at android.view.ViewGroup.dispatchKeyEvent(ViewGroup.java:1112)
at android.view.ViewGroup.dispatchKeyEvent(ViewGroup.java:1112)
at android.widget.TabHost.dispatchKeyEvent(TabHost.java:297)
at android.view.ViewGroup.dispatchKeyEvent(ViewGroup.java:1112)
at android.view.ViewGroup.dispatchKeyEvent(ViewGroup.java:1112)
at android.view.ViewGroup.dispatchKeyEvent(ViewGroup.java:1112)
at com.android.internal.policy.impl.PhoneWindow$DecorView.superDispatchKeyEvent(PhoneWindow.java:1855)
at com.android.internal.policy.impl.PhoneWindow.superDispatchKeyEvent(PhoneWindow.java:1277)
at android.app.Activity.dispatchKeyEvent(Activity.java:2269)
at com.android.internal.policy.impl.PhoneWindow$DecorView.dispatchKeyEvent(PhoneWindow.java:1803)
at android.view.ViewRoot.deliverKeyEventPostIme(ViewRoot.java:2880)
at android.view.ViewRoot.handleFinishedEvent(ViewRoot.java:2853)
at android.view.ViewRoot.handleMessage(ViewRoot.java:2028)
at android.os.Handler.dispatchMessage(Handler.java:99)
at android.os.Looper.loop(Looper.java:132)
at android.app.ActivityThread.main(ActivityThread.java:4028)
at java.lang.reflect.Method.invokeNative(Native Method)
at java.lang.reflect.Method.invoke(Method.java:491)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:844)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:602)
at dalvik.system.NativeStart.main(Native Method)
Run Code Online (Sandbox Code Playgroud)
显然它与FragmentManager有关,我不使用它.stacktrace没有显示我自己的任何类,所以我不知道这个异常发生在哪里以及如何防止它.
对于记录:我有一个tabhost,并且在每个选项卡中都有一个ActivityGroup在Activities之间切换.
Ovi*_*tcu 705
请在这里查看我的答案.基本上我只需要:
@Override
protected void onSaveInstanceState(Bundle outState) {
//No call for super(). Bug on API Level > 11.
}
Run Code Online (Sandbox Code Playgroud)
不要super()
对saveInstanceState
方法进行调用.这搞砸了......
这是支持包中的已知错误.
如果您需要保存实例并向您添加内容,outState
Bundle
可以使用以下内容:
@Override
protected void onSaveInstanceState(Bundle outState) {
outState.putString("WORKAROUND_FOR_BUG_19917_KEY", "WORKAROUND_FOR_BUG_19917_VALUE");
super.onSaveInstanceState(outState);
}
Run Code Online (Sandbox Code Playgroud)
最后,正确的解决方案(如评论中所示)使用:
transaction.commitAllowingStateLoss();
Run Code Online (Sandbox Code Playgroud)
添加或执行FragmentTransaction
导致的时间Exception
.
Syn*_*sso 120
类似的错误消息存在许多相关问题.检查此特定堆栈跟踪的第二行.此异常与调用特别相关FragmentManagerImpl.popBackStackImmediate
.
如果已保存会话状态popBackStack
,则此方法调用将始终失败IllegalStateException
.检查来源.没有什么可以阻止抛出此异常.
super.onSaveInstanceState
无济于事.commitAllowingStateLoss
无济于事.以下是我观察问题的方法:
onSaveInstanceState
被调用.popBackStackImmediate
尝试.IllegalStateException
被扔了.这是我为解决它所做的事情:
由于无法避免IllegalStateException
回调,因此请抓住并忽略它.
try {
activity.getSupportFragmentManager().popBackStackImmediate(name);
} catch (IllegalStateException ignored) {
// There's no way to avoid getting this if saveInstanceState has already been called.
}
Run Code Online (Sandbox Code Playgroud)
这足以阻止应用程序崩溃.但现在用户将恢复应用程序并看到他们认为按下的按钮根本没有被按下(他们认为).表单片段仍在显示!
要解决此问题,请在创建对话框时,进行一些状态以指示进程已启动.
progressDialog.show(fragmentManager, TAG);
submitPressed = true;
Run Code Online (Sandbox Code Playgroud)
并将此状态保存在捆绑中.
@Override
public void onSaveInstanceState(Bundle outState) {
...
outState.putBoolean(SUBMIT_PRESSED, submitPressed);
}
Run Code Online (Sandbox Code Playgroud)
别忘了再把它装回去 onViewCreated
然后,在恢复时,如果先前尝试过提交,则回滚片段.这可以防止用户回到看似未提交的表单.
@Override
public void onResume() {
super.onResume();
if (submitPressed) {
// no need to try-catch this, because we are not in a callback
activity.getSupportFragmentManager().popBackStackImmediate(name);
submitPressed = false;
}
}
Run Code Online (Sandbox Code Playgroud)
Nas*_*kov 55
isFinishing()
在显示片段之前检查活动是否正常并注意commitAllowingStateLoss()
.
例:
if(!isFinishing()) {
FragmentManager fm = getSupportFragmentManager();
FragmentTransaction ft = fm.beginTransaction();
DummyFragment dummyFragment = DummyFragment.newInstance();
ft.add(R.id.dummy_fragment_layout, dummyFragment);
ft.commitAllowingStateLoss();
}
Run Code Online (Sandbox Code Playgroud)
Ant*_*eef 23
这是2017年10月,谷歌使用新的东西调用生命周期组件制作Android支持库.它为此问题提供了一些新的想法'在onSaveInstanceState之后无法执行此操作'问题.
简而言之:
更长版本说明:
为什么这个问题出来了?
这是因为你试图FragmentManager
从你的活动中使用(我想假装你的片段?)为你的片段提交一个事务.通常这看起来就像你正在尝试为即将到来的片段做一些事务,同时主机活动已经调用savedInstanceState
方法(用户可能碰巧触摸主页按钮,所以活动调用onStop()
,在我的情况下,这是原因)
通常这个问题不应该发生 - 我们总是尝试在最开始时将片段加载到活动中,就像onCreate()
方法是一个完美的地方.但有时会发生这种情况,尤其是当您无法确定要加载到该活动的片段时,或者您正在尝试从AsyncTask
块中加载片段(或者任何事情需要一些时间).在片段事务真正发生之前的时间,但在活动的onCreate()
方法之后,用户可以做任何事情.如果用户按下主页按钮,触发活动的onSavedInstanceState()
方法,则会can not perform this action
发生崩溃.
如果有人想在这个问题看得更深,我建议他们看看这个博客帖子.它深入了解源代码层并对其进行了大量解释.此外,它给出了您不应该使用该commitAllowingStateLoss()
方法来解决此崩溃的原因(相信我它对您的代码没有任何好处)
如何解决这个问题?
我应该使用commitAllowingStateLoss()
方法来加载片段吗?不,你不应该 ;
我应该覆盖onSaveInstanceState
方法,忽略其中的super
方法吗?不,你不应该 ;
我应该使用神奇的isFinishing
内部活动,检查主机活动是否适合片段交易?是的,这看起来是正确的方法.
看看Lifecycle组件可以做什么.
基本上,Google在AppCompatActivity
类中进行了一些实现(以及您应该在项目中使用的其他几个基类),这使得更容易确定当前的生命周期状态.回顾一下我们的问题:为什么会出现这个问题?这是因为我们在错误的时间做某事.所以我们尽量不这样做,这个问题就会消失.
我为自己的项目编写了一些代码,这是我使用的LifeCycle
.我在Kotlin编码.
val hostActivity: AppCompatActivity? = null // the activity to host fragments. It's value should be properly initialized.
fun dispatchFragment(frag: Fragment) {
hostActivity?.let {
if(it.lifecyclecurrentState.isAtLeast(Lifecycle.State.RESUMED)){
showFragment(frag)
}
}
}
private fun showFragment(frag: Fragment) {
hostActivity?.let {
Transaction.begin(it, R.id.frag_container)
.show(frag)
.commit()
}
Run Code Online (Sandbox Code Playgroud)
正如我在上面所示.我将检查主机活动的生命周期状态.借助支持库中的Lifecycle组件,这可能更具体.代码lifecyclecurrentState.isAtLeast(Lifecycle.State.RESUMED)
意味着,如果当前状态至少onResume
,不迟于它?这确保我的方法不会在其他生命状态(如onStop
)中执行.
一切都完成了吗?
当然不是.我展示的代码告诉了一些防止应用程序崩溃的新方法.但是,如果它确实进入状态onStop
,那行代码将不会做任何事情,因此在屏幕上不显示任何内容.当用户返回应用程序时,他们将看到一个空屏幕,这是空主机活动,根本没有显示任何片段.这是糟糕的经历(是的,比崩溃好一点).
所以在这里我希望有更好的东西:如果生命状态晚于应用程序将不会崩溃onResume
,事务方法是生活状态意识; 此外,在用户回到我们的应用程序之后,活动将尝试继续完成该片段事务操作.
我在这个方法中添加了更多内容:
class FragmentDispatcher(_host: FragmentActivity) : LifecycleObserver {
private val hostActivity: FragmentActivity? = _host
private val lifeCycle: Lifecycle? = _host.lifecycle
private val profilePendingList = mutableListOf<BaseFragment>()
@OnLifecycleEvent(Lifecycle.Event.ON_RESUME)
fun resume() {
if (profilePendingList.isNotEmpty()) {
showFragment(profilePendingList.last())
}
}
fun dispatcherFragment(frag: BaseFragment) {
if (lifeCycle?.currentState?.isAtLeast(Lifecycle.State.RESUMED) == true) {
showFragment(frag)
} else {
profilePendingList.clear()
profilePendingList.add(frag)
}
}
private fun showFragment(frag: BaseFragment) {
hostActivity?.let {
Transaction.begin(it, R.id.frag_container)
.show(frag)
.commit()
}
}
}
Run Code Online (Sandbox Code Playgroud)
我在这个dispatcher
类中维护一个列表,以存储那些没有机会完成事务操作的片段.当用户从主屏幕返回并发现仍有片段等待启动时,它将转到注释resume()
下的方法@OnLifecycleEvent(Lifecycle.Event.ON_RESUME)
.现在我认为它应该像我预期的那样工作.
Jed*_*Jed 21
以下是此问题的不同解决方案.
使用私有成员变量,您可以将返回的数据设置为intent,然后可以在super.onResume()之后处理;
像这样:
private Intent mOnActivityResultIntent = null;
@Override
protected void onResume() {
super.onResume();
if(mOnActivityResultIntent != null){
... do things ...
mOnActivityResultIntent = null;
}
}
@Override
public void onActivityResult(int requestCode, int resultCode, Intent data){
if(data != null){
mOnActivityResultIntent = data;
}
}
Run Code Online (Sandbox Code Playgroud)
Vin*_*yak 19
短而有效的解决方案:
遵循简单步骤
脚步
步骤1:覆盖onSaveInstanceState
相应片段中的状态.并从中删除超级方法.
@Override
public void onSaveInstanceState( Bundle outState ) {
}
Run Code Online (Sandbox Code Playgroud)
第2步:使用
fragmentTransaction.commitAllowingStateLoss( );
而不是 fragmentTransaction.commit( );
片段操作.
sab*_*der 10
我找到了解决这类问题的肮脏方案.如果您仍然想要保留您ActivityGroups
的原因(我有时间限制的原因),您只需实施
public void onBackPressed() {}
Run Code Online (Sandbox Code Playgroud)
在你的Activity
,并back
在那里做一些代码.即使旧设备上没有这样的方法,这个方法也会被更新的方法调用.
不要使用commitAllowingStateLoss(),它只应用于UI状态可以在用户上意外更改的情况.
如果事务发生在parentFragment的ChildFragmentManager中,请使用 parentFragment.isResume() outside来检查.
if (parentFragment.isResume()) {
DummyFragment dummyFragment = DummyFragment.newInstance();
transaction = childFragmentManager.BeginTransaction();
trans.Replace(Resource.Id.fragmentContainer, startFragment);
}
Run Code Online (Sandbox Code Playgroud)
我有类似的问题,场景是这样的:
该的onCreate的方法活动是这样的:
mMainFragment = (SelectionFragment) getSupportFragmentManager()
.findFragmentByTag(MAIN_FRAGMENT_TAG);
if (mMainFragment == null) {
mMainFragment = new SelectionFragment();
mMainFragment.setListAdapter(new ArrayAdapter<String>(this,
R.layout.item_main_menu, getResources().getStringArray(
R.array.main_menu)));
mMainFragment.setOnSelectionChangedListener(this);
FragmentTransaction transaction = getSupportFragmentManager()
.beginTransaction();
transaction.add(R.id.content, mMainFragment, MAIN_FRAGMENT_TAG);
transaction.commit();
}
Run Code Online (Sandbox Code Playgroud)
抛出异常是因为when配置更改(设备已旋转),活动已创建,主片段从片段管理器的历史记录中检索,同时片段已经具有对已销毁活动的OLD引用
将实现更改为此解决了问题:
mMainFragment = (SelectionFragment) getSupportFragmentManager()
.findFragmentByTag(MAIN_FRAGMENT_TAG);
if (mMainFragment == null) {
mMainFragment = new SelectionFragment();
mMainFragment.setListAdapter(new ArrayAdapter<String>(this,
R.layout.item_main_menu, getResources().getStringArray(
R.array.main_menu)));
FragmentTransaction transaction = getSupportFragmentManager()
.beginTransaction();
transaction.add(R.id.content, mMainFragment, MAIN_FRAGMENT_TAG);
transaction.commit();
}
mMainFragment.setOnSelectionChangedListener(this);
Run Code Online (Sandbox Code Playgroud)
您需要在每次创建活动时设置侦听器,以避免碎片引用旧的已销毁活动实例的情况.
如果您继承自FragmentActivity
,则必须在中调用超类onActivityResult()
:
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent intent) {
super.onActivityResult(requestCode, resultCode, intent);
...
}
Run Code Online (Sandbox Code Playgroud)
如果不这样做,并尝试在该方法中显示一个片段对话框,则可能会得到OP的IllegalStateException
。(说实话,我不太理解为什么超级调用解决了这个问题。onActivityResult()
在之前被调用了onResume()
,因此仍然不允许显示片段对话框。)
片段交易不应在之后执行Activity.onStop()
!
检查您是否没有任何可以在 之后执行事务的回调onStop()
。最好是解决问题的原因,而不是试图用类似的方法来解决问题.commitAllowingStateLoss()
归档时间: |
|
查看次数: |
299562 次 |
最近记录: |