java - popBackStack 导致 java.lang.IllegalStateException : Can not perform this action after onSaveInstanceState

标签 java android android-fragments

我在下面的 manager.popBackStack 行收到此错误。有没有解决的办法?它恰好发生了。

public void updateView(Fragment fragment) {

        IFragment currentFragment = (IFragment)fragment;

        FragmentManager manager = getSupportFragmentManager();
        FragmentTransaction fragmentTransaction = manager.beginTransaction();
        fragmentTransaction.replace(R.id.content_frame, fragment);

        if(currentFragment != null)
        {
            if(currentFragment.isRoot())
            {
                manager.popBackStack(null, FragmentManager.POP_BACK_STACK_INCLUSIVE);
            }
            else
            {
                fragmentTransaction.addToBackStack("test");
            }
        }

        fragmentTransaction.commitAllowingStateLoss();

        if(drawerManager.DrawerLayout != null) {
            drawerManager.DrawerLayout.closeDrawer(drawerManager.DrawerList);
        }
    }
 Fatal Exception: java.lang.IllegalStateException: Can not perform this
 action after onSaveInstanceState
        at android.support.v4.app.FragmentManagerImpl.checkStateLoss(FragmentManager.java:2044)
        at android.support.v4.app.FragmentManagerImpl.enqueueAction(FragmentManager.java:2067)
        at android.support.v4.app.FragmentManagerImpl.popBackStack(FragmentManager.java:799)
        at com.exposure.activities.BaseActivity.updateView(BaseActivity.java:239)
        at com.exposure.activities.events.EventActivity.setupEvent(EventActivity.java:204)
        at com.exposure.activities.events.EventActivity.getData(EventActivity.java:117)
        at com.exposure.utilities.ActivityContainer.getData(ActivityContainer.java:83)
        at com.exposure.utilities.DataTask.onPostExecute(DataTask.java:37)
        at android.os.AsyncTask.finish(AsyncTask.java:695)
        at android.os.AsyncTask.-wrap1(Unknown Source)
        at android.os.AsyncTask$InternalHandler.handleMessage(AsyncTask.java:712)
        at android.os.Handler.dispatchMessage(Handler.java:105)
        at android.os.Looper.loop(Looper.java:164)
        at android.app.ActivityThread.main(ActivityThread.java:6938)
       at java.lang.reflect.Method.invoke(Method.java)
        at com.android.internal.os.Zygote$MethodAndArgsCaller.run(Zygote.java:327)
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1374)

最佳答案

Fragment Transactions & Activity State Loss 中所述在要点 “避免在异步回调方法中执行事务” 下:

Avoid performing transactions inside asynchronous callback methods. This includes commonly used methods such as AsyncTask#onPostExecute() and LoaderManager.LoaderCallbacks#onLoadFinished(). The problem with performing transactions in these methods is that they have no knowledge of the current state of the Activity lifecycle when they are called.

这似乎是您遇到的问题,因为 updateView() 是从异步任务中调用的,但让我们测试一下这个假设。

以下演示应用程序创建一个 fragment ,模拟后台处理和一个模仿您的异步回调的回调。代码中有一个标志,mFixIt 当设置为 true 时会导致应用程序正常运行(不会崩溃),当设置为 false 时会使应用程序失败。

使用 mFixIt == false。触发器是导致应用程序进入停止状态的主页按钮:

enter image description here

这是堆栈跟踪:

14967-15003 E/AndroidRuntime: FATAL EXCEPTION: Thread-2
    Process: com.example.illegalstatepopbackstack, PID: 14967
    java.lang.IllegalStateException: Can not perform this action after onSaveInstanceState
        at android.support.v4.app.FragmentManagerImpl.checkStateLoss(FragmentManager.java:2080)
        at android.support.v4.app.FragmentManagerImpl.enqueueAction(FragmentManager.java:2106)
        at android.support.v4.app.FragmentManagerImpl.popBackStack(FragmentManager.java:832)
        at com.example.illegalstatepopbackstack.MainActivity.updateView(MainActivity.java:70)
        at com.example.illegalstatepopbackstack.ui.main.MainFragment$1.run(MainFragment.java:63)
        at java.lang.Thread.run(Thread.java:764)

现在 mFixIt == true。这次的不同之处在于,应用程序在 Activity 处于停止状态时识别异步回调,记录这已经发生并在应用程序重新启动时完成处理。视觉只是按下主页按钮并从“最近”恢复。该应用程序只是在开始时放置 fragment ,当重新启动时更改顶部 TextView 文本并从后台弹出 fragment 。

enter image description here

可以看出,处理按预期完成。

