例外「IllegalStateException:onSaveInstanceStateの後でこのアクションを実行できません」が発生する


355

私はライブ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)  

解決策はもう見つかりましたか?ここでは同じ問題を抱えて:stackoverflow.com/questions/7575921/...
nhaarman


2
@phlebasいいえ、しませんでした。あなたはダイアログに関係しますが、これは関係ありません。スタックトレースの一致の一番上の行では不十分です。残りは非常に異なります。私はあなたの問題を見ただけで、残念ながら私には仕方がないので、これを言います。
themightyjon

そのアクティビティでスレッドまたはAsynTaskを使用しますか?
ホセ・カストロ

21
このエラーについては、ブログの投稿で説明しています...必ずお読みください。:)
アレックスロックウッド

回答:


455

これは私がこれまでに遭遇した中で最も愚かなバグです。API <11でFragment完全に機能するアプリケーションがあった、およびForce Closing11 API>を

私は彼らが内部で何が変わったのか本当に理解できませんでした Activityの呼び出しでライフサイクル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:上記の解決策は、私が覚えている初期のsupport.v4ライブラリの問題を修正していました。しかし、それでも問題が解決しない場合は、@ AlexLockwoodのブログも読むあります。フラグメントのトランザクションとアクティビティの状態の損失

ブログ投稿の要約(ただし、読むことを強くお勧めします):

  • NEVER commit()後の取引onPause()事前ハニカムに、とonStop()後のハニカムに
  • Activityライフサイクルメソッド内でトランザクションをコミットするときは注意してください。使用する onCreate()onResumeFragments()およびonPostResume()
  • 非同期コールバックメソッド内でトランザクションを実行しない
  • commitAllowingStateLoss()最後の手段としてのみ使用してください

97
commit()の代わりにcommitAllowingStateLoss()を使用する必要があります
meh

7
したがって、onSaveInstanceStateでsuperを呼び出さないと、FragmentManagerがすべてのフラグメントの状態を保存して復元できなくなります。回転に問題が発生する可能性があります。また、私はジャンクをバンドルに入れることについて他のことを試しましたが、それは私にとって何の違いもありません。それがどうなるかわからない-サポートパッケージで参照したバグはNullPointerExceptionであり、このIllegalStateExceptionのようには見えない...
themightyjon

56
@meh commitAllowingStateLoss()は例外を回避するだけです。偶発的な状態の損失からアプリケーションを保護するものではありません。こちらのブログ投稿をご覧ください。
Alex Lockwood

2
@AlexLockwoodなので、そのブログ投稿から、フラグメント内ですべてのネットワーク呼び出しを実行する必要があること(および必要に応じて一時的な進捗UIを表示する必要があること)を知ることができます。非同期メソッド呼び出しの後に呼び出されています。
2013

1
最初のポイントがわかりません。「ハニカム後のonStop()の後にcommit()トランザクションを実行しないでください」。フラグメントを別のものに置き換えるためのボタンが必要な場合はどうなりますか?アクティビティがonStopで終了したかどうかを確認するブール値を配置する必要があります。終了した場合は、代わりにcommitAllowingStateLossを呼び出しますか?また、フラグメント内にフラグメントがある場合、ボタンをクリックすると置換する必要がありますか?
android開発者

76

この問題の原因についてAndroidソースコードを調べると、FragmentManagerImplクラス(アクティビティで使用可能なインスタンス)のフラグmStateSavedの値がtrueであることがわかります。からの呼び出し時にバックスタックが保存されると(saveAllState)、trueに設定されますActivity#onSaveInstanceState。その後、ActivityThreadからの呼び出しは、FragmentManagerImpl#noteStateNotSaved()およびから利用可能なリセットメソッドを使用してこのフラグをリセットしませんdispatch()

私がそれを見る方法は、あなたのアプリが何をして何を使用しているかに応じて、いくつかの利用可能な修正があります:

良い方法

何よりもまず:アレックスロックウッドの記事を宣伝します。次に、これまでに行ったことから:

  1. 状態情報を保持する必要がないフラグメントとアクティビティの場合は、commitAllowStateLossを呼び出します。ドキュメントから取得:

    アクティビティの状態が保存された後にコミットを実行できるようにします。アクティビティを後でその状態から復元する必要がある場合はコミットが失われる可能性があるため、これは危険です。これは、ユーザーのUIの状態が予期せず変更されても問題がない場合にのみ使用してください。フラグメントが読み取り専用の情報を表示している場合は、これで問題ないと思います。または、編集可能な情報を表示する場合でも、コールバックメソッドを使用して編集した情報を保持します。

  2. トランザクションがコミットされた直後(commit()呼び出したばかり)にを呼び出しますFragmentManager.executePendingTransactions()

