web-dev-qa-db-ja.com

IllegalStateException:ViewPagerを指定したonSaveInstanceStateの後にこのアクションを実行できない

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

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と関係がある。 stacktraceは私自身のクラスのどれも表示しないので、私はこの例外がどこで発生するのか、そしてどのようにそれを防ぐのかわかりません。

レコードの場合:私はタブホストを持っていて、各タブにはアクティビティーを切り替えるActivityGroupがあります。

429
nhaarman

私の答えをチェックしてください ここ 。基本的に私はただしなければならなかった:

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

saveInstanceStateメソッドでsuper()を呼び出さないでください。これは混乱していました...

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

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

@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を追加または実行するとき。

670
Ovidiu Latcu

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

このメソッド呼び出しは、popBackStackと同様に、セッション状態がすでに保存されている場合、 always 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;
    }
}
113
Synesso

フラグメントを表示する前にアクティビティ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();
}
53
Naskov

2017年10月です。GoogleはAndroidサポートライブラリを作成し、ライフサイクルコンポーネントと呼ばれる新しいものを追加しました。この「onSaveInstanceStateの後にこのアクションを実行できません」という問題に対する新しいアイデアを提供します。

要するに:

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

Explain付きのより長いバージョン:

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

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

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

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

  • これを修正する方法は?

    • commitAllowingStateLoss()メソッドを使用してフラグメントをロードする必要がありますか? しないでください;

    • onSaveInstanceStateメソッドをオーバーライドする必要がありますが、その中のsuperメソッドを無視しますか? しないでください;

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

  • Lifecycle コンポーネントができることを見てみましょう。

    基本的に、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)メソッドに行きます。今、私はそれが期待したように動作するはずだと思います。

21
Anthonyeef

これはこの問題に対する別の解決策です。

プライベートメンバー変数を使用すると、返されたデータをインテントとして設定できます。そのインテントは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;
    }
}
21
Jed

短くて有効な解決策:

簡単な手順に従う

ステップ

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

 @Override
public void onSaveInstanceState( Bundle outState ) {

}  

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

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

16
Vinayak

_ beware _ transaction.commitAllowingStateLoss()を使用すると、ユーザーにとって悪い経験になる可能性があります。この例外がスローされる理由の詳細については、 この記事 を参照してください。

12
Eric Brandwein

私はこの種の問題に対する汚い解決策を見つけました。何らかの理由でActivityGroupsを保持したい場合(時間制限の理由があります)

public void onBackPressed() {}

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

10
saberrider

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

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

トランザクションがparentFragmentのChildFragmentManagerで発生した場合は、代わりに parentFragment.isResume() outsideを使用して確認してください。

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

私は同様の問題を抱えていました、シナリオは以下のようでした:

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

activity 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
Mina Samy

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

3
DCS

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

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

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

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;
    }
}
2
anoopbryan2

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

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

例えばisChangingConfigurations()を確認してください

すなわち.

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

チェックアウト これ リンクも

2
Amol Desai

@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
MorZa

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

popMyFragmentAndMoveOn();

これに:

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

私の場合は助けた。

1
mojuba

この問題を回避するために、Google I/O 2018で導入された ナビゲーションアーキテクチャコンポーネント を使用できます。ナビゲーションアーキテクチャコンポーネントを使用すると、Androidアプリでのナビゲーションの実装が簡単になります。

1
Levon Petrosyan

私の場合は、onActivityResultというオーバーライドメソッドでこのエラーが発生しました。掘り下げた後で私はちょうど私が前に ' super 'を呼び出す必要があったかもしれないことを理解する。
追加しましたが、うまくいった

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

}

たぶんあなたはあなたのコードの前にあなたがしているどんな上書きにも 'super'を追加する必要があるだけかもしれません。

1
Gian Gomen

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

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
sharath kumar

提供: IllegalStateExceptionの解決策

この問題は私を悩ませてきましたが、幸いなことに具体的な解決策を思いついたのです。それの詳しい説明は ここ です。

CommitAllowStateloss()を使用すると、この例外を防ぐことはできますが、UIの不規則性が生じる可能性があります。これは簡単にできます

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;
 }
}
1
IrshadKumail

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

0
Ivo Stoyanov

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

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

Crashlyticsはまだ私にこの奇妙なエラーメッセージを送っています。

しかし、現在バージョン7以降でのみエラーが発生しています。(Nougat)fragmentTransactionでcommit()の代わりに commitAllowingStateLoss() を使用することで解決しました。

この post はcommitAllowingStateLoss()に役立ち、二度とフラグメントの問題はありませんでした。

