在dispatchCreateOptionsMenu期间旋转时出现NullPointerException,堆栈跟踪不包括我的应用程序中的任何函数

我有一个应用程序,它使用ICS中操作栏上的选项卡,其中每个选项卡中都有一个片段。 在某些情况下,在我按下操作栏上选项菜单上的按钮后,然后旋转设备,我得到一个NullPointerException。 我可以使用相同的步骤可靠地重现它,但在某些情况下(如果我不按动作栏上的任何按钮)不会产生exception。 该exception似乎没有引用我的代码中的任何行,并且在方向更改后重新创建活动期间发生。

这是例外:

09-18 20:56:22.357: E/AndroidRuntime(689): FATAL EXCEPTION: main 09-18 20:56:22.357: E/AndroidRuntime(689): java.lang.RuntimeException: Unable to start activity ComponentInfo{com.andavapps.flightbot/com.andavapps.flightbot.FlightBotActivity}: java.lang.NullPointerException 09-18 20:56:22.357: E/AndroidRuntime(689): at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:1956) 09-18 20:56:22.357: E/AndroidRuntime(689): at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:1981) 09-18 20:56:22.357: E/AndroidRuntime(689): at android.app.ActivityThread.handleRelaunchActivity(ActivityThread.java:3351) 09-18 20:56:22.357: E/AndroidRuntime(689): at android.app.ActivityThread.access$700(ActivityThread.java:123) 09-18 20:56:22.357: E/AndroidRuntime(689): at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1151) 09-18 20:56:22.357: E/AndroidRuntime(689): at android.os.Handler.dispatchMessage(Handler.java:99) 09-18 20:56:22.357: E/AndroidRuntime(689): at android.os.Looper.loop(Looper.java:137) 09-18 20:56:22.357: E/AndroidRuntime(689): at android.app.ActivityThread.main(ActivityThread.java:4424) 09-18 20:56:22.357: E/AndroidRuntime(689): at java.lang.reflect.Method.invokeNative(Native Method) 09-18 20:56:22.357: E/AndroidRuntime(689): at java.lang.reflect.Method.invoke(Method.java:511) 09-18 20:56:22.357: E/AndroidRuntime(689): at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:784) 09-18 20:56:22.357: E/AndroidRuntime(689): at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:551) 09-18 20:56:22.357: E/AndroidRuntime(689): at dalvik.system.NativeStart.main(Native Method) 09-18 20:56:22.357: E/AndroidRuntime(689): Caused by: java.lang.NullPointerException 09-18 20:56:22.357: E/AndroidRuntime(689): at android.app.FragmentManagerImpl.dispatchCreateOptionsMenu(FragmentManager.java:1831) 09-18 20:56:22.357: E/AndroidRuntime(689): at android.app.Activity.onCreatePanelMenu(Activity.java:2445) 09-18 20:56:22.357: E/AndroidRuntime(689): at com.android.internal.policy.impl.PhoneWindow.preparePanel(PhoneWindow.java:388) 09-18 20:56:22.357: E/AndroidRuntime(689): at com.android.internal.policy.impl.PhoneWindow.invalidatePanelMenu(PhoneWindow.java:739) 09-18 20:56:22.357: E/AndroidRuntime(689): at com.android.internal.policy.impl.PhoneWindow.restorePanelState(PhoneWindow.java:1664) 09-18 20:56:22.357: E/AndroidRuntime(689): at com.android.internal.policy.impl.PhoneWindow.restoreHierarchyState(PhoneWindow.java:1619) 09-18 20:56:22.357: E/AndroidRuntime(689): at android.app.Activity.onRestoreInstanceState(Activity.java:906) 09-18 20:56:22.357: E/AndroidRuntime(689): at android.app.Activity.performRestoreInstanceState(Activity.java:878) 09-18 20:56:22.357: E/AndroidRuntime(689): at android.app.Instrumentation.callActivityOnRestoreInstanceState(Instrumentation.java:1100) 09-18 20:56:22.357: E/AndroidRuntime(689): at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:1934) 09-18 20:56:22.357: E/AndroidRuntime(689): ... 12 more 