推奨されない方法:

  1. Ovidiu Latcuが前述したように、電話しないでくださいsuper.onSaveInstanceState()。しかし、これはフラグメントの状態とともにアクティビティの全体の状態を失うことを意味します。

  2. オーバーライドonBackPressedして、そこでのみ呼び出しますfinish()。アプリケーションがFragments APIを使用しない場合は、これで問題ありません。のsuper.onBackPressed呼び出しがあるようにFragmentManager#popBackStackImmediate()

  3. 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);
    }
}

乾杯!


これは、ジンジャーブレッドの下のActionBarSherlockでも発生するようです。そのため、ビルドIDをチェックするケースは
意味がない

また、指摘する必要があります-これはABSの使用にも適していません:)
t0mm13b '28

@ t0mm13b:上記のコードは、フラグメントもサポートも使用しないため、私のプロジェクトを表しています。また、support.FragmentActivityも使用していません。これはandroid.app.Activityで実行され、例外をトリガーする不整合はFragmentManager(APIレベル11以上)が原因で発生するため、チェックが行われるのはそのためです。悪の原因も同じであると思われる場合は、お気軽にチェックを削除します。ABSは、互換性パッケージとsupport.FragmentActivityの実装の上で実行されるため、別の話です。FragmentManagerは、同じ実装のFragmentManagerとvoilaを使用できます。同じ問題です。
ガンナー2013

@ t0mm13b:600文字では不十分なため、さらに追加するには、まず何が原因であるかを最初に調査する必要があります。また、上記は醜いハックであり、実行してもしなくても私は責任を負わないことを理解する必要があります(私にとっては状況を考慮した最善の解決策でした)。使用する必要がある場合は、標準のパッケージと異なる可能性があるため、変数の命名について互換性のあるソースコードを再確認してください。この問題が互換性パッケージの次のバージョンで解決されることを願っていますが、Androidの経験から、発生する可能性はほとんどありません...
gunar

ええと...このバグレポート まったく同じ問題が、このOPの質問のポイントです。あなたは-私は私のコメントに立つべきで明示的に免責事項に入れて、それが保証されていないとも言っている必要がありそうでない理由を答えのどちらかという投稿を気に-あなたはフラグメントを使用していないと述べました!:)ただ言って...
t0mm13b

35

このような例外は、フラグメントアクティビティonSaveInstanceState()が呼び出された後にフラグメント遷移を実行しようとすると発生します。

これが発生する可能性がある1つの理由は、アクティビティが停止したときにAsyncTask(またはThread)を実行したままにした場合です。

onSaveInstanceState()システムがリソースのアクティビティを再利用し、後で再作成すると、呼び出し後の遷移が失われる可能性があります。


2
Hey Funk、私はここで質問があります。そのアクティビティまたはフラグメントが停止されている場合、そのアクティビティまたはフラグメントでonBackPressedを呼び出すことができるのはなぜですか。上記の例外はいくつかのUIイベント(つまり、戻るキーを押す)から生成されたようですが、非同期タスクと戻るキーの関係を見つけることができません。
dcool 2011年

フラグメントのトランジションをバック状態に保存できるため、戻るボタンを押すと、保存したトランジションが元に戻る可能性があります(古いフラグメントが元に戻る)。onSaveInstanceStateは、必ずしもonStopが呼び出された後ではなく、アクティビティが破棄されてリソースをシステムに復元する前に呼び出されます。申し訳ありませんが、私の答えではそれははっきりしていませんでした。
FunkTheMonk

ファンクですが、アプリケーションでフラグメントを使用していません。ネイティブコードで使用されるフラグメントの可能性があります。以前、あなたは同じことを話していると思いました。
dcool

1
フラグメントへの参照を持つAsyncTaskがありました。onSaveInstanceStateからsuper()呼び出しを削除し、AsyncTaskからの参照をWeakReference <Fragment>に置き換えた結果、問題は解決しました。
バッファロー

2
@バッファローそれは間違いなく問題の解決策ではありません。あなたはいつも電話する必要がありますsuper.onSaveInstanceState()
Alex Lockwood 2013

27

