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