这是我的活动代码(为了简单起见,删除了一些不相关的代码)

 public class MyActivity extends Activity { private class MyTabListener implements ActionBar.TabListener { private Activity activity; private MyFragment fragmentMain; private MyFragment fragmentSide; private Class cls; public MyTabListener(Activity a, Class c) { activity = a; cls = c; } @Override public void onTabReselected(Tab tab, FragmentTransaction ft) { } @Override public void onTabSelected(Tab tab, FragmentTransaction ft) { if (sidebar) { if (fragmentSide == null) { fragmentSide = (MyFragment) Fragment.instantiate(activity, cls.getName()); ft.add(c.SIDE_FRAME, fragmentSide, fragmentSide.getViewTag()); } else ft.attach(fragmentSide); } else { if (fragmentMain == null) { fragmentMain = (MyFragment) Fragment.instantiate(activity, cls.getName()); ft.add(c.MAIN_FRAME, fragmentMain, fragmentMain.getViewTag()); } else ft.attach(fragmentMain); } selected = tabs.indexOf(tab); mainUp = false; if (setUpComplete) invalidateOptionsMenu(); } @Override public void onTabUnselected(Tab tab, FragmentTransaction ft) { if (fragmentSide != null && !fragmentSide.isDetached()) ft.detach(fragmentSide); if (fragmentMain != null && !fragmentMain.isDetached()) ft.detach(fragmentMain); } } private class MyMainTabListener implements ActionBar.TabListener { private Activity activity; private MyFragment fragmentMain; private Class cls; public MyMainTabListener(Activity a, Class c) { activity = a; cls = c; } @Override public void onTabReselected(Tab tab, FragmentTransaction ft) { } @Override public void onTabSelected(Tab tab, FragmentTransaction ft) { if (fragmentMain == null) { fragmentMain = (MyFragment) Fragment.instantiate(activity, cls.getName()); ft.add(c.MAIN_FRAME, fragmentMain, fragmentMain.getViewTag()); } else if (fragmentMain.isDetached()) ft.attach(fragmentMain); mainUp = !sidebar; if (setUpComplete) invalidateOptionsMenu(); } @Override public void onTabUnselected(Tab tab, FragmentTransaction ft) { if (!sidebar && fragmentMain != null && !fragmentMain.isDetached()) ft.detach(fragmentMain); else if (sidebar && (fragmentMain == null || fragmentMain.isDetached())) { if (fragmentMain == null) { fragmentMain = (MyFragment) Fragment.instantiate(activity, cls.getName()); ft.add(c.MAIN_FRAME, fragmentMain, fragmentMain.getViewTag()); } else if (fragmentMain.isDetached()) ft.attach(fragmentMain); } } } private static final class c { //A bunch of constants are defined here } private ArrayList tabs = new ArrayList(); private int side; private int orient; private int selected; private boolean sidebar; private boolean mainUp; private boolean lock; private boolean setUpComplete = false; @Override public void onCreate(Bundle inState) { super.onCreate(inState); setContentView(R.layout.main_rel); orient = detectOrientation(); if (inState != null) { selected = inState.getInt("selected", 1); side = inState.getInt("side", c.LEFT_SIDE); sidebar = inState.getBoolean("visible", true); mainUp = inState.getBoolean("mainup", !sidebar); lock = inState.getBoolean("lock", false); } else { selected = 1; side = c.LEFT_SIDE; sidebar = true; mainUp = false; lock = false; } ActionBar ab = getActionBar(); ab.setNavigationMode(ActionBar.NAVIGATION_MODE_TABS); tabs.add(ab.newTab().setText(MainFragment.getTabText()).setTabListener(new MyMainTabListener(this, MainFragment.class))); tabs.add(ab.newTab().setText(OtherFragment.getTabText()).setTabListener(new MyTabListener(this, OtherFragment.class))); tabs.add(ab.newTab().setText(AnotherFragment.getTabText()).setTabListener(new MyTabListener(this, AnotherFragment.class))); tabs.add(ab.newTab().setText(YetAnotherFragment.getTabText()).setTabListener(new MyTabListener(this, YetAnotherFragment.class))); } @Override protected void onStart() { super.onStart(); if (!setUpComplete) setUp(); } @Override protected void onResume() { super.onResume(); if (!setUpComplete) setUp(); } @Override protected void onPause() { super.onPause(); } @Override protected void onStop() { super.onStop(); } @Override protected void onDestroy() { super.onDestroy(); } @Override public void onSaveInstanceState(Bundle outState) { setUpComplete = false; getActionBar().removeAllTabs(); super.onSaveInstanceState(outState); outState.putInt("selected", selected); outState.putInt("side", side); outState.putBoolean("visible", sidebar); outState.putBoolean("mainup", mainUp); } @Override public boolean onCreateOptionsMenu(Menu menu) { getMenuInflater().inflate(R.menu.menu, menu); return true; } @Override public boolean onPrepareOptionsMenu(Menu menu) { menu.findItem(R.id.menu_showhide).setTitle(c.MENU_SHOW_TEXT[(sidebar ? 1 : 0)]).setIcon(c.MENU_SHOW_DRAW[(sidebar ? 1 : 0)][side][orient][(mainUp ? 0 : 1)]); menu.findItem(R.id.menu_swapside).setTitle(c.MENU_SIDE_TEXT[side][orient]).setIcon(c.MENU_SIDE_DRAW[side][orient]); menu.findItem(R.id.menu_lock).setTitle(c.MENU_LOCK_TEXT[(lock ? 1 : 0)]).setIcon(c.MENU_LOCK_DRAW[(lock ? 0 : 1)]); if (!sidebar) menu.findItem(R.id.menu_swapside).setVisible(false).setEnabled(false); return true; } @Override public boolean onOptionsItemSelected(MenuItem item) { switch (item.getItemId()) { case R.id.menu_swapside: toggleSide(); return true; case R.id.menu_showhide: toggleVisibility(); return true; case R.id.menu_lock: toggleRotation(); return true; default: return super.onOptionsItemSelected(item); } } private int detectOrientation() { int o = getResources().getConfiguration().orientation; int r = getWindowManager().getDefaultDisplay().getRotation(); if (o == Configuration.ORIENTATION_LANDSCAPE && (r == Surface.ROTATION_0 || r == Surface.ROTATION_90)) return c.LAND_ORIENT; else if (o == Configuration.ORIENTATION_PORTRAIT && (r == Surface.ROTATION_90 || r == Surface.ROTATION_180)) return c.RPORT_ORIENT; else if (o == Configuration.ORIENTATION_LANDSCAPE && (r == Surface.ROTATION_180 || r == Surface.ROTATION_270)) return c.RLAND_ORIENT; else return c.PORT_ORIENT; } private void toggleVisibility() { sidebar = !sidebar; mainUp = !sidebar; invalidateOptionsMenu(); setUpTabs(); } private void toggleSide() { side = (side == c.RIGHT_SIDE ? c.LEFT_SIDE : c.RIGHT_SIDE); invalidateOptionsMenu(); setUpSide(); } private void toggleRotation() { lock = !lock; invalidateOptionsMenu(); setUpLock(); } private void setUp() { setUpTabs(); setUpSide(); setUpLock(); setUpComplete = true; } private void setUpTabs() { ActionBar ab = getActionBar(); ab.removeAllTabs(); ab.addTab(tabs.get(0), sidebar || mainUp); if (sidebar) ab.removeTab(tabs.get(0)); for (int i = 1; i < tabs.size(); i ++) ab.addTab(tabs.get(i), !mainUp && selected == i); } private void setUpSide() { RelativeLayout.LayoutParams params = new RelativeLayout.LayoutParams(RelativeLayout.LayoutParams.MATCH_PARENT, RelativeLayout.LayoutParams.MATCH_PARENT); params.addRule(c.SIDE_RULE[side][orient], c.SIDE_FRAME); FrameLayout mf = (FrameLayout) findViewById(c.MAIN_FRAME); mf.setLayoutParams(params); params = new RelativeLayout.LayoutParams(c.LAYOUT_WIDTH[orient], c.LAYOUT_HEIGHT[orient]); params.addRule(c.ALIGN_RULE[side][orient]); FrameLayout sf = (FrameLayout) findViewById(c.SIDE_FRAME); sf.setLayoutParams(params); } private void setUpLock() { setRequestedOrientation((lock ? c.LOCK_ORIENT[orient] : ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED)); } } 

关于我的应用程序的几个注释和解释事物的代码:

  • 该应用程序显示主要片段和侧边栏片段
  • 选项菜单包含三个按钮:一个用于将侧边栏从屏幕的一侧切换到另一侧,一个用于隐藏侧边栏,另一个用于锁定方向
  • 主片段始终是选项卡列表中的第一个,并且始终为MainFragmenttypes
  • 我在运行ICS(Asus Trans Prime,4.0.4; HTC Vivid,4.0.3)和仿真器(ICS 4.0.3和JB 4.1)的两台设备上运行它。 这只发生在ICS上。

以下序列发生exception:

  • 启动应用
  • 按按钮隐藏侧边栏
  • 旋转设备

如果在旋转设备之前发生任何其他事情,则不会发生exception。 例如,如果侧边栏被取消隐藏,我不会得到例外。 如果首先旋转设备,则永远不会发生exception,因此即使侧边栏被隐藏,并且设备再次旋转,我也不会得到exception。 并且堆栈跟踪不会引用我的代码中的单个函数,所以我甚至可以find根本原因。

看来这是FragmentManager.java(包android.app)中抛出exception的函数:

 1827 public boolean dispatchCreateOptionsMenu(Menu menu, MenuInflater inflater) { 1828 boolean show = false; 1829 ArrayList newMenus = null; 1830 if (mActive != null) { 1831 for (int i=0; i<mAdded.size(); i++) { 1832 Fragment f = mAdded.get(i); 1833 if (f != null && !f.mHidden && f.mHasMenu && f.mMenuVisible) { 1834 show = true; 1835 f.onCreateOptionsMenu(menu, inflater); 1836 if (newMenus == null) { 1837 newMenus = new ArrayList(); 1838 } 1839 newMenus.add(f); 1840 } 1841 } 1842 } 1843 1844 if (mCreatedMenus != null) { 1845 for (int i=0; i<mCreatedMenus.size(); i++) { 1846 Fragment f = mCreatedMenus.get(i); 1847 if (newMenus == null || !newMenus.contains(f)) { 1848 f.onDestroyOptionsMenu(); 1849 } 1850 } 1851 } 1852 1853 mCreatedMenus = newMenus; 1854 1855 return show; 1856 } 

在尝试使用它之前,没有对mAdded进行空检查。 JB中的相同function用(mActive != null)替换(mAdded != null) 。 但我不知道我可以为ICS做些什么作为避免这种情况的解决方法。

有人有什么想法吗? 我已经搜索了StackOverflow寻找类似的问题,但到目前为止空洞。 谢谢! 如果我需要发布任何其他内容,请告诉我,我会添加它。

我有一个使用工具栏的应用程序(基于com.example.android.actionbarcompat的代码),我遇到了同样的问题。 在某些设备上,onCreateOptionsMenu()不会在onCreate()之后调用,这意味着操作栏未初始化,当我尝试更改图标时,应用程序崩溃了。

对我来说好消息是onCreateOptionsMenu()稍后会被调用,当它发生时,我会继续引用onCreateOptionsMenu()中的菜单。 我修改了剩余的活动代码,以便在他们对操作栏执行任何操作之前检查此引用。 这仍然执行速度超快,用户体验不受影响。

我建议你这样做,并推迟一些初始化。

将其添加到您的活动代码:

 android:configChanges="orientation" 

并覆盖此方法:

 public void onConfigurationChanged(Configuration newConfig) 

您可以在以下位置find更多信息

http://developer.android.com/reference/android/app/Activity.html#onConfigurationChanged(android.content.res.Configuration

http://developer.android.com/reference/android/R.attr.html#configChanges

我有一个类似的问题,一个应用程序在选项卡中有Fragments ,NPE在轮换更改。 原因是Fragments中的Activity引用有时为null。 我通过在可能的地方更改Fragment's Activity对应用程序上下文引用的引用来避免NPE问题,并通过在Fragments中测试所有剩余的Activity引用为null:

 if(getActivity() != null) { getActivity.someMethod() };