フラグメントを表示する前に単にsuper.onPostResume()を呼び出すか、super.onPostResume()を呼び出した後にonPostResume()メソッドにコードを移動します。これで問題が解決します!


6
onPostResume()を呼び出すと、onResumeFragments()が確実に呼び出され、これが理想的なソリューションになります。
j2emanue 14年

20

これはdismiss()、画面がロックされてブランクになり、アクティビティ+ダイアログのインスタンス状態が保存された後にダイアログフラグメントを呼び出すときにも発生する可能性があります。この呼び出しを回避するには:

dismissAllowingStateLoss()

文字通り、私がダイアログを閉じるたびに、とにかくその状態は気にしないので、これは問題ありません。実際に状態を失うことはありません。


2
これは私の正確な問題でした!あなたは素晴らしいです!
Tash Pemhiwa、2016年

17

短くて実用的なソリューション:

簡単な手順に従ってください:

ステップ1:各フラグメントのonSaveInstanceState状態をオーバーライドします。そして、そこからスーパーメソッドを削除します。

@Override
public void onSaveInstanceState(Bundle outState) {
};

ステップ2:CommitAllowingStateLoss();を使用します commit();の代わりに 一方、フラグメント操作。

fragmentTransaction.commitAllowingStateLoss();

1
スーパーメソッドを削除するとうまくいきましたが、理由を説明できますか?それを削除しても安全ですか?
Bruce

7
super()を削除するのは安全ではありません。その後、他のデータ設定が失われます!
デッドフィッシュ2016

12

ライフサイクル状態は、Androidサポートライブラリv26.1.0からこのようなクラッシュを防ぐのに役立つと思います。次のチェックを行うことができます。

if (getLifecycle().getCurrentState().isAtLeast(Lifecycle.State.STARTED)){
  // Do fragment's transaction commit
}

またはあなたが試すことができます:

Fragment.isStateSaved()

詳細はこちら https://developer.android.com/reference/android/support/v4/app/Fragment.html#isStateSaved()


7

これは私のために働いた...私自身でこれを見つけた...それがあなたを助けることを願っています!

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();
    }
}

6

onActivityForResult()メソッドでフラグメントを表示しようとしたときに常にこれを取得していたため、問題は次のとおりでした。

  1. 私のアクティビティは一時停止および停止しています。つまり、onSaveInstanceState()はすでに呼び出されています(Honeycomb以前のデバイスとHoneycombデバイスの両方で)。
  2. 結果が発生した場合、フラグメントを表示/非表示にするトランザクションを作成しました。これにより、このIllegalStateExceptionが発生します。

私が作ったのは次です:

  1. 必要なアクションが行われたかどうかを判断するための追加値(例:camereから写真を撮る-isPhotoTaken)-必要なトランザクションの数に応じて、ブール値または整数値にすることができます。
  2. オーバーライドされたonResumeFragments()メソッドで、自分の値を確認し、フラグメントトランザクションを作成した後、必要に応じました。この場合、onResumeFragments()メソッドで状態が返されたため、onSaveInstanceStateの後にcommit()は実行されませんでした。

5

onconfigurationchangedで問題を解決しました。トリックは、Androidアクティビティのライフサイクルに従って、明示的にインテント(カメラインテント、またはその他のインテント)を呼び出した場合です。その場合、アクティビティは一時停止され、onsavedInstanceが呼び出されます。アクティビティがアクティブであった位置以外の別の位置にデバイスを回転させたとき。フラグメントコミットなどのフラグメント操作を実行すると、不正な状態の例外が発生します。それについて多くの不満があります。これは、Androidアクティビティのライフサイクル管理と適切なメソッド呼び出しに関するものです。それを解決するために私はこれを行いました:1-アクティビティのonsavedInstanceメソッドをオーバーライドし、現在の画面の向き(縦または横)を決定し、アクティビティが一時停止する前に画面の向きをそれに設定します。そうすれば、アクティビティが別のアクティビティによって回転された場合に、アクティビティの画面回転をロックできます。2-then、onresumeメソッドのアクティビティをオーバーライドし、オリエンテーションモードをセンサーに設定して、onsavedメソッドが呼び出された後、構成をもう一度呼び出して回転を適切に処理するようにします。

このコードをコピーしてアクティビティに貼り付け、処理することができます。

@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);
} 

4

IllegalStateExceptionを取得して同じ問題が発生しましたが、commit()へのすべての呼び出しをcommitAllowingStateLoss()で置き換えても効果がありませんでした。

