IllegalStateException:ViewPagerでonSaveInstanceStateの後にこのアクションを実行できません


496

市場のアプリからユーザーレポートを取得していますが、次の例外があります。

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.onKeyUp(Activity.java:2044)
at android.view.KeyEvent.dispatch(KeyEvent.java:2529)
at android.app.Activity.dispatchKeyEvent(Activity.java:2274)
at com.android.internal.policy.impl.PhoneWindow$DecorView.dispatchKeyEvent(PhoneWindow.java:1803)
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:1855)
at com.android.internal.policy.impl.PhoneWindow.superDispatchKeyEvent(PhoneWindow.java:1277)
at android.app.Activity.dispatchKeyEvent(Activity.java:2269)
at com.android.internal.policy.impl.PhoneWindow$DecorView.dispatchKeyEvent(PhoneWindow.java:1803)
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.widget.TabHost.dispatchKeyEvent(TabHost.java:297)
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:1855)
at com.android.internal.policy.impl.PhoneWindow.superDispatchKeyEvent(PhoneWindow.java:1277)
at android.app.Activity.dispatchKeyEvent(Activity.java:2269)
at com.android.internal.policy.impl.PhoneWindow$DecorView.dispatchKeyEvent(PhoneWindow.java:1803)
at android.view.ViewRoot.deliverKeyEventPostIme(ViewRoot.java:2880)
at android.view.ViewRoot.handleFinishedEvent(ViewRoot.java:2853)
at android.view.ViewRoot.handleMessage(ViewRoot.java:2028)
at android.os.Handler.dispatchMessage(Handler.java:99)
at android.os.Looper.loop(Looper.java:132)
at android.app.ActivityThread.main(ActivityThread.java:4028)
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:844)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:602)
at dalvik.system.NativeStart.main(Native Method)

どうやらそれは私が使用しないFragmentManagerと関係があるようです。スタックトレースは自分のクラスを表示しないので、この例外が発生する場所とそれを防ぐ方法はわかりません。

記録のために:私はタブホストを持っており、各タブにはアクティビティを切り替えるActivityGroupがあります。


2
私はどちらか...同じ問題を議論し、この質問が、そこには解決策を見つけたstackoverflow.com/questions/7469082/...
nhaarman

3
を使用していませんがFragmentManager、Honeycombは確かに使用しています。これは実際のHoneycombタブレットで起こっていますか?または、誰かがハッキングされたHoneycombを電話などで実行していて、ハッキングされたエディションが問題を抱えているのでしょうか?
CommonsWare 2011

1
何も思いつきません。これは私が市場のデベロッパーコンソールで取得する情報のみで、ユーザメッセージは、有用な情報のいずれか..含まれていません
nhaarman

私はFlurryを使用しています。これは、Android 3.0.1との11のセッションを示しており、この例外について11のレポートがあります。偶然かもしれません。Android 3.1と3.2には、それぞれ56セッションと38セッションがあります。
nhaarman '27

市場のエラーレポートは、「プラットフォーム」セクションがあり、時にはそれはそれで、デバイスのAndroidのバージョンがあります。
ニコライ・エレンコフ

回答:


720

私の答えをここで確認してください。基本的に私はちょうどしなければならなかった:

@Override
protected void onSaveInstanceState(Bundle outState) {
    //No call for super(). Bug on API Level > 11.
}

電話をしないでくださいsuper()上のsaveInstanceState方法。これはめちゃくちゃでした...

これは、サポートパッケージの既知のバグです。

インスタンスを保存して何かを追加するoutState Bundle必要がある場合は、以下を使用できます。

@Override
protected void onSaveInstanceState(Bundle outState) {
    outState.putString("WORKAROUND_FOR_BUG_19917_KEY", "WORKAROUND_FOR_BUG_19917_VALUE");
    super.onSaveInstanceState(outState);
}

最後に、適切な解決策は(コメントで見られるように)使用することでした:

transaction.commitAllowingStateLoss();

追加または実行するFragmentTransactionと、それが原因でしたException


370
commit()ではなくcommitAllowingStateLoss()を使用する必要があります
meh

18
commitAllowingStateLoss()に関するこのコメントは、それ自体が回答です。そのように投稿する必要があります。
Risadinha 2013年

