363

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

32 に答える 32

465

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

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ブログ:Fragment Transactions&ActivityStateLossも読む必要があります。

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

  • ハニカム前およびハニカム後のトランザクションcommit()絶対に行わないでくださいonPause()onStop()
  • Activityライフサイクルメソッド内でトランザクションをコミットするときは注意してください。を使用onCreate()onResumeFragments()onPostResume()
  • 非同期コールバックメソッド内でトランザクションを実行しないでください
  • commitAllowingStateLoss()最後の手段としてのみ使用してください
于 2012-04-21T17:41:52.690 に答える
76

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

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

良い方法

何よりもまず、 AlexLockwoodの記事を宣伝します。次に、私がこれまでに行ったことから:

  1. 状態情報を保持する必要のないフラグメントおよびアクティビティについては、commitAllowStateLossを呼び出します。ドキュメントからの抜粋:

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

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

推奨されない方法:

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

  2. オーバーライドonBackPressedして、そこでのみ呼び出しますfinish()。アプリケーションがFragmentsAPIを使用しない場合、これは問題ないはずです。のように。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);
    }
}

乾杯!

于 2012-12-21T09:21:03.700 に答える
38

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

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

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

于 2011-11-09T00:50:17.357 に答える
27

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

于 2013-09-16T09:20:26.690 に答える
17

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

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

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

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

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

fragmentTransaction.commitAllowingStateLoss();
于 2014-09-05T14:26:48.180 に答える
6

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

  1. つまり、onSaveInstanceState() はすでに呼び出されています (ハニカム前とハニカム後の両方のデバイス)。
  2. 結果が得られた場合、フラグメントを表示/非表示にするトランザクションを作成したため、この IllegalStateException が発生しました。

私が作ったものは次のとおりです。

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

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

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

@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);
} 
于 2012-08-21T14:23:36.063 に答える
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);
    }

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

この問題はめったに発生しません。この問題に遭遇したことはありません。問題が再発することはありません。

問題 25517を見つけました

以下の場合に発生した可能性があります

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

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

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

@Override
public void onBackPressed() {

    try{
        super.onBackPressed();
    }catch (IllegalStateException e){
        // can output some information here
        finish();
    }
}
于 2016-08-16T10:28:34.320 に答える
4

IllegalStateException を取得するという同じ問題がありましたが、commit() へのすべての呼び出しを commitAllowingStateLoss() に置き換えても役に立ちませんでした。

原因は DialogFragment.show() の呼び出しでした。

で囲みます

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

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

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

面白いことに、ユーザーはアプリを離れることはありません。代わりに、AdMob のインタースティシャル広告が表示されました。

于 2015-04-09T07:40:25.450 に答える
3

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

このユーザーが画面を回転させてから戻るボタンを押した可能性があります (このユーザーがアプリの使用中に携帯電話をいじった可能性もあるため)

于 2011-12-26T22:22:44.673 に答える
2

http://chris-alexander.co.uk/on-engineering/dev/android-fragments-within-fragments/を読む

論文。fragment.isResumed() チェックは、onSaveInstanceState メソッドを使用せずに onDestroyView で役立ちます。

于 2013-04-18T09:24:02.280 に答える
0

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

于 2016-09-02T12:18:12.200 に答える
0

さて、上記のすべてのソリューションを成功させずに試した後 (基本的にトランザクションがないため)。

私の場合、AlertDialogs と ProgressDialog をフラグメントとして使用していましたが、回転時に FragmentManager を要求するとエラーが発生することがあります。

いくつかの同様の投稿を混在させる回避策を見つけました:

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

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();
}
于 2013-09-19T20:27:06.110 に答える
0

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

于 2014-07-14T11:44:15.553 に答える
0

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

于 2017-04-24T14:40:09.800 に答える
0

少し調べた後、この問題の解決策は、onresume でフラグメントのコミットを行うことです。

ソース: https://wenchaojames.wordpress.com/2013/01/12/illegalstateexception-from-onactivityresult/

于 2015-03-06T10:14:32.460 に答える
-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://joryliu.blogspot.com/2014/09/illegalstateexception-can-not-perform.html

于 2014-09-04T07:38:04.740 に答える
-2

FragmentActivity.onStateNotSaved()これらの操作の前に呼び出すことが、今のところ最良の選択肢になると思います。

于 2017-05-24T19:22:55.963 に答える