犯人はDialogFragment.show()の呼び出しでした。

で囲む

try {
    dialog.show(transaction, "blah blah");
}
catch(IllegalStateException e) {
    return;
}

そしてそれはそれをやった。OK、ダイアログを表示することはできませんが、この場合は問題ありませんでした。

これは、アプリでFragmentManager.beginTransaction()を最初に呼び出したが、commit()を呼び出さなかった唯一の場所だったため、「commit()」を探しても見つかりませんでした。

面白いのは、ユーザーがアプリを離れることはないということです。代わりに、キラーは現れたAdMobインタースティシャル広告でした。


3
こっちも一緒。'show(FragmentManager manager、String tag)'メソッドをオーバーライドして、 'commit'を 'commitAllowingStateLoss'に置き換えて解決しました。ダイアログの2つのプライベート属性、mDismissedとmShownByMeを設定できないため、何かが失われます。しかし、それは毎回動作するようです:)
Francesco Ditrani

この例外を回避できるDialogFragmentの代替ソリューションを作成しました:github.com/AndroidDeveloperLB/DialogShard
android developer

4

その問題に対する私の解決策は

フラグメント追加メソッド:

@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
    ...
    guideMapFragment = (SupportMapFragment)a.getSupportFragmentManager().findFragmentById(R.id.guideMap);
    guideMap = guideMapFragment.getMap();
    ...
}

@Override
public void onDestroyView() {
    SherlockFragmentActivity a = getSherlockActivity();
    if (a != null && guideMapFragment != null) {
        try {
            Log.i(LOGTAG, "Removing map fragment");
            a.getSupportFragmentManager().beginTransaction().remove(guideMapFragment).commit();
            guideMapFragment = null;
        } catch(IllegalStateException e) {
            Log.i(LOGTAG, "IllegalStateException on exit");
        }
    }
    super.onDestroyView();
}

悪いかもしれませんが、これ以上良いものを見つけることができませんでした。


True ..例外をキャッチするとアプリケーションのクラッシュを回避できますが、動作に問題があり、画面に残されたり、追加されなかったりします。
Marcos Vasconcelos 2013

4
スクロールし続けます。真実はそこにあります
anil

4

この問題が発生しましたが、この問題はcommitおよびcommitAllowStateLossとは関係がないと思います。

次のスタックトレースと例外メッセージは、commit()に関するものです。

java.lang.IllegalStateException: Can not perform this action after onSaveInstanceState
at android.support.v4.app.FragmentManagerImpl.checkStateLoss(FragmentManager.java:1341)
at android.support.v4.app.FragmentManagerImpl.enqueueAction(FragmentManager.java:1352)
at android.support.v4.app.BackStackRecord.commitInternal(BackStackRecord.java:595)
at android.support.v4.app.BackStackRecord.commit(BackStackRecord.java:574)

しかし、この例外はonBackPressed()が原因でした。

java.lang.IllegalStateException: Can not perform this action after onSaveInstanceState
at android.support.v4.app.FragmentManagerImpl.checkStateLoss(Unknown Source)
at android.support.v4.app.FragmentManagerImpl.popBackStackImmediate(Unknown Source)
at android.support.v4.app.FragmentActivity.onBackPressed(Unknown Source)

それらはすべてcheckStateLoss()が原因でした。

