当恢复状态时,android的onCreateOptionsMenu被调用两次。

8
这是我创建的一个简单的安卓应用程序,用于展示我的问题:
public class OptionMenuTest extends Activity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        Log.d("test", "create activity");
        setContentView(R.layout.options_layout);
        if(getFragmentManager().findFragmentByTag("frag") == null) {
            getFragmentManager().beginTransaction().add(R.id.option_fragment_container, new OptionMenuFragment(), "frag").commit(); 
        }

    }

    @Override
    protected void onSaveInstanceState(Bundle outState) {
        Log.d("test", "saving Activity state");
        super.onSaveInstanceState(outState);
    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        Log.d("test", "create Activity options menu");
        menu.add("activity");
        return true;
    }
}

碎片:

public class OptionMenuFragment extends Fragment {

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        Log.d("test", "create fragment");
        setHasOptionsMenu(true);
    }

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        TextView tv = new TextView(getActivity());
        tv.setText("Hello world");
        Log.d("test", "create fragment view");
        return tv;
    }

    @Override
    public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
        menu.add("fragment");
        Log.d("test", "create fragment options menu");
    }
}

布局只是一个LinearLayout,用来将片段放入其中:

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/option_fragment_container"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    >
</LinearLayout>

非常简单,对吧?当我运行它时,如预期所示,我会得到以下输出:
06-12 15:42:51.415: D/test(957): create activity
06-12 15:42:51.446: D/test(957): create fragment
06-12 15:42:51.446: D/test(957): create fragment view
06-12 15:42:51.446: D/test(957): create Activity options menu
06-12 15:42:51.446: D/test(957): create fragment options menu

现在当我旋转手机时,出现了一些奇怪的行为:
06-12 15:43:11.251: D/test(957): saving Activity state
06-12 15:43:11.290: D/test(957): create fragment
06-12 15:43:11.290: D/test(957): create activity
06-12 15:43:11.306: D/test(957): create fragment view
06-12 15:43:11.306: D/test(957): create Activity options menu
06-12 15:43:11.306: D/test(957): create fragment options menu
06-12 15:43:11.306: D/test(957): create Activity options menu
06-12 15:43:11.306: D/test(957): create fragment options menu

为什么activity的onCreateOptionMenu和fragment的onCreateOptionsMenu会被调用两次?如果我从fragment中删除选项菜单,那么我将按预期获得1次调用到activity的onCreateOptionsMenu:
06-12 15:50:03.610: D/test(1076): create fragment
06-12 15:50:03.610: D/test(1076): create fragment view
06-12 15:50:03.813: D/test(1076): create Activity options menu
06-12 15:50:08.392: D/test(1076): saving Activity state // <-- rotate happens here
06-12 15:50:08.446: D/test(1076): create fragment
06-12 15:50:08.446: D/test(1076): create activity
06-12 15:50:08.462: D/test(1076): create fragment view
06-12 15:50:08.470: D/test(1076): create Activity options menu

我不理解这个问题,似乎也没有其他人遇到过这个问题。真正的问题是我的SearchView无法在配置更改(手机旋转)时恢复其状态,因为onCreateOptionsMenu被调用了两次。第一次它似乎保留了它的状态,但第二次它被清除并重置了。我无法弄清楚自己做错了什么。
提前致谢。

我认为你最好的选择是获取源代码并使用调试器逐步执行。很可能是Android中的一个错误。 - Timo Ohr
我在这个问题上没有取得任何进展。我已经逐步检查了代码,但对于幕后的片段/活动生命周期不够熟悉,无法知道发生了什么或者问题可能是什么。我现在有一个相当“hacky”的解决方法,但我要继续前进了。如果有人找到答案,请告诉我。谢谢! - Dave
我有完全相同的问题。onCreateOptionMenuonPrepareOptionsMenu会被调用两次,第二次调用会重置菜单状态。目前还没有找到解决方案 :( - Gerardo Contijoch
3个回答

6

我认为我找到了这个问题的答案。

看看这个:

https://dev59.com/SGw05IYBdhLWcg3weBpF#7225296

问题似乎与Android在活动销毁时(设备旋转时)未销毁片段有关。

基本上,我添加了:

setRetainInstance(true);

将我的片段构造函数传递给它,问题得到解决。

希望对你有所帮助!


3
是的!非常感谢。这解决了我的问题。 有趣的是,如果片段参与选项菜单但不调用setRetainInstance(true),即使SearchView由活动添加,该活动也存在此问题。如果从片段中删除setHasOptionsMenu,则活动的SearchView会正常工作。我认为这是因为新添加的片段导致活动的onCreateOptionsMenu再次被调用。 - Dave

3

@Gerardo Contijoch的答案是有误导性的,除了一个事实:

在你的示例中,无论是Activity还是Fragment都会在旋转时被销毁并重新创建。这就是为什么onCreateOptionsMenu()会被调用两次。这是正确和预期的行为。

通过setRetainInstance(true),您告诉Android不要销毁该片段。这可能对于没有任何活动上下文的无UI片段非常有用(用于AsyncTasks协调和一些其他类似服务的内容)。

在其他情况下,片段潜在地会导致内存泄漏,您需要避免这种情况。


-1

我刚刚在填充之前清除了菜单,现在它可以正常工作。

 @Override
 public void   onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
      menu.clear();
      inflater.inflate(R.menu.call_menu, menu);
      super.onCreateOptionsMenu(menu, inflater);

 }

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接