我有一个Live Android应用程序,从市场上我收到了以下堆栈跟踪信息,我不知道为什么它发生在应用程序代码中而不是发生,而是由应用程序中的某些或其他事件引起的(假设)
我没有使用Fragments,但仍然有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)
#1楼
这是到目前为止我遇到的最愚蠢的错误。 我有一个适用于API <11的Fragment
应用程序,并且在API> 11上Force Closing
。
我真的不知道在调用saveInstance
它们在Activity
生命周期内发生了什么变化,但是我在这里解决了这个问题:
@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:从我记得的情况来看 ,上述解决方案正在解决早期support.v4库中的问题。 但是,如果您仍然对此有疑问,还必须阅读@AlexLockwood的博客: 碎片事务和活动状态丢失
博客文章的摘要(但我强烈建议您阅读):
- 永远不要在蜂巢前
onPause()
和蜂巢后onStop()
之后commit()
交易 - 在
Activity
生命周期方法内提交事务时要小心。 使用onCreate()
,onResumeFragments()
和onPostResume()
- 避免在异步回调方法中执行事务
- 只能将
commitAllowingStateLoss()
用作最后的手段
#2楼
我通过onconfigurationchanged解决了这个问题。 诀窍是根据android活动的生命周期,当您显式调用intent(相机intent或任何其他一个)时; 活动被暂停,在这种情况下将调用onsavedInstance。 将设备旋转到活动进行期间以外的其他位置时; 进行片段提交等片段操作会导致非法状态异常。 对此有很多抱怨。 这与android活动生命周期管理和正确的方法调用有关。 为了解决这个问题,我做到了:1-重写活动的onsavedInstance方法,并确定当前的屏幕方向(人像或风景),然后在暂停活动之前为其设置屏幕方向。 这样,如果您的活动被另一个旋转,则您可以锁定该屏幕的旋转。 2-然后,覆盖活动的onresume方法,现在将方向模式设置为传感器,以便在调用onsaved方法后,它将再调用一次onconfiguration来正确处理旋转。
您可以将此代码复制/粘贴到您的活动中以进行处理:
@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);
}
#3楼
查看导致此问题的原因的Android源代码会发现FragmentManagerImpl
类(活动中可用的实例)中的标志mStateSaved的值为true。 当从Activity#onSaveInstanceState
调用保存后退堆栈(saveAllState)时,将其设置为true。 之后,从ActivityThread进行的调用不会使用FragmentManagerImpl#noteStateNotSaved()
和dispatch()
可用的重置方法来重置此标志。
我的看法是,有一些可用的修复程序,具体取决于您的应用程序在做什么和使用:
好方法
在此之前:我会为Alex Lockwood的文章做广告。 然后,从我到目前为止所做的事情来看:
对于不需要保留任何状态信息的片段和活动,请调用commitAllowStateLoss 。 来自文档:
允许在保存活动状态后执行提交。 这很危险,因为如果以后需要将活动从其状态还原,则提交可能会丢失,因此仅应在UI状态可以在用户上意外更改的情况下使用。 我想如果片段显示只读信息就可以使用。 或者即使它们确实显示了可编辑的信息,也可以使用回调方法保留已编辑的信息。
在事务提交之后(您刚刚调用了
commit()
),立即调用FragmentManager.executePendingTransactions()
。
不推荐的方法:
如上述Ovidiu Latcu所述,请勿调用
super.onSaveInstanceState()
。 但这意味着您将丢失活动的整个状态以及碎片状态。覆盖
onBackPressed
,在那里仅调用finish()
。 如果您的应用程序不使用Fragments API,则应该没问题; 与super.onBackPressed
一样,存在对FragmentManager#popBackStackImmediate()
的调用。如果您同时使用Fragments API,并且活动状态很重要/很重要,则可以尝试使用反射API
FragmentManagerImpl#noteStateNotSaved()
进行调用。 但这是黑客,或者可以说这是一种解决方法。 我不喜欢它,但是就我而言,这是可以接受的,因为我有一个来自旧版应用程序的代码,该代码使用了不赞成使用的代码(TabActivity
和隐式LocalActivityManager
)。
以下是使用反射的代码:
@Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
invokeFragmentManagerNoteStateNotSaved();
}
@SuppressWarnings({ "rawtypes", "unchecked" })
private void invokeFragmentManagerNoteStateNotSaved() {
/**
* For post-Honeycomb devices
*/
if (Build.VERSION.SDK_INT < 11) {
return;
}
try {
Class cls = getClass();
do {
cls = cls.getSuperclass();
} while (!"Activity".equals(cls.getSimpleName()));
Field fragmentMgrField = cls.getDeclaredField("mFragments");
fragmentMgrField.setAccessible(true);
Object fragmentMgr = fragmentMgrField.get(this);
cls = fragmentMgr.getClass();
Method noteStateNotSavedMethod = cls.getDeclaredMethod("noteStateNotSaved", new Class[] {});
noteStateNotSavedMethod.invoke(fragmentMgr, new Object[] {});
Log.d("DLOutState", "Successful call for noteStateNotSaved!!!");
} catch (Exception ex) {
Log.e("DLOutState", "Exception on worka FM.noteStateNotSaved", ex);
}
}
干杯!
#4楼
阅读http://chris-alexander.co.uk/on-engineering/dev/android-fragments-within-fragments/
文章。 fragment.isResumed()检查可以帮助我使用onSaveInstanceState方法来实现onDestroyView。
#5楼
这对我有用...我自己发现了...希望对您有帮助!
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();
}
}
来源:oschina
链接:https://my.oschina.net/stackoom/blog/3182749