private void checkStateLoss() {
    if (mStateSaved) {
        throw new IllegalStateException(
                "Can not perform this action after onSaveInstanceState");
    }
    if (mNoTransactionsBecause != null) {
        throw new IllegalStateException(
                "Can not perform this action inside of " + mNoTransactionsBecause);
    }

mSaveSavedは、onSaveInstanceStateの後でtrueになります。

この問題はめったに発生しません。この問題に遭遇したことはありません。問題を再発させることはできません。

問題25517が見つかりました

次の状況で発生した可能性があります

  1. onSaveInstanceStateの後で、新しいアクティビティが開始される前に、Backキーが呼び出されます。

  2. コードでonStop()を使用する

問題の根本が何かはわかりません。だから私は醜い方法を使いました。

@Override
public void onBackPressed() {

    try{
        super.onBackPressed();
    }catch (IllegalStateException e){
        // can output some information here
        finish();
    }
}

私は実際には問題を解決しませんでしたが、この問題はcommitおよびcommitAllowStateLossとは関係ありません。
oO_ox

4

私のアプリでも同じ問題が発生しています。この問題はsuper.onBackPressed();、前のクラスのcommitAllowingStateLoss()on を呼び出し、そのフラグメントで現在のクラスのon を呼び出すだけで解決されました。


2
ありがとうございました。このソリューションは、commitAllowingStateLoss()代わりにuisngの問題を解決しましたcommit()
Chintak Patel 2017

commitAllowingStateLoss()medium.com/@elye.project/…の
swooby

3

onSaveInstanceは、ユーザーが画面を回転させて新しい向きに関連付けられたリソースをロードできる場合に呼び出されます。

このユーザーが画面を回転させてから[戻る]ボタンを押した可能性があります(このユーザーがアプリの使用中にスマートフォンを操作した可能性もあるため)


2
構成の変更(方向の変更など)によってこの例外が発生する可能性がありますが、根本的な原因ではありません。
Alex Lockwood 2013


2

同じ問題があり、すべての記事、ブログ、stackoverflowを1日長時間分析した後、簡単な解決策を見つけました。savedInstanceStateはまったく使用しないでください。これは、1行のコードの条件です。フラグメントコード:

@Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(null);
    .....

2

これは、フラグメントをロードしようとしているが、アクティビティがその状態をonPause()に変更したときに発生します。これは、たとえば、データをフェッチしてアクティビティにロードしようとしたときに、ユーザーがボタンをクリックして、次のアクティビティに移動しました。

これは2つの方法で解決できます

transaction.commit()の代わりにtransaction.commitAllowingStateLoss()を使用してフラグメントをロードできますが、実行されたコミット操作が失われる可能性があります。

または

フラグメントをロードするときに、アクティビティが再開され、一時停止状態にならないことを確認してください。ブール値を作成し、アクティビティがonPause()状態にならないかどうかを確認します。

@Override
public void onResume() {
    super.onResume();
    mIsResumed = true;
}

@Override
public void onPause() {
    mIsResumed = false;
    super.onPause();
}

次に、フラグメントのロード中に、アクティビティが存在するかどうかを確認し、アクティビティがフォアグラウンドの場合にのみロードします。

if(mIsResumed){
 //load the fragment
}

1

@gunarに感謝しますが、もっと良い方法があると思います。

ドキュメントによると:

 * If you are committing a single transaction that does not modify the
 * fragment back stack, strongly consider using
 * {@link FragmentTransaction#commitNow()} instead. This can help avoid
 * unwanted side effects when other code in your app has pending committed
 * transactions that expect different timing.
 *
 * @return Returns true if there were any pending transactions to be
 * executed.
 */
public abstract boolean executePendingTransactions();

したがってcommitNow、置き換えに使用します。

fragmentTransaction.commit();
FragmentManager.executePendingTransactions()

0

まあ、上記のすべての解決策を試した後、成功しませんでした(基本的にはトランザクションがないためです)。

私の場合、AlertDialogsとProgressDialogをフラグメントとして使用していましたが、ローテーション時にFragmentManagerを要求すると、エラーが発生しました。

私はいくつかの同様の投稿をいくつか組み合わせて回避策を見つけました:

その3ステップのソリューションはすべてFragmentActivity(この場合はGenericActivityと呼ばれます)で行われます。

private static WeakReference<GenericActivity> activity = null; //To avoid bug for fragments: Step 1 of 3

@Override
protected void onCreate(Bundle savedInstanceState){
    super.onCreate(savedInstanceState);
    //To avoid bug for fragments: Step 2 of 3
    activity = new WeakReference<GenericActivity>(this);
}

@Override
public FragmentManager getSupportFragmentManager(){
    //To avoid bug for fragments: Step 3 of 3
    if (this == activity.get()) {
        return super.getSupportFragmentManager();
    }
    return activity.get().getSupportFragmentManager();
}

0

1つのフラグメントでstartactivityを使用すると、この例外が発生します。

startactivityforresultを使用するように変更すると、例外がなくなります:)

したがって、それを修正する簡単な方法は、startActivityForResult APIを使用することです:)


0

マップフラグメントアクティビティのインテントチューザーをキャンセルするために戻るボタンを押しているときに、この例外が発生しました。onResume()(フラグメントを初期化してトランザクションをコミットしていた)のコードをonStart()に置き換えることでこれを解決し、アプリは現在正常に動作しています。それが役に立てば幸い。


0

これはAndroid 4.2とサポートライブラリのソースで修正されています。[*]

