替换片段时的"Can not perform this action after onSaveInstanceState"



在我的项目上下文中,我在活动a的片段f(1)中有一个按钮b

片段f(x)F的一个实例,其中内容取决于参数x

我需要将当前实例f(1)替换为b单击事件时f(2)实例:

  • 从活动a

    private void setFragment(int x) {
        Bundle args = new Bundle();
        args.putInt("x", x);
        F f = new F();
        f.setArguments(args);
        f.setListener(new F.Listener() {
            public void onButtonClick(int x) {
                setFragment(x);
            }
        });
        getSupportFragmentManager()
            .beginTransaction()
            .replace(ID, f)
            .commit();
    }
    
  • 从片段f

    b.setOnClickListener(new View.onClickListener() {
        public void onClick(View view) {
            listener.onButtonClick(x + 1);
        }
    });
    

我的问题是:

仅当发生配置状态更改时b单击事件才会引发异常:

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

拜托,我的错误是什么?我阅读了很多关于此异常的帖子,但没有找到任何解决方案


编辑:我只是在没有AsyncTask的情况下进行测试,请参阅代码:

尝试旋转屏幕并按下按钮

public class MainActivity extends ActionBarActivity {
    @Override
    protected void onCreate(Bundle state) {
        super.onCreate(state);
        setContentView(R.layout.activity_main);
        if (state == null) {
            setFragment(1);
        }
    }
    private void setFragment(int id) {
        Bundle args = new Bundle();
        args.putInt("id", id);
        MyFragment myFragment = new MyFragment();
        myFragment.setArguments(args);
        myFragment.setListener(new MyFragment.Listener() {
            @Override
            public void onClick(int id) {
                setFragment(id);
            }
        });
        getSupportFragmentManager()
            .beginTransaction()
            .replace(R.id.fragment, myFragment)
            .commit();
    }
    public static class MyFragment extends Fragment{
        @Override
        public void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setRetainInstance(true);
        }
        @Override
        public View onCreateView(LayoutInflater inflater, ViewGroup view, Bundle state) {
            return new Button(getActivity()) {
                {
                    setText(String.valueOf(getArguments().getInt("id")));
                    setOnClickListener(new View.OnClickListener() {
                        @Override
                        public void onClick(View view) {
                            listener.onClick(getArguments().getInt("id") + 1);
                        }
                    });
                }
            };
        }
        private static interface Listener {
            public void onClick(int id);
        }
        private Listener listener;
        public void setListener(Listener listener) {
            this.listener = listener;
        }
    }
}

问题在于您设置侦听器的方式。您正在设置听众,然后将设备从横向旋转到纵向。轮换后发生的情况是:

  1. 安卓创建了一个全新的MainActivity实例。
  2. FragmentManager内部创建一个新的MyFragment实例,并将其自动重新添加到活动中,就像方向更改之前一样。
  3. 如果单击该按钮,将调用侦听器。但是,侦听器是已被销毁的上一个活动(在轮换之前)的侦听器。

因此,您不仅有内存泄漏(旧活动不能被垃圾回收,因为它是从这里引用的),而且您还会收到此错误。

如何正确操作:好吧,问题不仅在于您没有正确理解Android片段生命周期setRetainInstanceState()。如上所述,片段由FragmentManager(FragmentTransaction)控制。所以,是的,每次你旋转屏幕时都会创建一个新的片段实例,但FragmentManager会自动为你附加它们(这有点奇怪,但这就是片段的工作方式)

我建议使用EventBus。片段将触发事件onClick()活动将收到此事件,因为它已订阅。我重新评论绿色道事件巴士。否则,您可以查看官方文档,但从我的角度来看,它们教授的不是一个好的解决方案,因为您的片段和活动几乎没有连接(不是模块化的)。他们说你应该使用onAttach()就像你在文档中的示例中看到的那样:http://developer.android.com/guide/components/fragments.html#EventCallbacks

顺便说一句,如果您不使用片段参数来"传递"数据,则可能会出现类似的问题。有关更多详细信息,请阅读此博客:http://hannesdorfmann.com/android/fragmentargs/

相关内容

最新更新