20
'commitAllowingStateLoss'について-/>「アクティビティを後で状態から復元する必要がある場合にコミットが失われる可能性があるため、これは危険です。UIの状態が予期せずに変化しても問題がない場合にのみ使用してください。ユーザー。"
2013

10
popBackStackImmediate状態が保存されている場合、v4ソースを確認するとすぐに失敗します。以前にフラグメントを追加してcommitAllowingStateLossも何の役割も果たしません。私のテストでは、これが正しいことを示しています。この特定の例外には影響しません。必要なのはpopBackStackImmediateAllowingStateLossメソッドです。
Synesso、2015年

3
@DanieleBはい、ここに回答を投稿しました。しかし、私は実際にOttoメッセージバスを使用することでさらに優れた解決策を見つけました。フラグメントをサブスクライバーとして登録し、バスからの非同期結果をリッスンします。一時停止時に登録解除し、再開時に再登録します。非同期には、完了してフラグメントが一時停止するときのためにProduceメソッドも必要です。時間があるときは、これについてより詳細に回答を更新します。
Synesso、2015年

130

同様のエラーメッセージで多くの関連する問題があります。この特定のスタックトレースの2行目を確認します。この例外は、特にへの呼び出しに関連していますFragmentManagerImpl.popBackStackImmediate

このメソッド呼び出しは、セッション状態がすでに保存されている場合popBackStack、と同様に常に失敗しIllegalStateExceptionます。ソースを確認してください。この例外のスローを停止するためにできることはありません。

  • への呼び出しを削除してsuper.onSaveInstanceStateも役に立ちません。
  • でフラグメントを作成してcommitAllowingStateLossも役に立ちません。

これが私が問題を観察した方法です:

  • 送信ボタンのあるフォームがあります。
  • ボタンをクリックすると、ダイアログが作成され、非同期プロセスが開始されます。
  • ユーザーは、プロセスが完了する前にホームキーをクリックします- onSaveInstanceState呼び出されます。
  • プロセスが完了し、コールバックが行われてpopBackStackImmediate試行されます。
  • IllegalStateException スローされます。

これを解決するために私がしたことは次のとおりです:

IllegalStateExceptionコールバックを回避することはできないため、キャッチして無視します。

try {
    activity.getSupportFragmentManager().popBackStackImmediate(name);
} catch (IllegalStateException ignored) {
    // There's no way to avoid getting this if saveInstanceState has already been called.
}

これで、アプリのクラッシュを防ぐことができます。しかし今、ユーザーはアプリを復元し、彼らが押したと思ったボタンがまったく押されていないことを確認します(彼らは考えています)。フォームフラグメントはまだ表示されています!

これを修正するには、ダイアログが作成されたときに、プロセスが開始されたことを示す状態を作成します。

progressDialog.show(fragmentManager, TAG);
submitPressed = true;

この状態をバンドルに保存します。

@Override
public void onSaveInstanceState(Bundle outState) {
    ...
    outState.putBoolean(SUBMIT_PRESSED, submitPressed);
}

再度ロードすることを忘れないでください onViewCreated

次に、再開時に、以前に送信が試行された場合はフラグメントをロールバックします。これにより、ユーザーが未送信のフォームのように見えるのを防ぐことができます。

@Override
public void onResume() {
    super.onResume();
    if (submitPressed) {
        // no need to try-catch this, because we are not in a callback
        activity.getSupportFragmentManager().popBackStackImmediate(name);
        submitPressed = false;
    }
}

5
これについての興味深い読み物:androiddesignpatterns.com/2013/08/…–
Pascal

DialogFragmentを使用する場合は、ここで代替手段を作成しました:github.com/AndroidDeveloperLB/DialogShard
android developer

popBackStackImmediateAndroid自体から呼び出された場合はどうなりますか?
Kimi Chiu 2016年

1
絶対に素晴らしい。これは受け入れられる答えになるはずです。どうもありがとうございました!多分私はsubmitPressed = falseを追加します; popBackStackInmediateの後。
ネオニグマ

public void onSaveInstanceState(Bundle outState)メソッドを使用しませんでした。public void onSaveInstanceState(Bundle outState)に空のメソッドを設定する必要がありますか?
Faxriddin Abdullayev

55

isFinishing()フラグメントを表示する前にアクティビティを確認し、に注意してくださいcommitAllowingStateLoss()

例:

