"在onSaveInstanceState"之后无法执行此操作当片段被替换时

时间:2015-01-27 13:50:00

标签: android android-fragments

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

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

我需要在f(1)点击事件上用实例f(2)替换当前实例b

  • 来自活动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 个答案:

答案 0 :(得分:2)

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

  1. Android创建了MainActivity
  2. 的全新实例
  3. FragmentManager在内部创建MyFragment的新实例,并在方向更改之前将其自动重新添加到活动中。
  4. 如果单击按钮,将调用侦听器。但是,侦听器是之前活动(旋转之前)的侦听器,已被破坏。
  5. 因此,不仅存在内存泄漏(旧活动不能被垃圾收集,因为它是从这里引用的),但您也会收到此错误。

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

    我建议使用EventBus。片段将触发事件onClick(),活动将在订阅后接收此事件。我推荐GreenDao EventBus。 否则你可以查看official docs,但从我的观点来看,他们教的不是一个好的解决方案,因为你的片段和活动很难连接(不是模块化的)。他们说您应该使用onAttach(),就像您在文档中的示例中看到的那样:http://developer.android.com/guide/components/fragments.html#EventCallbacks

    顺便说一下。如果你没有使用Fragment参数来传递"传递"会出现类似的问题。数据。有关详细信息,请阅读此博客:http://hannesdorfmann.com/android/fragmentargs/