我有一个Live Android应用程序,从市场上我收到了以下堆栈跟踪,我不知道为什么它会发生,因为它不是发生在应用程序代码中,而是由应用程序的一些或其他事件引起的(假设)
我没有使用片段,仍然有一个FragmentManager的参考。
如果有人能揭示一些隐藏的事实,以避免这类问题:
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.onKeyDown(Activity.java:1962)
at android.view.KeyEvent.dispatch(KeyEvent.java:2482)
at android.app.Activity.dispatchKeyEvent(Activity.java:2274)
at com.android.internal.policy.impl.PhoneWindow$DecorView.dispatchKeyEvent(PhoneWindow.java:1668)
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.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:1720)
at com.android.internal.policy.impl.PhoneWindow.superDispatchKeyEvent(PhoneWindow.java:1258)
at android.app.Activity.dispatchKeyEvent(Activity.java:2269)
at com.android.internal.policy.impl.PhoneWindow$DecorView.dispatchKeyEvent(PhoneWindow.java:1668)
at android.view.ViewRoot.deliverKeyEventPostIme(ViewRoot.java:2851)
at android.view.ViewRoot.handleFinishedEvent(ViewRoot.java:2824)
at android.view.ViewRoot.handleMessage(ViewRoot.java:2011)
at android.os.Handler.dispatchMessage(Handler.java:99)
at android.os.Looper.loop(Looper.java:132)
at android.app.ActivityThread.main(ActivityThread.java:4025)
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:841)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:599)
at dalvik.system.NativeStart.main(Native Method)
我有同样的问题,得到IllegalStateException,但替换所有调用commit()与commitAllowingStateLoss()没有帮助。
罪魁祸首是调用DialogFragment.show()。
我用
try {
dialog.show(transaction, "blah blah");
}
catch(IllegalStateException e) {
return;
}
这样就成功了。好吧,我没有展示对话,但在这种情况下,这是可以的。
这是我的应用程序中唯一一个我第一次调用FragmentManager.beginTransaction()但从未调用commit()的地方,所以当我寻找“commit()”时,我没有找到它。
有趣的是,用户从未离开该应用。相反,出现的AdMob插页广告才是罪魁祸首。
这对我很管用……我自己发现的…希望对你有所帮助!
1)不要有全局的“静态”FragmentManager / FragmentTransaction。
2) onCreate,总是初始化FragmentManager再次!
样本如下:-
public abstract class FragmentController extends AnotherActivity{
protected FragmentManager fragmentManager;
protected FragmentTransaction fragmentTransaction;
protected Bundle mSavedInstanceState;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mSavedInstanceState = savedInstanceState;
setDefaultFragments();
}
protected void setDefaultFragments() {
fragmentManager = getSupportFragmentManager();
//check if on orientation change.. do not re-add fragments!
if(mSavedInstanceState == null) {
//instantiate the fragment manager
fragmentTransaction = fragmentManager.beginTransaction();
//the navigation fragments
NavigationFragment navFrag = new NavigationFragment();
ToolbarFragment toolFrag = new ToolbarFragment();
fragmentTransaction.add(R.id.NavLayout, navFrag, "NavFrag");
fragmentTransaction.add(R.id.ToolbarLayout, toolFrag, "ToolFrag");
fragmentTransaction.commitAllowingStateLoss();
//add own fragment to the nav (abstract method)
setOwnFragment();
}
}
Android 4.2和支持库的源代码中修正了这个问题。[*]
有关原因(和解决方案)的详细信息,请参阅谷歌错误报告:
http://code.google.com/p/android/issues/detail?id=19917
如果你正在使用支持库,那么你不应该担心这个错误(很长时间)[*]。然而,如果你直接使用API(即不使用支持库的FragmentManager),并针对Android 4.2以下的API,那么你将需要尝试一种变通方法。
[*]在撰写本文时,Android SDK管理器仍在分发显示此错误的旧版本。
编辑,我要在这里补充一些澄清,因为我显然把投票反对这个答案的人弄糊涂了。
There are several different (but related) circumstances that can cause this exception to be thrown. My answer above is referring to the specific instance discussed in the question i.e. a bug in Android which has subsequently been fixed. If you're getting this exception for another reason it's because you're adding/removing fragments when you shouldn't be (after fragment states have been saved). If you're in such a situation then perhaps "Nested Fragments - IllegalStateException “Can not perform this action after onSaveInstanceState”" can be of use to you.
解决这个问题的另一种生命周期方法是使用kotlin最新发布的lifecycle-ktx。
lifecycleScope.launchWhenResumed {
// your code with fragment or dialogfragment
}
闭包将在恢复状态后运行,因此甚至在恢复状态后调用此方法
停,等下一份简历来的时候就安全执行了。
你也可以选择喜欢
lifecycleScope.launchWhenCreated
// or
lifecycleScope.launchWhenStarted
适合你的情况。
当销毁完成时,代码将被取消。
谷歌文档链接:
https://developer.android.com/kotlin/ktx#lifecycle
这是我迄今为止遇到的最愚蠢的错误。我有一个片段应用程序工作完美的API < 11,并强制关闭API > 11。
我真的不知道他们在saveInstance调用的活动生命周期内改变了什么,但我在这里是我如何解决这个问题的:
@Override
protected void onSaveInstanceState(Bundle outState) {
//No call for super(). Bug on API Level > 11.
}
我只是不调用.super(),一切都很好。我希望这将为您节省一些时间。
编辑:经过进一步研究,这是支持包中的一个已知错误。
如果你需要保存实例,并添加一些东西到你的outState Bundle,你可以使用以下方法:
@Override
protected void onSaveInstanceState(Bundle outState) {
outState.putString("WORKAROUND_FOR_BUG_19917_KEY", "WORKAROUND_FOR_BUG_19917_VALUE");
super.onSaveInstanceState(outState);
}
EDIT2:如果您试图在Activity在后台消失后执行事务,也可能会发生这种情况。为了避免这种情况,你应该使用commitAllowingStateLoss()
EDIT3: The above solutions were fixing issues in the early support.v4 libraries from what I can remember. But if you still have issues with this you MUST also read @AlexLockwood 's blog : Fragment Transactions & Activity State Loss
Summary from the blog post (but I strongly recommend you to read it) :
NEVER commit() transactions after onPause() on pre-Honeycomb, and onStop() on post-Honeycomb
Be careful when committing transactions inside Activity lifecycle methods. Use onCreate(), onResumeFragments() and onPostResume()
Avoid performing transactions inside asynchronous callback methods
Use commitAllowingStateLoss() only as a last resort
I solved the issue with onconfigurationchanged. The trick is that according to android activity life cycle, when you explicitly called an intent(camera intent, or any other one); the activity is paused and onsavedInstance is called in that case. When rotating the device to a different position other than the one during which the activity was active; doing fragment operations such as fragment commit causes Illegal state exception. There are lots of complains about it. It's something about android activity lifecycle management and proper method calls.
To solve it I did this:
1-Override the onsavedInstance method of your activity, and determine the current screen orientation(portrait or landscape) then set your screen orientation to it before your activity is paused. that way the activity you lock the screen rotation for your activity in case it has been rotated by another one.
2-then , override onresume method of activity, and set your orientation mode now to sensor so that after onsaved method is called it will call one more time onconfiguration to deal with the rotation properly.
你可以复制/粘贴这段代码到你的活动来处理它:
@Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
Toast.makeText(this, "Activity OnResume(): Lock Screen Orientation ", Toast.LENGTH_LONG).show();
int orientation =this.getDisplayOrientation();
//Lock the screen orientation to the current display orientation : Landscape or Potrait
this.setRequestedOrientation(orientation);
}
//A method found in stackOverflow, don't remember the author, to determine the right screen orientation independently of the phone or tablet device
public int getDisplayOrientation() {
Display getOrient = getWindowManager().getDefaultDisplay();
int orientation = getOrient.getOrientation();
// Sometimes you may get undefined orientation Value is 0
// simple logic solves the problem compare the screen
// X,Y Co-ordinates and determine the Orientation in such cases
if (orientation == Configuration.ORIENTATION_UNDEFINED) {
Configuration config = getResources().getConfiguration();
orientation = config.orientation;
if (orientation == Configuration.ORIENTATION_UNDEFINED) {
// if height and widht of screen are equal then
// it is square orientation
if (getOrient.getWidth() == getOrient.getHeight()) {
orientation = Configuration.ORIENTATION_SQUARE;
} else { //if widht is less than height than it is portrait
if (getOrient.getWidth() < getOrient.getHeight()) {
orientation = Configuration.ORIENTATION_PORTRAIT;
} else { // if it is not any of the above it will defineitly be landscape
orientation = Configuration.ORIENTATION_LANDSCAPE;
}
}
}
}
return orientation; // return value 1 is portrait and 2 is Landscape Mode
}
@Override
public void onResume() {
super.onResume();
Toast.makeText(this, "Activity OnResume(): Unlock Screen Orientation ", Toast.LENGTH_LONG).show();
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_SENSOR);
}