65

我有一个LoginActivity(用户登录)。它基本上是它自己的Activity,它的主题就像对话框一样(显示为对话框)。它出现在SherlockFragmentActivity之上。我想要的是:如果登录成功,则应该有两个FragmentTransaction来更新视图。下面是代码:“Failure Delivering Result” - onActivityForResult

LoginActivity,如果登录成功,

setResult(1, new Intent()); 

SherlockFragmentActivity

@Override 
protected void onActivityResult(int requestCode, int resultCode, Intent data) { 
    super.onActivityResult(requestCode, resultCode, data); 

    if (resultCode == 1) { 
     LoggedStatus = PrefActivity.getUserLoggedInStatus(this); 
     FragmentTransaction t = MainFragmentActivity.this.getSupportFragmentManager().beginTransaction(); 
     SherlockListFragment mFrag = new MasterFragment(); 
     t.replace(R.id.menu_frame, mFrag); 
     t.commit(); 

     // Set up Main Screen 
     FragmentTransaction t2 = MainFragmentActivity.this.getSupportFragmentManager().beginTransaction(); 
     SherlockListFragment mainFrag = new FeaturedFragment(); 
     t2.replace(R.id.main_frag, mainFrag); 
     t2.commit(); 
    } 
} 

它崩溃的第一次提交,这个logcat的:

E/AndroidRuntime(32072): Caused by: java.lang.IllegalStateException: Can not perform this action after onSaveInstanceState 
E/AndroidRuntime(32072): at android.support.v4.app.FragmentManagerImpl.checkStateLoss(FragmentManager.java:1299) 
E/AndroidRuntime(32072): at android.support.v4.app.FragmentManagerImpl.enqueueAction(FragmentManager.java:1310) 
E/AndroidRuntime(32072): at android.support.v4.app.BackStackRecord.commitInternal(BackStackRecord.java:541) 
E/AndroidRuntime(32072): at android.support.v4.app.BackStackRecord.commit(BackStackRecord.java:525) 
E/AndroidRuntime(32072): at com.kickinglettuce.rate_this.MainFragmentActivity.onActivityResult(MainFragmentActivity.java:243) 
E/AndroidRuntime(32072): at android.app.Activity.dispatchActivityResult(Activity.java:5293) 
E/AndroidRuntime(32072): at android.app.ActivityThread.deliverResults(ActivityThread.java:3315) 

回答

257

首先,您应该阅读我的blog post以获取更多信息(它介绍了为什么会发生此异常以及您可以如何防止发生此异常)。

调用commitAllowingStateLoss()更像是一种修补而已。国家损失不好,应该不惜一切代价避免。在调用onActivityResult()时,activity/fragment的状态可能尚未恢复,因此在此期间发生的任何事务都将因此而丢失。这是一个必须解决的非常重要的错误! (请注意,只有当您的系统被系统杀死后才会返回您的Activity ......这取决于设备的内存大小,有时可能很罕见......所以这种错误并不是什么测试时很容易捕捉)。

尝试移动你的交易进入onPostResume()代替(注意onActivityResult()onPostResume()onResume()onResume()后,总是叫总是叫):

private boolean mReturningWithResult = false; 

@Override 
protected void onActivityResult(int requestCode, int resultCode, Intent data) { 
    super.onActivityResult(requestCode, resultCode, data); 
    mReturningWithResult = true; 
} 

@Override 
protected void onPostResume() { 
    super.onPostResume(); 
    if (mReturningWithResult) { 
     // Commit your transactions here. 
    } 
    // Reset the boolean flag back to false for next time. 
    mReturningWithResult = false; 
} 

这似乎有点不可思议,但在做这样的事情是需要确保您的FragmentTransaction s始终承诺Activity的状态已恢复到其原始状态(onPostResume()保证在Activity的状态已被调用后才会被调用tored)。

+4

在片段中,我使用onResume时不存在onPostResume。我还没有看到这个问题,但也许你想对此发表评论。 PS:非常感谢您富有洞察力的帖子! – Maragues 2013-09-18 14:17:49

+21

是的,在'Fragment#onResume()'中这样做很好。这是因为'FragmentActivity#onPostResume()'调用'FragmentActivity#onResumeFragments()',它调用'FragmentManager#dispatchResume()',为每个活动的片段调用'Fragment#onResume()'。因此'Fragment#onResume()'在'FragmentActivity#onPostResume()'之后被调用,所以不会有问题(你可以查看[源代码](http://goo.gl/Lo1Z1T )每个班级为自己验证这个...或者你可以只是我:P)。谢谢!很高兴你认为他们很有洞察力。 :) – 2013-09-18 21:13:58

+0

@Alex,如果dialogfragment进行网络调用,并且在dialogfragment获取异步响应之前按下Home按钮,则在异步响应中调用this.dismiss()将导致状态丢失异常。在这种情况下,何时应该调用dismiss()以使状态不会丢失?请注意,请求和响应在dialogfragment中,而不在活动中。 – 2013-10-29 07:08:58

-1

你的logcat明确表示:“无法执行此操作n onSaveInstanceState之后“ - 您的Activity已经在此时死亡,无法返回任何结果。

正义之举:

Intent in = new Intent(); 
setResult(1, in); 

的地方在哪里Activity它仍然活着,一切都会好起来。 并且不要忘记finish()你的Activity来提供结果。

+0

感谢。 'finish()'是我正在做的最后一件事。我的意图代码就在它的上面,我把它移到了顶部,同样的错误。我正在添加代码来回答... – KickingLettuce 2013-04-28 17:58:07

+0

最后一条评论还有一点。我其实并没有看到烤面包。但是,当我在崩溃后重新进入应用程序时,我已登录。 – KickingLettuce 2013-04-28 18:03:44

-1

您可以使用ft.commitAllowingStateLoss()来解决此问题。

原因:您的ft.commit()方法在onSaveInstanceState之后切换。

0

在我的情况我遇到由于以下

public void onBackPressed() { 
    super.onBackPressed(); 
    setIntents(); 

} 


private void setIntents(){ 
    Intent searchConstaints=new Intent(); 
    searchConstaints.putExtra("min",20); 
    searchConstaints.putExtra("max",80); 
    setResult(101,searchConstaints); 
    finish(); 
} 

通过重新排列的功能解决了相同的问题中调用onBackPressed()

public void onBackPressed() { 

    setIntents(); 
    super.onBackPressed(); 

} 
2

这类似于@ Alex Lockwood回答但使用了Runnable

private Runnable mOnActivityResultTask; 

@Override 
protected void onActivityResult(int requestCode, int resultCode, Intent data) { 
    super.onActivityResult(requestCode, resultCode, data); 
    mOnActivityResultTask = new Runnable() { 
     @Override 
     public void run() { 
      // Your code here 
     } 
    } 
} 

@Override 
protected void onPostResume() { 
    super.onPostResume(); 
    if (mOnActivityResultTask != null) { 
     mOnActivityResultTask.run(); 
     mOnActivityResultTask = null; 
    } 
} 

如果您正在运行Android 3.0的及以上lambda表达式,使用此:

@Override 
protected void onActivityResult(int requestCode, int resultCode, Intent data) { 
    super.onActivityResult(requestCode, resultCode, data); 
    mOnActivityResultTask =() -> { 
     // Your code here 
    } 
} 
+0

我宁愿和runnable一起去,因为这可以让你将逻辑写得靠近它发生的地方。在我的情况下,它来自rx用户。 – Fabio 2017-09-23 07:35:56