if(!isFinishing()) {
FragmentManager fm = getSupportFragmentManager();
            FragmentTransaction ft = fm.beginTransaction();
            DummyFragment dummyFragment = DummyFragment.newInstance();
            ft.add(R.id.dummy_fragment_layout, dummyFragment);
            ft.commitAllowingStateLoss();
}

1
!isFinishing()&&!isDestroyed()は機能しません。
Allen Vork 2018

!isFinishing()&&!isDestroyed()は機能しましたが、API 17が必要ですが、単にDialogFragment。他の優れたソリューションについては、stackoverflow.com / questions / 15729138 / …を参照してください。stackoverflow.com/ a / 41813953/2914140が役立ちました。
CoolMind 2019

29

2017年10月、Googleはライフサイクルコンポーネントと呼ばれる新しい機能を備えたAndroidサポートライブラリを作成します。これは、この「onSaveInstanceStateの後でこのアクションを実行できない」問題に関するいくつかの新しいアイデアを提供します。

要するに:

  • ライフサイクルコンポーネントを使用して、フラグメントをポップアップする適切なタイミングかどうかを判断します。

説明付きの長いバージョン:

  • なぜこの問題が出てくるのですか?

    それはあなたがFragmentManagerあなたの活動からフラグメントをトランザクションをコミットするために使用しようとしているからです(これは私のフラグメントを保持するつもりですか?)。通常これは、ホストアクティビティがすでにsavedInstanceStateメソッドを呼び出している間、次のフラグメントに対してトランザクションを実行しようとしているように見えます(ユーザーがホームボタンにタッチして、アクティビティがを呼び出すことがありますonStop()。私の場合はそれが理由です)。

    通常、この問題は発生しませんonCreate()。メソッドが最適な場所であるように、最初から常にフラグメントをアクティビティにロードしようとします。ただし、これが発生することもあります。特に、そのアクティビティにロードするフラグメントを決定できない場合、またはAsyncTaskブロックからフラグメントをロードしようとしている場合(または少し時間がかかる場合)です。フラグメントトランザクションが実際に発生するまでの時間ですが、アクティビティのonCreate()メソッドの後、ユーザーは何でも実行できます。ユーザーがホームボタンを押すと、アクティビティのonSavedInstanceState()メソッドがトリガーされ、can not perform this actionクラッシュします。

    この問題の詳細を知りたい場合は、このブログ投稿をご覧になることをお勧めします。それはソースコード層の奥深くに見え、それについてたくさん説明しています。また、それはあなたがcommitAllowingStateLoss()このクラッシュを回避するためにメソッドを使用すべきではない理由を与えます(あなたのコードに何も良いものを提供しないと信じてください)

  • これを修正するには?

    • commitAllowingStateLoss()メソッドを使用してフラグメントをロードする必要がありますか?いいえ、すべきではありません

    • onSaveInstanceStateメソッドをオーバーライドし、superその中のメソッドを無視する必要がありますか?いいえ、すべきではありません

    • 魔法のisFinishing内部アクティビティを使用して、ホストアクティビティがフラグメントトランザクションの適切なタイミングにあるかどうかを確認する必要がありますか?ええ、これ正しい方法のよう見えます。

  • ライフサイクルコンポーネントが実行できることを見てみましょう。

    基本的に、GoogleはAppCompatActivityクラス(およびプロジェクトで使用する必要がある他のいくつかの基本クラス)内にいくつかの実装を作成します。これにより、現在のライフサイクル状態を簡単に判別できます。私たちの問題を振り返ってみましょう:なぜこの問題が発生するのでしょうか?間違ったタイミングで何かをするからです。だから私たちはそれをしないように努め、そしてこの問題はなくなるでしょう。

    私は自分のプロジェクト用に少しコーディングしていますが、これは私が使用しているものLifeCycleです。Kotlinでコーディングします。

val hostActivity: AppCompatActivity? = null // the activity to host fragments. It's value should be properly initialized.

fun dispatchFragment(frag: Fragment) {
    hostActivity?.let {
       if(it.lifecyclecurrentState.isAtLeast(Lifecycle.State.RESUMED)){
           showFragment(frag)
       }
    }
}