原因(および回避策)の詳細については、Googleバグレポートを参照してください。 ください http

サポートライブラリを使用している場合は、このバグを(長い間)心配する必要はありません[*]。ただし、APIを直接使用している(つまり、サポートライブラリのFragmentManagerを使用していない)場合、Android 4.2より前のAPIをターゲットにすると、回避策の1つを試す必要があります。

[*]現時点では、Android SDK Managerはこのバグを示す古いバージョンを配布しています。

編集この回答に反対票を投じた人を明らかに混乱させたので、ここでいくつかの説明を追加します。

あり、この例外がスローされる可能性がありますいくつかの異なる(しかし関連)状況が。上記の私の答えは、質問で説明されている特定のインスタンス、つまりAndroidのバグであり、その後修正されています。別の理由でこの例外が発生した場合は、フラグメントの状態を保存した後ではないはずのときにフラグメントを追加または削除しているためです。このような状況にある場合は、おそらく「ネストされたフラグメント-IllegalStateException「onSaveInstanceStateの後でこのアクションを実行できない」」が役に立ちます。



0

私の使用例:リスナをフラグメントで使用して、何かが起こったことをアクティビティに通知しました。コールバックメソッドで新しいフラグメントのコミットを行いました。これは初めて完璧に機能します。ただし、向きを変更すると、アクティビティは保存されたインスタンスの状態で再作成されます。その場合、フラグメントが再度作成されないということは、フラグメントが古い破壊されたアクティビティであるリスナーを持っていることを意味します。コールバックメソッドがアクションでトリガーされる方法。それは問題を引き起こす破壊された活動に行きます。解決策は、現在のライブアクティビティで断片的にリスナーをリセットすることです。これは問題を解決します。


0

私が見つけたのは、別のアプリがダイアログタイプであり、タッチをバックグラウンドアプリに送信できる場合、ほとんどすべてのバックグラウンドアプリがこのエラーでクラッシュすることです。インスタンスが保存または復元されたかどうか、トランザクションが実行されるたびに確認する必要があると思います。


0

私の場合、同じエラー例外を使用して、「onBackPressed()」をランナブルに配置します(任意のビューを使用できます)。

myView.post(new Runnable() {
                    @Override
                    public void run() {
                        onBackPressed()
                    }
                });

理由はわかりませんが、うまくいきます!


ビューに投稿すると、ビューが適切にレイアウトされて画面に描画された後でのみRunnableが実行されます。多くの場合、これはアクティビティ自体が完全に再開されたことを意味するため、問題はありません
Mercato

0

あなたはfragmentManager.popBackStackImmediate();を呼び出しているかもしれません。アクティビティが一時停止したとき。アクティビティは終了していませんが、一時停止していてフォアグラウンドにはありません。popBackStackImmediate()の前に、アクティビティが一時停止しているかどうかを確認する必要があります。


0

とても面白いことに気づきました。私のアプリには電話のギャラリーを開くオプションがあり、デバイスは使用するアプリを尋ねます。そこでダイアログから離れた灰色の領域をクリックして、この問題を確認しました。私のアクティビティがonPause、onSaveInstanceStateからonResumeに戻ることに気付きましたが、onCreateViewにアクセスすることはありません。onResumeで取引をしています。つまり、私がやったことは、フラグをonPauseで否定し、onCreateViewで真にすることです。フラグがtrueの場合はonResumeを実行し、次にonCommitを実行します。それ以外の場合はcommitAllowingStateLossを実行します。継続して多くの時間を無駄にすることができましたが、ライフサイクルを確認したいと思いました。sdkversion 23のデバイスを使用していますが、この問題は発生しませんが、21の別のデバイスを使用していますが、そこに表示されます。


-1

あなたはpopBackStackImmediateの前にFragmentActivity.onStartを使うことができます

このような:

public void backStackFragment() {
    this.start();
    getFragmentManager().popBackStackImmediate();
}

public void start(){
    FragmentActivity a = getActivity();
    if(a instanceof DepositPlanPadActivity){
      ((DepositPlanPadActivity)a).onStart();
    }
    if(a instanceof SmallChangePlanPad){
            ((SmallChangePlanPad)a).onStart();
        }
        if(a instanceof UserCenterActivity){
            ((UserCenterActivity)a).onStart();
        }
    }

http://jorryliu.blogspot.com/2014/09/illegalstateexception-can-not-perform.html

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.