要約すると、ここで承認された答えはNougat Androidの以前のバージョンで機能する可能性があります。

これにより、検索にかかる時間を数時間節約できます。幸せなコーディング。 <3歓声

0
ralphgabb

状態を保存する場合は、popup()の代わりにremove()を使用してください。

   private void removeFragment() {
    FragmentManager fragmentManager = getSupportFragmentManager();
    if (fragmentManager.isStateSaved()) {
        List<Fragment> fragments = fragmentManager.getFragments();
        if (fragments != null && !fragments.isEmpty()) {
            fragmentManager.beginTransaction().remove(fragments.get(fragments.size() - 1)).commitAllowingStateLoss();
        }
    }
}
0
kim

Kotlin拡張

fun FragmentManager?.replaceAndAddToBackStack(
    @IdRes containerViewId: Int,
    fragment: () -> Fragment,
    tag: String
) {
    // Find and synchronously remove a fragment with the same tag.
    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.
        addToBackStack(null)
    }?.commitAllowingStateLoss()
}

使用法:

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

私はまったく同じ問題を抱えていました。それは前の活動の破壊のために起こりました。以前の活動を後押ししたとき、それは破壊されました。基本活動にする(間違っている)

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    SpinnerCustom2.setFragmentManager(getSupportFragmentManager());
    onCreateDrawerActivity(savedInstanceState);
}

それは正しかったonStartに入れました

@Override
protected void onStart() {
    super.onStart();
    SpinnerCustom2.setFragmentManager(getSupportFragmentManager());

}
0
Samet öztoprak

別の考えられる回避策、すべての場合に役立つかどうかわからない(Origin here

@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

PopBackStack()またはpopBackStackImmediate()メソッドでクラッシュした場合は、fixtを試してみてください。

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

これは私のためにも機能します。

0
Tapa Save

基本フラグメントを作成し、それを自分のアプリ内のすべてのフラグメントに拡張させることにしました

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
Ahmad El-Melegy

例外はここで投げられます(In FragmentActivity):

@Override
public void onBackPressed() {
    if (!mFragments.getSupportFragmentManager().popBackStackImmediate()) {
        super.onBackPressed();
    }
}

FragmentManager.popBackStatckImmediate()では、FragmentManager.checkStateLoss()が最初に呼び出されます。それがIllegalStateExceptionの原因です。下記の実装を参照してください。

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

この問題を解決するには、単純にフラグを使ってActivityの現在のステータスをマークします。これが私の解決策です:

public class MainActivity extends AppCompatActivity {
    /**
     * A flag that marks whether current Activity has saved its instance state
     */
    private boolean mHasSaveInstanceState;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
    }

    @Override
    protected void onSaveInstanceState(Bundle outState) {
        mHasSaveInstanceState = true;
        super.onSaveInstanceState(outState);
    }

    @Override
    protected void onResume() {
        super.onResume();
        mHasSaveInstanceState = false;
    }

    @Override
    public void onBackPressed() {
        if (!mHasSaveInstanceState) {
            // avoid FragmentManager.checkStateLoss()'s throwing IllegalStateException
            super.onBackPressed();
        }
    }
}
0
Frost Lau

@ Gian Gomen私の場合、SUPERが問題を解決します。それは問題を解決するのでそれを隠さないでください、commitAllowingStateLoss()よりも正しい解決策のようです。

@Override
public void onRequestPermissionsResult(
     final int requestCode,
     @NonNull final String[] permissions, 
     @NonNull final int[] grantResults
) {
        super.onRequestPermissionsResult(requestCode,permissions, grantResults); //<--- Without this line crash 
        switch (requestCode) {
            case Constants.REQUEST_CODE_PERMISSION_STORAGE:
                if (grantResults[0] == PackageManager.PERMISSION_GRANTED) {
                    onPermissionGranted(Constants.REQUEST_CODE_PERMISSION_STORAGE);
                }
                break;
        }

0
no_cola

私はまたこの問題を経験しました、そしてあなたのFragmentActivityのコンテキストが変更されるたびに問題が発生します(例えばスクリーンの向きが変更されるなど)。だからそれのための最善の解決策はあなたのFragmentActivityからコンテキストを更新することです。

0
Adam

これをあなたの活動に追加してください

@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
yifan

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

CommitNowを呼び出すことに続いてexecutePendingTransactions()を呼び出すことが望ましいのは、それが望ましい振る舞いであるかどうかにかかわらず、現在保留中のすべてのトランザクションをコミットしようとするという副作用があるためです。