private fun showFragment(frag: Fragment) {
    hostActivity?.let {
        Transaction.begin(it, R.id.frag_container)
                .show(frag)
                .commit()
    }

上で示したように。ホストアクティビティのライフサイクル状態を確認します。サポートライブラリ内のライフサイクルコンポーネントを使用すると、より具体的になる可能性があります。コードlifecyclecurrentState.isAtLeast(Lifecycle.State.RESUMED)は、現在の状態が少なくともであるonResume場合、それ以降ではないことを意味しますか?これにより、他のライフステート(などonStop)でメソッドが実行されなくなります。

  • すべて完了しましたか?

    もちろん違います。私が示したコードは、アプリケーションがクラッシュするのを防ぐための新しい方法を示しています。しかし、それがの状態になったonStop場合、そのコード行は何も実行しないため、画面には何も表示されません。ユーザーがアプリケーションに戻ると、空の画面が表示されます。これは、フラグメントをまったく示さない空のホストアクティビティです。それは悪い経験です(ええ、クラッシュより少しいいです)。

    ですから、ここでもっと良いものがあればいいのにと思っonResumeています。さらに、ユーザーがアプリに戻った後、アクティビティはフラグメントトランザクションアクションの終了を継続しようとします。

    このメソッドにさらに何かを追加します。

class FragmentDispatcher(_host: FragmentActivity) : LifecycleObserver {
    private val hostActivity: FragmentActivity? = _host
    private val lifeCycle: Lifecycle? = _host.lifecycle
    private val profilePendingList = mutableListOf<BaseFragment>()

    @OnLifecycleEvent(Lifecycle.Event.ON_RESUME)
    fun resume() {
        if (profilePendingList.isNotEmpty()) {
            showFragment(profilePendingList.last())
        }
    }

    fun dispatcherFragment(frag: BaseFragment) {
        if (lifeCycle?.currentState?.isAtLeast(Lifecycle.State.RESUMED) == true) {
            showFragment(frag)
        } else {
            profilePendingList.clear()
            profilePendingList.add(frag)
        }
    }

    private fun showFragment(frag: BaseFragment) {
        hostActivity?.let {
            Transaction.begin(it, R.id.frag_container)
                    .show(frag)
                    .commit()
        }
    }
}

私はこのdispatcherクラス内にリストを保持しています。これらのフラグメントを格納するために、トランザクションアクションを完了する機会がありません。そして、ユーザーがホーム画面から戻ってきて、起動するのを待っているフラグメントがあることに気づくとresume()@OnLifecycleEvent(Lifecycle.Event.ON_RESUME)アノテーションの下のメソッドに行きます。これで、期待どおりに機能するはずです。


8
Kotlinの代わりにJavaを使用するとよいでしょう
Shchvova

1
FragmentDispatcherフラグメントの復元が1つしかない場合、実装でリストを使用して保留中のフラグメントを保存するのはなぜですか?
fraherm 2018

21

この問題の別の解決策を次に示します。

プライベートメンバー変数を使用すると、返されたデータをインテントとして設定し、super.onResume();の後に処理できます。

そのようです:

private Intent mOnActivityResultIntent = null; 

@Override
protected void onResume() {
    super.onResume();
    if(mOnActivityResultIntent != null){
        ... do things ...
        mOnActivityResultIntent = null;
    }
 }

@Override
public void onActivityResult(int requestCode, int resultCode, Intent data){
    if(data != null){
        mOnActivityResultIntent = data;
    }
}

7
許可されていなかったアクションによっては、onResume()よりさらに後の時点に移動する必要がある場合があります。insatcneの場合、FragmentTransaction.commit()が問題の場合、これは代わりにonPostResume()に入る必要があります。
pjv

1
これがこの質問に対する私の答えです。受け取ったNFCタグを前のアクティビティに転送する必要があったので、これが私のためにそれを行いました。
Janis Peisenieks 2013

7
私には電話をしていないので、それは起こっていましたsuper.onActivityResult()
スーフィアン2015

20

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

簡単な手順に従う

手順

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

 @Override
public void onSaveInstanceState( Bundle outState ) {

}  

ステップ2:使用 fragmentTransaction.commitAllowingStateLoss( );

fragmentTransaction.commit( ); whileフラグメント操作の代わりに 。


回答は、いくつかの試行錯誤によって得られた私の実用的なソリューションによって人々に役立つように投稿された他の場所でコピーまたは参照されたフォームではありません
Vinayak

12

注意:を使用transaction.commitAllowingStateLoss()すると、ユーザーのエクスペリエンスが低下する可能性があります。この例外がスローされる理由の詳細については、この投稿を参照してください。


6
これは質問に対する回答を提供していません。質問に対して有効な回答を提供する必要があります
Umar Ata

10

私はこの種の問題に対する汚い解決策を見つけました。ActivityGroups何らかの理由(時間制限の理由があった)のために引き続き自分を維持したい場合は、

public void onBackPressed() {}

あなたの中Activityでいくつかのbackコードを実行します。古いデバイスにそのようなメソッドがない場合でも、このメソッドは新しいデバイスによって呼び出されます。


6

commitAllowingStateLoss()は使用しないでください。ユーザーでUIの状態が予期せず変化しても問題がない場合にのみ使用してください。

https://developer.android.com/reference/android/app/FragmentTransaction.html#commitAllowingStateLoss()

トランザクションがparentFragmentのChildFragmentManagerで発生する場合、代わりにparentFragment.isResume()を外部で使用 してチェックします。

if (parentFragment.isResume()) {
    DummyFragment dummyFragment = DummyFragment.newInstance();
    transaction = childFragmentManager.BeginTransaction();
    trans.Replace(Resource.Id.fragmentContainer, startFragment);
}

5

私にも同様の問題があり、シナリオは次のようでした:

  • 私の活動はリストのフラグメントを追加/置換しています。
  • 各リストフラグメントにはアクティビティへの参照があり、リストアイテムがクリックされたときにアクティビティに通知します(オブザーバーパターン)。
  • 各リストフラグメントはsetRetainInstance(true)を呼び出します。その中のonCreateメソッド。

アクティビティonCreateメソッドは次のとおりです。

mMainFragment = (SelectionFragment) getSupportFragmentManager()
                .findFragmentByTag(MAIN_FRAGMENT_TAG);
        if (mMainFragment == null) {
            mMainFragment = new SelectionFragment();

            mMainFragment.setListAdapter(new ArrayAdapter<String>(this,
                    R.layout.item_main_menu, getResources().getStringArray(
                            R.array.main_menu)));
mMainFragment.setOnSelectionChangedListener(this);
            FragmentTransaction transaction = getSupportFragmentManager()
                    .beginTransaction();
            transaction.add(R.id.content, mMainFragment, MAIN_FRAGMENT_TAG);
            transaction.commit();
        }

例外がスローされたのは、構成が変更された(デバイスが回転した)ときにアクティビティが作成され、メインフラグメントがフラグメントマネージャーの履歴から取得され、同時にフラグメントには破棄されたアクティビティへのOLD参照があるためです。

実装をこれに変更することで問題は解決しました:

mMainFragment = (SelectionFragment) getSupportFragmentManager()
                .findFragmentByTag(MAIN_FRAGMENT_TAG);
        if (mMainFragment == null) {
            mMainFragment = new SelectionFragment();

            mMainFragment.setListAdapter(new ArrayAdapter<String>(this,
                    R.layout.item_main_menu, getResources().getStringArray(
                            R.array.main_menu)));
            FragmentTransaction transaction = getSupportFragmentManager()
                    .beginTransaction();
            transaction.add(R.id.content, mMainFragment, MAIN_FRAGMENT_TAG);
            transaction.commit();
        }
        mMainFragment.setOnSelectionChangedListener(this);

フラグメントがアクティビティの古い破棄されたインスタンスへの参照を持っている状況を回避するために、アクティビティが作成されるたびにリスナーを設定する必要があります。


5

から継承する場合はFragmentActivity、次の場所でスーパークラスを呼び出す必要がありますonActivityResult()

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

これを行わず、そのメソッドでフラグメントダイアログボックスを表示しようとすると、OPが表示される場合がありますIllegalStateException。(正直なところ、スーパーコールで問題が修正される理由がよくわかりません。onActivityResult()がの前onResume()に呼び出されるため、フラグメントダイアログボックスを表示することはできません。)


1
これが問題を解決する理由を知りたいです。
Big McLargeHuge

3

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


2

使用することtransaction.commitAllowingStateLoss();は最善の解決策ではないと思います。この例外は、アクティビティの構成が変更され、フラグメントonSavedInstanceState()が呼び出された後、非同期コールバックメソッドがフラグメントをコミットしようとしたときにスローされます。

簡単な解決策は、アクティビティが構成を変更しているかどうかを確認することです

たとえばチェック isChangingConfigurations()

すなわち

if(!isChangingConfigurations()) { //commit transaction. }

このリンクもチェックアウトしください


どういうわけか、ユーザーが何かをクリックすると、この例外が発生しました(クリックはtransaction-commitを実行するためのトリガーです)。これはどうやってできるの?ここであなたの解決策はありますか?
Android開発者、

@androiddeveloperユーザーのクリックで他に何をしていますか。トランザクションをコミットする前に、フラグメントが何らかの形でその状態を保存しています
Amol Desai '23 / 09/23

例外は、トランザクションコミットの正確な行でスローされました。また、私には奇妙な誤植がありました。「ここにいる」の代わりに「ここにいる」という意味でした。
Android開発者

@androiddeveloperあなたは正しいです!しかし、トランザクションをコミットする前に、バックグラウンドスレッドなどを生成していますか?
Amol Desai 2015

私はそうは思いません(オフィスにいなくてすみません)が、なぜそれが問題になるのでしょうか?これはすべてUIに関するものです...バックグラウンドスレッドで何かを作成した場合、例外が発生します。さらに、UIに関連するものをバックグラウンドスレッドに配置しないでください。リスクが高すぎるためです。
android開発者

2

おそらく、私の場合に見つけた最もスムーズで最も簡単な解決策は、アクティビティの結果に応じて、問題のあるフラグメントをスタックからポップしないようにすることでした。だから私の中でこの呼び出しを変更するonActivityResult()

popMyFragmentAndMoveOn();

これに:

new Handler(Looper.getMainLooper()).post(new Runnable() {
    public void run() {
        popMyFragmentAndMoveOn();
    }
}

私の場合に役立ちました。


2

onActivityResultでFragmentTransactionを実行している場合、何ができるかをonActivityResult内にブール値を設定してから、onResumeでブール値に基づいてFragmentTransactionを実行できます。以下のコードを参照してください。

@Override
protected void onResume() {
    super.onResume;
    if(isSwitchFragment){
        isSwitchFragment=false;
        bottomNavigationView.getTabAt(POS_FEED).select();
    }
}

@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
    if (requestCode == FilterActivity.FILTER_REQUEST_EVENT && data != null) {
        isSwitchFragment=true;
    }
}

コードを画像として投稿せず、代わりにコードのフォーマットを使用してください。
マイサー

1
はい、それは私を助けました..、これはマシュマロデバイスの正確かつ適切な解決策です。この例外が発生し、この簡単なトリックで解決しました。したがって、賛成票を投じた。
sandhya sasane

2

礼儀:IllegalStateExceptionの解決策

この問題は長い間私を悩ませてきましたが、幸いにも私はそれに対する具体的な解決策を思いつきました。詳細な説明はこちら

commitAllowStateloss()を使用すると、この例外を回避できる可能性がありますが、UIが不規則になる可能性があります。これまでのところ、アクティビティの状態が失われた後にフラグメントをコミットしようとすると、IllegalStateExceptionが発生することがわかっているため、状態が復元されるまでトランザクションを遅らせる必要があります。それはこのように簡単に行うことができます

2つのプライベートブール変数を宣言する

 public class MainActivity extends AppCompatActivity {

    //Boolean variable to mark if the transaction is safe
    private boolean isTransactionSafe;

    //Boolean variable to mark if there is any transaction pending
    private boolean isTransactionPending;

onPostResume()とonPauseで、ブール変数isTransactionSafeを設定および設定解除します。アクティビティがフォアグラウンドにあるときにのみトランザクションを安全であるとマークして、状態損失の可能性がないようにするのがアイデアです。

/*
onPostResume is called only when the activity's state is completely restored. In this we will
set our boolean variable to true. Indicating that transaction is safe now
 */
public void onPostResume(){
    super.onPostResume();
    isTransactionSafe=true;
}
/*
onPause is called just before the activity moves to background and also before onSaveInstanceState. In this
we will mark the transaction as unsafe
 */

public void onPause(){
    super.onPause();
    isTransactionSafe=false;

}

private void commitFragment(){
    if(isTransactionSafe) {
        MyFragment myFragment = new MyFragment();
        FragmentManager fragmentManager = getFragmentManager();
        FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
        fragmentTransaction.add(R.id.frame, myFragment);
        fragmentTransaction.commit();
    }
}

-これまでに行ったことでIllegalStateExceptionは保存されますが、アクティビティがバックグラウンドに移動した後でトランザクションが完了すると、commitAllowStateloss()のようなトランザクションが失われます。これを支援するために、isTransactionPendingブール変数があります。

public void onPostResume(){
   super.onPostResume();
   isTransactionSafe=true;
/* Here after the activity is restored we check if there is any transaction pending from
the last restoration
*/
   if (isTransactionPending) {
      commitFragment();
   }
}


private void commitFragment(){

 if(isTransactionSafe) {
     MyFragment myFragment = new MyFragment();
     FragmentManager fragmentManager = getFragmentManager();
     FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
     fragmentTransaction.add(R.id.frame, myFragment);
     fragmentTransaction.commit();
     isTransactionPending=false;
 }else {
     /*
     If any transaction is not done because the activity is in background. We set the
     isTransactionPending variable to true so that we can pick this up when we come back to
foreground
     */
     isTransactionPending=true;
 }
}

2

後にフラグメントトランザクションを実行しないでくださいActivity.onStop() 後にトランザクションを実行する可能性のあるコールバックがないことを確認してくださいonStop()。次のようなアプローチで問題を回避するのではなく、理由を修正することをお勧めします.commitAllowingStateLoss()


1

サポートライブラリバージョン24.0.0以降FragmentTransaction.commitNow()では、をcommit()続けて呼び出す代わりに、このトランザクションを同期的にコミットするメソッドを呼び出すことができますexecutePendingTransactions()文書化しても良く、このアプローチは言います:

commitNowの呼び出しは、commit()の後にexecutePendingTransactions()を呼び出すよりも望ましいです。後者の場合、望ましい動作であるかどうかに関係なく、現在保留中のすべてのトランザクションをコミットしようとする副作用があります。


1

アクティビティにフラグメントをロードしようとするときはいつでも、アクティビティが再開状態にあり、一時停止状態にならないことを確認してください。一時停止状態では、実行されたコミット操作が失われる可能性があります。

transaction.commit()の代わりにtransaction.commitAllowingStateLoss()を使用してフラグメントをロードできます

または

ブール値を作成し、アクティビティが一時停止しないかどうかを確認します

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

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

次に、フラグメントチェックの読み込み中に

if(mIsResumed){
//load the your fragment
}

1

この問題を回避するには、Google I / O 2018で導入されたThe Navigation Architecture Componentを使用できます。NavigationArchitecture Componentは、Androidアプリでのナビゲーションの実装を簡素化します。


それは十分ではなく、バグがあります(ディープリンクの処理が不十分で、状態の表示/非表示のフラグメントや、まだ開いている重要な問題を保存できません)
do01

1

@Anthonyeefのすばらしい回答に関して、Javaのサンプルコードを次に示します。

private boolean shouldShowFragmentInOnResume;

private void someMethodThatShowsTheFragment() {

    if (this.getLifecycle().getCurrentState().isAtLeast(Lifecycle.State.RESUMED)) {
        showFragment();
    } else {
        shouldShowFragmentInOnResume = true;
    }
}

private void showFragment() {
    //Your code here
}

@Override
protected void onResume() {
    super.onResume();

    if (shouldShowFragmentInOnResume) {
        shouldShowFragmentInOnResume = false;
        showFragment();
    }
}

1

popBackStack()またはpopBackStackImmediate()メソッドでクラッシュした場合は、次のコマンドでfixtを試してください。

        if (!fragmentManager.isStateSaved()) {
            fragmentManager.popBackStackImmediate();
        }

これも私のために働いています。


API 26以降が必要であることに注意してください
Itay Feldman '31 / 12/31

1

私の場合、onActivityResultというオーバーライドメソッドでこのエラーが発生しました。掘った後、たぶん前に「スーパー」を呼び出す必要があったのかもしれません。
私はそれを追加し、それはちょうどうまくいった

override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
    super.onActivityResult(requestCode, resultCode, data); //<--- THIS IS THE SUPPER CALL
    if (resultCode == Activity.RESULT_OK && requestCode == 0) {
        mostrarFragment(FiltroFragment.newInstance())
    }

}

たぶん、あなたはあなたのコードの前にあなたがしているすべてのオーバーライドに「スーパー」を追加する必要があるだけです。


1

Kotlin拡張

fun FragmentManager?.replaceAndAddToBackStack(
    @IdRes containerViewId: Int,
    fragment: () -> Fragment,
    tag: String
) {
    // Find and synchronously remove a fragment with the same tag.
    // The second transaction must start after the first has finished.
    this?.findFragmentByTag(tag)?.let {
        beginTransaction().remove(it).commitNow()
    }
    // Add a fragment.
    this?.beginTransaction()?.run {
        replace(containerViewId, fragment, tag)
        // The next line will add the fragment to a back stack.
        // Remove if not needed.
        // You can use null instead of tag, but tag is needed for popBackStack(), 
        // see https://stackoverflow.com/a/59158254/2914140
        addToBackStack(tag)
    }?.commitAllowingStateLoss()
}

使用法:

val fragment = { SomeFragment.newInstance(data) }
fragmentManager?.replaceAndAddToBackStack(R.id.container, fragment, SomeFragment.TAG)

()を削除できます->フラグメントの前
クレア

@クレア、ありがとう!に変更することを意味しfragment: Fragmentますか?はい、このバリアントを試しましたが、この場合はフラグメントがすべての場合に作成されます(fragmentManager == nullの場合でも、この状況は発生しませんでした)。回答を更新し、nullをのタグに変更しましたaddToBackStack()
CoolMind

1

このクラッシュは、所有するアクティビティのライフサイクルがすでにonSaveInstanceStateを実行した後にFragmentTransactionがコミットされたことが原因です。これは、非同期コールバックからFragmentTransactionsをコミットすることによって引き起こされることがよくあります。詳細については、リンクされたリソースをご覧ください。

フラグメントトランザクションとアクティビティ状態の損失

http://www.androiddesignpatterns.com/2013/08/fragment-transaction-commit-state-loss.html


0

これをアクティビティに追加してください

@Override
public void onSaveInstanceState(Bundle outState) {
    super.onSaveInstanceState(outState);
    if (outState.isEmpty()) {
        // Work-around for a pre-Android 4.2 bug
        outState.putBoolean("bug:fix", true);
    }
}

0

私もこの問題を経験しており、コンテキストFragmentActivityが変更されるたびに問題が発生します(画面の向きが変更されるなど)。したがって、それに対する最良の修正は、からコンテキストを更新することですFragmentActivity


0

私は最終的にベースフラグメントを作成し、アプリのすべてのフラグメントにそれを拡張させました

public class BaseFragment extends Fragment {

    private boolean mStateSaved;

    @CallSuper
    @Override
    public void onSaveInstanceState(Bundle outState) {
        mStateSaved = true;
        super.onSaveInstanceState(outState);
    }

    /**
     * Version of {@link #show(FragmentManager, String)} that no-ops when an IllegalStateException
     * would otherwise occur.
     */
    public void showAllowingStateLoss(FragmentManager manager, String tag) {
        // API 26 added this convenient method
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            if (manager.isStateSaved()) {
                return;
            }
        }

        if (mStateSaved) {
            return;
        }

        show(manager, tag);
    }
}

次に、showAllowingStateLoss代わりに使用するフラグメントを表示しようとするとshow

このような:

MyFragment.newInstance()
.showAllowingStateLoss(getFragmentManager(), MY_FRAGMENT.TAG);

私はこのPRからこのソリューションにたどり着きました:https : //github.com/googlesamples/easypermissions/pull/170/files


0

別の可能な回避策は、すべての場合に役立つかどうかはわかりません(ここから開始):

@Override
protected void onSaveInstanceState(Bundle outState) {
    super.onSaveInstanceState(outState);
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
        final View rootView = findViewById(android.R.id.content);
        if (rootView != null) {
            rootView.cancelPendingInputEvents();
        }
    }
}

0

@Ovidiu Latcuによる回答が承認されていることは知っていますが、しばらくしてもエラーが解消されません。

@Override
protected void onSaveInstanceState(Bundle outState) {
     //No call for super(). Bug on API Level > 11.
}

Crashlyticsから引き続きこの奇妙なエラーメッセージが送信されます。

ただし、バージョン7以降でのみエラーが発生するようになりました(Nougat)私の修正は、fragmentTransactionでcommit()ではなくcommitAllowingStateLoss()を使用することでした

この投稿はcommitAllowingStateLoss()に役立ち、フラグメントの問題が二度と発生しませんでした。

要約すると、ここで受け入れられている回答は、Nougatより前のAndroidバージョンで機能する可能性があります。

これにより、検索に数時間かかる可能性があります。幸せなコーディング。<3歓声

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