私は市場で私のアプリからユーザーレポートを取得していますが、以下の例外があります。
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があります。
私の答えをチェックしてください ここ 。基本的に私はただしなければならなかった:
@Override
protected void onSaveInstanceState(Bundle outState) {
//No call for super(). Bug on API Level > 11.
}
saveInstanceState
メソッドでsuper()
を呼び出さないでください。これは混乱していました...
これはサポートパッケージの既知の バグ です。
インスタンスを保存して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
を追加または実行するとき。
同様のエラーメッセージを持つ多くの関連問題があります。この特定のスタックトレースの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;
}
}
フラグメントを表示する前にアクティビティ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();
}
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)
メソッドに行きます。今、私はそれが期待したように動作するはずだと思います。
これはこの問題に対する別の解決策です。
プライベートメンバー変数を使用すると、返されたデータをインテントとして設定できます。そのインテントは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;
}
}
短くて有効な解決策:
簡単な手順に従う
ステップ
ステップ1:それぞれのフラグメントのonSaveInstanceState
状態をオーバーライドします。そしてそれからスーパーメソッドを削除します。
@Override
public void onSaveInstanceState( Bundle outState ) {
}
ステップ2:fragmentTransaction.commitAllowingStateLoss( );
を使う
フラグメント化操作中にfragmentTransaction.commit( );
の代わりに。
_ beware _ 、transaction.commitAllowingStateLoss()
を使用すると、ユーザーにとって悪い経験になる可能性があります。この例外がスローされる理由の詳細については、 この記事 を参照してください。
私はこの種の問題に対する汚い解決策を見つけました。何らかの理由でActivityGroups
を保持したい場合(時間制限の理由があります)
public void onBackPressed() {}
あなたのActivity
の中で、そしてそこにいくつかのback
コードをしなさい。古いデバイスにそのようなメソッドがない場合でも、このメソッドは新しいものによって呼び出されます。
CommitAllowingStateLoss()を使用しないでください。ユーザー上でUIの状態が予期せずに変更されても問題ない場合にのみ使用してください。
トランザクションがparentFragmentのChildFragmentManagerで発生した場合は、代わりに parentFragment.isResume() outsideを使用して確認してください。
if (parentFragment.isResume()) {
DummyFragment dummyFragment = DummyFragment.newInstance();
transaction = childFragmentManager.BeginTransaction();
trans.Replace(Resource.Id.fragmentContainer, startFragment);
}
私は同様の問題を抱えていました、シナリオは以下のようでした:
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);
フラグメントにアクティビティの古い破棄されたインスタンスへの参照があるという状況を回避するために、アクティビティが作成されるたびにリスナーを設定する必要があります。
マップフラグメントアクティビティでインテントチューザをキャンセルするために戻るボタンを押したときに、この例外が発生していました。私はonResumeのコードをonstart()に置き換えることでこれを解決しました。
FragmentActivity
から継承する場合は、onActivityResult()
でスーパークラスを呼び出す必要があります。
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent intent) {
super.onActivityResult(requestCode, resultCode, intent);
...
}
これを行わずにそのメソッドでフラグメントダイアログボックスを表示しようとすると、OPのIllegalStateException
が表示されることがあります。 (正直に言うと、 なぜ スーパーコールで問題が解決するのか、まったくわかりません。onActivityResult()
はonResume()
の前に呼び出されるので、フラグメントダイアログボックスを表示することはできません。)
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;
}
}
transaction.commitAllowingStateLoss();
を使うのは最善の解決策ではないと思います。この例外は、アクティビティの設定が変更され、フラグメントonSavedInstanceState()
が呼び出された後に非同期コールバックメソッドがフラグメントをコミットしようとしたときにスローされます。
簡単な解決策は、アクティビティが設定を変更しているかどうかを確認することです。
例えばisChangingConfigurations()
を確認してください
すなわち.
if(!isChangingConfigurations()) { //commit transaction. }
チェックアウト これ リンクも
@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();
}
}
おそらく、私のケースで私が見つけた最もスムーズで最も簡単な解決策は、アクティビティの結果に応じて問題のフラグメントをスタックからポップさせないようにすることでした。だから私のonActivityResult()
でこの呼び出しを変更する:
popMyFragmentAndMoveOn();
これに:
new Handler(Looper.getMainLooper()).post(new Runnable() {
public void run() {
popMyFragmentAndMoveOn();
}
}
私の場合は助けた。
この問題を回避するために、Google I/O 2018で導入された ナビゲーションアーキテクチャコンポーネント を使用できます。ナビゲーションアーキテクチャコンポーネントを使用すると、Androidアプリでのナビゲーションの実装が簡単になります。
私の場合は、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'を追加する必要があるだけかもしれません。
アクティビティにフラグメントをロードしようとしているときはいつでも、アクティビティが再開していて、一時停止状態にならないようにしてください。一時停止状態では、行われたコミット操作を失う可能性があります。
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
}
この問題は私を悩ませてきましたが、幸いなことに具体的な解決策を思いついたのです。それの詳しい説明は ここ です。
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;
}
}
Activity.onStop()
の後にフラグメントトランザクションを実行しないでくださいonStop()
の後にトランザクションを実行できるコールバックがないことを確認します。 .commitAllowingStateLoss()
などのアプローチで問題を回避しようとするのではなく、理由を修正することをお勧めします
私は@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歓声
状態を保存する場合は、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();
}
}
}
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)
私はまったく同じ問題を抱えていました。それは前の活動の破壊のために起こりました。以前の活動を後押ししたとき、それは破壊されました。基本活動にする(間違っている)
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
SpinnerCustom2.setFragmentManager(getSupportFragmentManager());
onCreateDrawerActivity(savedInstanceState);
}
それは正しかったonStartに入れました
@Override
protected void onStart() {
super.onStart();
SpinnerCustom2.setFragmentManager(getSupportFragmentManager());
}
別の考えられる回避策、すべての場合に役立つかどうかわからない(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();
}
}
}
PopBackStack()またはpopBackStackImmediate()メソッドでクラッシュした場合は、fixtを試してみてください。
if (!fragmentManager.isStateSaved()) {
fragmentManager.popBackStackImmediate();
}
これは私のためにも機能します。
基本フラグメントを作成し、それを自分のアプリ内のすべてのフラグメントに拡張させることにしました
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
例外はここで投げられます(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();
}
}
}
@ 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;
}
私はまたこの問題を経験しました、そしてあなたのFragmentActivity
のコンテキストが変更されるたびに問題が発生します(例えばスクリーンの向きが変更されるなど)。だからそれのための最善の解決策はあなたのFragmentActivity
からコンテキストを更新することです。
これをあなたの活動に追加してください
@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);
}
}
サポートライブラリのバージョン24.0.0以降では、FragmentTransaction.commitNow()
の後にcommit()
を呼び出す代わりに、このトランザクションを同期的にコミットするexecutePendingTransactions()
メソッドを呼び出すことができます。 ドキュメンテーション として、このアプローチはさらに良いと言います。
CommitNowを呼び出すことに続いてexecutePendingTransactions()を呼び出すことが望ましいのは、それが望ましい振る舞いであるかどうかにかかわらず、現在保留中のすべてのトランザクションをコミットしようとするという副作用があるためです。