web-dev-qa-db-ja.com

FragmentTransactionをコミットする前に状態が保存されているかどうかを確認する

ユーザーがアクティビティを参照していないときに(状態が保存された後に)発生する可能性のあるコミットについて、次のスタックトレースが表示されることがあります。

Java.lang.IllegalStateException: Can not perform this action after onSaveInstanceState
    at Android.support.v4.app.FragmentManagerImpl.checkStateLoss(FragmentManager.Java:1327)
    at Android.support.v4.app.FragmentManagerImpl.enqueueAction(FragmentManager.Java:1338)
    at Android.support.v4.app.BackStackRecord.commitInternal(BackStackRecord.Java:595)
    at Android.support.v4.app.BackStackRecord.commit(BackStackRecord.Java:574)

Androidソースを見ると、これはまったく理にかなっています:

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

今、フラグメントが望ましくない状態でコミットされるかどうかを確認する方法(on(Save/Restore)InstanceStateにクラス変数を保存する以外に)があるのだろうか、この方法で後でトランザクションを保存して作成することができます適切なタイミングでコミットします。

40
hwrdprkns

サンプルコードを添付しなかったため、トランザクションをコミットするときに「間違った」メソッドを使用していると推測できます。

したがって、FragmentTransaction.commit()を使用する代わりに、 FragmentTransaction.commitAllowingStateLoss() を使用する必要があります。

また、この問題(またはAPIの動作の変更)に関するレポートと回避策が このGoogleブログの投稿 にあります。

20
Tomo

サポートライブラリバージョン 26.0.0 Beta 1 から開始して、FragmentManagerおよびFragmentクラスで新しいAPIを使用できます。

FragmentManagerおよびFragmentにはがありますisStateSaved()メソッドを使用して、トランザクションが状態を失うことなく許可されるかどうかを照会できます。これは、トランザクションを実行する前にonClick()イベントを処理するときにチェックするのに特に役立ちます。

Android.support.v4.app.FragmentManager#isStateSaved() のドキュメントから:

FragmentManagerの状態がすでに保存されている場合、trueを返しますホスト。このメソッドがtrueを返す場合、保存状態を変更する操作は実行しないでください。たとえば、popBackStack()メソッド(popBackStackImmediate()など)または任意のFragmentTransactioncommit()の代わりにcommitAllowingStateLoss()は状態を変更し、エラーになります。

このAPIは、フレームワークの Android.app.FragmentManager からAndroid O.

43
azizbekian

不幸なことにAndroid Fragmentは、トランザクションをコミットしても問題ない場合、APIチェックを提供しません。

ただし、添付されたアクティビティにブールフィールドを追加して、確認に役立てることができます。次のコードを参照してください。

_public class GlobalBaseActivity extends FragmentActivity {

    private boolean mAllowCommit;

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

        mAllowCommit = true;
    }

    @Override
    protected void onSaveInstanceState(Bundle outState) {
        mAllowCommit = false;
        super.onSaveInstanceState(outState);
    }

    @Override
    protected void onResumeFragments() {
        mAllowCommit = true;
        super.onResumeFragments();
    }

    public boolean allowFragmentCommit() {
        return mAllowCommit;
    } 

    public void callbackOnEvent() {
        if (allowFragmentCommit()){
            getFragmentManager().beginTransaction().add(new YourFragment(), TAG).commit();
        }
    }
}
_

トランザクションインジケータを許可するものとしてonResumeFragment()を選択する理由については、ref this good blog を参照してください。有名なIllegalStateExceptionについて詳しく説明しています。

13
peacepassion

RuntimeExceptions(およびIllegalStateExceptionはその1つです)は、ほとんどの場合、コードが何かを達成しようとした方法が正しくないことを意味します。そのような例外を(たとえば、キャッチすることによって)処理しようとすることも間違っています。コードは、Androidがこのような例外をスローするような方法で動作することはありません。

ハンドラーを使用し、将来処理されるメッセージを投稿または送信する場合、再開状態から出る前にキューをクリアする必要があります。また、アクティビティからAsyncTaskを開始し、onPostExecuteでトランザクションをコミットすることはできません。ユーザーがアクティビティから戻ることができるためです。キャンセルして、キャンセルされたかどうかを確認する必要があります。

これらのような多くの例がありますが、すべては「一時的な」メモリリークが原因です。

基本的にあなたのコードは悪いものであり、それを提供しなければ、その方法を伝えることは不可能です。

0
MaciejGórski

これらのメソッドOnCreate、OnResumeFragments、OnPostResumeでのみcommitメソッドを呼び出す必要があります。あなたがここで読むことができる詳細 フラグメントトランザクションとアクティビティ状態の損失

0
Abbath

最新のisStateSaved()FragmentManagerメソッドを活用するKotlinの例を次に示します。 https://proandroiddev.com/kotlin-extensions-to-commit-fragments-safely-de06218a1f4

0
worked

ft.commitAllowingStateLose()は、この状況での最善の策です。ただし、前述のとおり、状態が維持される保証はありません。

0
astryk