Android java.lang.IllegalStateException:在onSaveInstanceState之后无法执行此操作

Android java.lang.IllegalStateException:在onSaveInstanceState之后无法执行此操作,android,exception,android-asynctask,android-fragments,illegalstateexception,Android,Exception,Android Asynctask,Android Fragments,Illegalstateexception,我正在使用我的应用程序的支持库。在我的FragmentActivity中,我使用AsyncTask从internet下载数据。在onPreExecute()方法中,我添加了一个片段,在onPostExecute()方法中,我再次删除了它。当方向在两者之间改变时,我得到上面提到的异常。请看一下详情: private class onFriendAddedAsyncTask extends AsyncTask<String, Void, String> { DummyFragme

我正在使用我的应用程序的支持库。在我的FragmentActivity中,我使用AsyncTask从internet下载数据。在onPreExecute()方法中,我添加了一个片段,在onPostExecute()方法中,我再次删除了它。当方向在两者之间改变时,我得到上面提到的异常。请看一下详情:

private class onFriendAddedAsyncTask extends AsyncTask<String, Void, String> {
    DummyFragment dummyFragment; 
    FragmentManager fm;
    FragmentTransaction ft;

@Override
protected void onPreExecute() {
    Log.v("MyFragmentActivity", "onFriendAddedAsyncTask/onPreExecute");
    dummyFragment = DummyFragment.newInstance();
    fm = getSupportFragmentManager();
    ft = fm.beginTransaction();
    ft.add(dummyFragment, "dummy_fragment");
    ft.commit();
}

@Override
protected void onPostExecute(String result) {
    Log.v("MyFragmentActivity", "onFriendAddedAsyncTask/onPostExecute");
    ft = fm.beginTransaction();
    ft.remove(dummyFragment);
    ft.commit();
}

@Override
protected String doInBackground(String... name) {
    Log.v("MyFragmentActivity", "onFriendAddedAsyncTask/doInBackground");
    ...
}
在其他关于类似问题的线程中,原因似乎是在调用onResume()方法之前调用了onPostExecute方法。但是,即使之前调用了onResume(),我也会遇到异常

有人知道怎么了吗

该活动如下所示:

public class MyFragmentActivity extends FragmentActivity implements OnFriendSelectedListener, OnFriendAddedListener, OnFriendOptionSelectedListener, LoaderCallbacks<Cursor> {

@Override
public void onCreate(Bundle savedInstanceState) {
    Log.v("MyFragmentActivity", "onCreate");
    super.onCreate(savedInstanceState);
    setContentView(R.layout.fragment_activity_layout);
    FragmentManager fm = getSupportFragmentManager();
    FragmentTransaction ft = fm.beginTransaction();
    FriendListFragment friendListFragment = (FriendListFragment)fm.findFragmentById(R.id.friend_list_fragment_layout);
    if (friendListFragment == null) {
        friendListFragment = new FriendListFragment(); 
        ft.add(R.id.friend_list_fragment_layout, friendListFragment);
        ft.commit();
        fm.executePendingTransactions();
        startService(new Intent(this, MyIntentService.class));
        getSupportLoaderManager().initLoader(CHECK_EMPTY_DATABASE, null, this);
    }
}

    @Override
public boolean onCreateOptionsMenu(Menu menu) {
    super.onCreateOptionsMenu(menu);
    getMenuInflater().inflate(R.menu.fragment_activity_options_menu, menu);
    return true;
}

@Override
public boolean onOptionsItemSelected(MenuItem item) {
    super.onOptionsItemSelected(item);
    switch (item.getItemId()) {
    case R.id.add_friend_menu_item:
        AddFriendDialogFragment addFriendDialogFragment = AddFriendDialogFragment.newInstance();
        addFriendDialogFragment.show(getSupportFragmentManager(), "add_friend_dialog_fragment");
        return true;
    default:
        return false;
    }
}

@Override
public void onFriendAdded(String name) {
    name = name.trim();
    if (name.length() > 0) {
        new onFriendAddedAsyncTask().execute(name);
    }
}
当我实现AsynTask时,我得到了同样的IllegalStateException,如下所示,因为findFragmentById()方法返回一个空指针

private class onFriendAddedAsyncTask extends AsyncTask<String, Void, String> {

    protected void onPreExecute() {
        Log.v("MyFragmentActivity", "onFriendAddedAsyncTask/onPreExecute");
        FragmentManager fm = getSupportFragmentManager();
        FragmentTransaction ft = fm.beginTransaction();
        DummyFragment dummyFragment = DummyFragment.newInstance();
        ft.add(R.id.dummy_fragment_layout, dummyFragment);
        ft.commit();
    }

    protected void onPostExecute(String result) {
        Log.v("MyFragmentActivity", "onFriendAddedAsyncTask/onPostExecute");
        FragmentManager fm = getSupportFragmentManager();
        FragmentTransaction ft = fm.beginTransaction();
        DummyFragment dummyFragment = (DummyFragment) fm.findFragmentById(R.id.dummy_fragment_layout);
        ft.remove(dummyFragment);
        ft.commitAllowingStateLoss();
    }
在onPreExecute()中,FriendListFragment的id为0x7f0a0002。在处理程序内部,将创建id为0x7f0a0004的DummyFragment。在onPostExecute()中,两个ID都为null。 在onPreExecute()中,MyFragmentActivity的地址是45e38358。但是在onPostExecute()中,它是空的。但在这两种方法中,FragmentManager地址都是45e384a8。
我猜onPostExecute使用了无效的FragmentManager。但是为什么呢?

您应该在
处理程序中执行以下事务:

@Override
protected void onPostExecute(String result) {
    Log.v("MyFragmentActivity", "onFriendAddedAsyncTask/onPostExecute");
    new Handler().post(new Runnable() {
            public void run() {
                fm = getSupportFragmentManager();
                ft = fm.beginTransaction();
                ft.remove(dummyFragment);
                ft.commit();
            }
        });
}

异常的原因是在
AsyncTask
运行期间重新创建
FragmentActivity
,以及在
onPostExecute()
之后访问先前已销毁的
FragmentActivity

问题是获取新
碎片活动的有效引用。没有用于此的方法,无论是
getActivity()
还是
findById()
或类似的方法。本论坛充满了与此问题相关的帖子(例如,在onPostExecute中搜索
“活动上下文”
)。其中一些描述了变通方法(直到现在我还没有找到一个好的)


也许为我的目的使用服务是更好的解决方案。

谢谢Oleg Vaskevich。使用
FragmentActivity
WeakReference
解决了问题。现在,我的代码如下所示:

public class MyFragmentActivity extends FragmentActivity implements OnFriendAddedListener {

    private static WeakReference<MyFragmentActivity> wrActivity = null;

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        wrActivity = new WeakReference<MyFragmentActivity>(this);
        ...

    private class onFriendAddedAsyncTask extends AsyncTask<String, Void, String> {

        @Override
        protected void onPreExecute() {
            FragmentManager fm = getSupportFragmentManager();
            FragmentTransaction ft = fm.beginTransaction();
            DummyFragment dummyFragment = DummyFragment.newInstance();
            ft.add(R.id.dummy_fragment_layout, dummyFragment);
            ft.commit();
        }

        @Override
        protected void onPostExecute(String result) {
            final Activity activity = wrActivity.get();
            if (activity != null && !activity.isFinishing()) {
                FragmentManager fm = activity.getSupportFragmentManager();
                FragmentTransaction ft = fm.beginTransaction();
                DummyFragment dummyFragment = (DummyFragment) fm.findFragmentById(R.id.dummy_fragment_layout);
                ft.remove(dummyFragment);
                ft.commitAllowingStateLoss();
            }
        }
公共类MyFragmentActivity扩展了FragmentActivity在FriendAddedListener上的实现{
私有静态WeakReference wrActivity=null;
@凌驾
创建时的公共void(Bundle savedInstanceState){
super.onCreate(savedInstanceState);
wrActivity=新的WeakReference(本);
...
私有类OnFriendAddadaSyncTask扩展了AsyncTask{
@凌驾
受保护的void onPreExecute(){
FragmentManager fm=getSupportFragmentManager();
FragmentTransaction ft=fm.beginTransaction();
DummyFragment DummyFragment=DummyFragment.newInstance();
ft.add(R.id.dummy\u fragment\u布局,dummyFragment);
ft.commit();
}
@凌驾
受保护的void onPostExecute(字符串结果){
最终活动=wrActivity.get();
if(activity!=null&&!activity.isFinishing()){
FragmentManager fm=activity.getSupportFragmentManager();
FragmentTransaction ft=fm.beginTransaction();
DummyFragment DummyFragment=(DummyFragment)fm.findFragmentById(R.id.dummy\u fragment\u布局);
ft.remove(dummyFragment);
ft.佣金列报损失();
}
}

值得一提的是,我在后台运行服务的应用程序上出现了此错误。在其中一个应用程序上,必须向用户显示超时对话框。如果应用程序不再在前台运行,则该对话框就是导致此错误的问题


在我们的例子中,当应用程序处于后台时,显示对话框是没有用的,所以我们只是跟踪它(布尔标记为on pause en on resume),然后仅当用户实际可以看到应用程序时才显示对话框。

我相信这个问题的正确答案是以下方法

public abstract int commitAllowingStateLoss ()
类似于commit(),但允许在活动结束后执行提交 状态已保存。这是危险的,因为如果 该活动需要稍后从其状态恢复,因此应该 仅适用于用户界面状态可以更改的情况 意外地在用户身上

上述描述与该方法有关

protected void onSaveInstanceState(android.os.Bundle outState)
这个问题恰恰发生在设备进入睡眠状态时


这发生在我身上,因为我正在从正在泄漏活动的子片段调用
commit()
。它将活动保留为属性,并且在旋转活动上,活动变量没有被
onAttach();
更新,所以我试图通过reserved
提交僵尸活动上的事务(setRetainInstance(true);)
fragment.

我有一个类似的问题,通过将一些片段事务代码从
onResume()
移动到
onStart()
中,我解决了这个问题

更准确地说:我的应用程序是一个启动器。在按下Android Home按钮后,用户可以选择一个启动器,直到他/她的决定被记住。当此时“返回”时(例如,点击灰色区域),应用程序崩溃


也许这对某人有帮助。

简短而有效的解决方案:

遵循简单的步骤:

步骤1:覆盖相应片段中的
onSaveInstanceState
状态。并从中删除super方法

@Override
public void onSaveInstanceState(Bundle outState) {
}
步骤2:在执行片段操作时,使用
CommitAllowingStateLoss();
而不是
commit();

fragmentTransaction.commitAllowingStateLoss();

在显示片段之前,检查活动
isFinishing()

示例:

if(!isFinishing()) {
FragmentManager fm = getSupportFragmentManager();
            FragmentTransaction ft = fm.beginTransaction();
            DummyFragment dummyFragment = DummyFragment.newInstance();
            ft.add(R.id.dummy_fragment_layout, dummyFragment);
            ft.commitAllowingStateLoss();
}

我的应用程序有一个片段要在3秒钟内加载,但当第一个屏幕准备显示时,我按下home按钮并继续运行它,它显示相同的错误,因此它编辑了我的代码,运行非常平稳:

new Handler().post(new Runnable() {
        public void run() {
            if (saveIns == null) {
                mFragment = new Fragment_S1_loading();
                getFragmentManager().beginTransaction()
                        .replace(R.id.container, mFragment).commit();
            }
            getActionBar().hide();
            // Loading screen in 3 secs:
            mCountDownTimerLoading = new CountDownTimer(3000, 1000) {

                @Override
                public void onTick(long millisUntilFinished) {

                }

                @Override
                public void onFinish() {
                    if (saveIns == null) {// TODO bug when start app and press home
                                            // button
                        getFragmentManager()
                                .beginTransaction()
                                .replace(R.id.container,
                                        new Fragment_S2_sesstion1()).commitAllowingStateLoss();
                    }
                    getActionBar().show();
                }
            }.start();
        }
    });
注意:添加佣金
@Override
public void onSaveInstanceState(Bundle outState) {
}
fragmentTransaction.commitAllowingStateLoss();
if(!isFinishing()) {
FragmentManager fm = getSupportFragmentManager();
            FragmentTransaction ft = fm.beginTransaction();
            DummyFragment dummyFragment = DummyFragment.newInstance();
            ft.add(R.id.dummy_fragment_layout, dummyFragment);
            ft.commitAllowingStateLoss();
}
new Handler().post(new Runnable() {
        public void run() {
            if (saveIns == null) {
                mFragment = new Fragment_S1_loading();
                getFragmentManager().beginTransaction()
                        .replace(R.id.container, mFragment).commit();
            }
            getActionBar().hide();
            // Loading screen in 3 secs:
            mCountDownTimerLoading = new CountDownTimer(3000, 1000) {

                @Override
                public void onTick(long millisUntilFinished) {

                }

                @Override
                public void onFinish() {
                    if (saveIns == null) {// TODO bug when start app and press home
                                            // button
                        getFragmentManager()
                                .beginTransaction()
                                .replace(R.id.container,
                                        new Fragment_S2_sesstion1()).commitAllowingStateLoss();
                    }
                    getActionBar().show();
                }
            }.start();
        }
    });
@Override
public void onSaveInstanceState(Bundle outState) {
}
@Override
public void onSaveInstanceState(Bundle outState) {
     // TODO: Add code to remove fragment here
     super.onSaveInstanceState(outState);
}
/**
 * Flag to avoid "java.lang.IllegalStateException: Can not perform this action after
 * onSaveInstanceState". Avoid Fragment transaction until onRestoreInstanceState or onResume
 * gets called.
 */
private boolean isOnSaveInstanceStateCalled = false;


@Override
public void onRestoreInstanceState(final Bundle bundle) {
    .....
    isOnSaveInstanceStateCalled = false;
    .....
}

@Override
public void onSaveInstanceState(final Bundle outState) {
    .....
    isOnSaveInstanceStateCalled = true;
    .....
}

@Override
public void onResume() {
    super.onResume();
    isOnSaveInstanceStateCalled = false;
    .....
}
private void fragmentReplace(Fragment fragment, String fragmentTag){
    if (!isOnSaveInstanceStateCalled) {
        getSupportFragmentManager()
                .beginTransaction()
                .replace(R.id.layout_container, fragment, fragmentTag)
                .commit();
    }
}
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 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();
    }
}
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;
 }
}
val fragmentTransaction = activity.supportFragmentManager.beginTransaction()
fragmentTransaction.add(dialogFragment, tag)
fragmentTransaction.commitAllowingStateLoss()