这是一个简单的例子。如果您的处理涉及更多,或者您只是想要一种更正式的方式来处理这种情况,我建议您查看 this solution .

这是演示应用程序的代码:

MainActivity.java

public class MainActivity extends AppCompatActivity {
    // Set to true to fix the problem; false will cause the IllegalStateException
    private boolean mFixIt = false;

    private MainFragment mFragment;
    private TextView mTextView;
    private boolean mIsPaused;
    private boolean mUpdateViewNeeded;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main_activity);
        mTextView = findViewById(R.id.textView);

        FragmentManager fm = getSupportFragmentManager();
        if (savedInstanceState == null) {
            // Create out fragment
            mFragment = MainFragment.newInstance();
            fm.beginTransaction()
                .replace(R.id.container, mFragment)
                .addToBackStack(FRAGMENT_TAG)
                .commit();
        } else {
            // Find the restored fragment.
            mFragment = (MainFragment) fm.findFragmentByTag(FRAGMENT_TAG);
        }
    }

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

        // Simulate a background task that does something useful. This one just waits a few
        // second then does a callback to updateView(). The activity will be fully paused by then.
        mFragment.doSomethingInBackground();
        mIsPaused = true;
        Log.d("MainActivity","<<<< stopped");
    }

    @Override
    protected void onStart() {
        super.onStart();
        mIsPaused = false;
        if (mUpdateViewNeeded) {
            // Execute delayed processing now that the activity is resumed.
            updateView(getSupportFragmentManager().findFragmentByTag(FRAGMENT_TAG));
        }
    }

    public void updateView(Fragment fragment) {
        if (mIsPaused && mFixIt) {
            // Delay processing
            mUpdateViewNeeded = true;
        } else {
            // Do out update work. If we are paused, this will get an IllegalStateException. If
            // we are resumed, this will work as intended.
            mTextView.setText("Replaced...");
            getSupportFragmentManager().popBackStack(null, FragmentManager.POP_BACK_STACK_INCLUSIVE);
            mUpdateViewNeeded = false;
        }
    }

    public static final String FRAGMENT_TAG = "MyFragment";
}

MainFragment.java

public class MainFragment extends Fragment {

    MainActivity mMainActivity;

    public static MainFragment newInstance() {
        return new MainFragment();
    }

    @Nullable
    @Override
    public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container,
                             @Nullable Bundle savedInstanceState) {
        return inflater.inflate(R.layout.main_fragment, container, false);
    }

    @Override
    public void onActivityCreated(@Nullable Bundle savedInstanceState) {
        super.onActivityCreated(savedInstanceState);
    }

    @Override
    public void onAttach(Context context) {
        super.onAttach(context);
        mMainActivity = (MainActivity) context;
    }

    @Override
    public void onDetach() {
        super.onDetach();
        mMainActivity = null;
    }

    @Override
    public void onStart() {
        super.onStart();

    }

    public void doSomethingInBackground() {
        new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    Thread.sleep(2000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                if (mMainActivity != null) {
                    mMainActivity.updateView(MainFragment.this);
                }
            }
        }).start();
    }
}

ma​​in_activity.xml

<TextView
    android:id="@+id/textView"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:background="@android:color/holo_blue_light"
    android:gravity="center"
    android:text="To be replaced..."
    android:textSize="36sp"
    android:textStyle="bold" />

<FrameLayout
    android:id="@+id/container"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity" />

ma​​in_fragment.xml

<android.support.constraint.ConstraintLayout 
    android:id="@+id/main"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="@android:color/holo_green_light"
    tools:context=".ui.main.MainFragment">

    <TextView
        android:id="@+id/message"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="MainFragment"
        android:textSize="36sp"
        android:textStyle="bold"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

</android.support.constraint.ConstraintLayout>

关于java - popBackStack 导致 java.lang.IllegalStateException : Can not perform this action after onSaveInstanceState,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/53566847/

相关文章:

android - DialogFragment.dismiss 因 NullPointerException 而崩溃

java - 在创建 fragment 时使用 ConstraintLayout

java - 确定 Spring Bean 可用的属性?

android-layout - 如何在所有 Activity 上放置注销按钮?

java - 未从 Android Geofence 事件中收到 Intent

android - 如何在平行空间中停止我的应用程序?

android - 在服务和 Activity 之间共享数据的最有效方式

java - 具有 IP 验证且仅允许用户投票的机制 : problem in getting user IP address with two app servers and apache in front

java - 在Java中如何将整数方法转换为字符串方法?

java - 在非 native 应用程序中将号码添加到 iPhone 或 Android 手机联系人列表