546

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

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 と関係があるようです。スタックトレースには自分のクラスが表示されないため、この例外がどこで発生し、それを防ぐ方法がわかりません。

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

4

34 に答える 34

763

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

@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

于 2012-04-21T17:43:55.327 に答える
139

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

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

  • への呼び出しを削除してsuper.onSaveInstanceStateも役に立ちません。
  • Fragment を作成して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;
    }
}
于 2015-01-09T04:55:56.183 に答える
21

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

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

手順

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

 @Override
public void onSaveInstanceState( Bundle outState ) {

}  

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

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

于 2014-04-08T13:24:30.610 に答える
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;
    }
}
于 2012-10-16T05:44:06.900 に答える
10

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

public void onBackPressed() {}

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

于 2012-02-21T13:10:59.150 に答える
5

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

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

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

于 2018-11-30T22:23:24.263 に答える
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);

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

于 2015-07-18T12:28:37.243 に答える
3

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

于 2014-07-14T11:35:34.637 に答える
2

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

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

例: チェックisChangingConfigurations()

すなわち

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

こちらのリンクもチェック

于 2015-08-13T07:40:21.353 に答える
2

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

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

これは私にとってもうまくいきます。

于 2018-12-12T09:18:09.927 に答える
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

于 2017-12-15T17:03:48.877 に答える
0

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

于 2019-02-13T13:50:27.057 に答える