NullPointer调度期间轮换时的异常创建选项菜单,堆栈跟踪在我的应用程序中不包含任何函数



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

这里有一个例外:

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<C extends MyFragment> implements ActionBar.TabListener {
private Activity activity;
private MyFragment fragmentMain;
private MyFragment fragmentSide;
private Class<C> cls;
public MyTabListener(Activity a, Class<C> 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<C extends MyFragment> implements ActionBar.TabListener {
private Activity activity;
private MyFragment fragmentMain;
private Class<C> cls;
public MyMainTabListener(Activity a, Class<C> 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<ActionBar.Tab> tabs = new ArrayList<ActionBar.Tab>();
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<MainFragment>(this, MainFragment.class)));
tabs.add(ab.newTab().setText(OtherFragment.getTabText()).setTabListener(new MyTabListener<OtherFragment>(this, OtherFragment.class)));
tabs.add(ab.newTab().setText(AnotherFragment.getTabText()).setTabListener(new MyTabListener<AnotherFragment>(this, AnotherFragment.class)));
tabs.add(ab.newTab().setText(YetAnotherFragment.getTabText()).setTabListener(new MyTabListener<YetAnotherFragment>(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));
}
}

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

  • 应用程序显示一个主片段和一个侧边栏片段
  • 选项菜单包含三个按钮:一个用于将侧边栏从屏幕的一侧切换到另一侧,一个用于隐藏侧边栏,另一个用于锁定方向
  • 主片段总是选项卡列表中的第一个,并且总是MainFragment类型
  • 我在两台运行ICS的设备上运行这个(华硕Trans Prime,4.0.4;HTC Vivid,4.0.3)&Emulator(ICS 4.0.3&JB 4.1)。这种情况仅在ICS上发生

异常按照以下顺序发生:

  • 启动应用程序
  • 按下按钮隐藏侧边栏
  • 旋转装置

如果在旋转设备之前发生任何其他情况,则不会发生异常。例如,如果侧边栏被取消隐藏,我不会得到异常。如果先旋转设备,则永远不会出现异常,因此即使侧边栏被隐藏,设备再次旋转,我也不会出现异常。堆栈跟踪在我的代码中没有引用任何函数,所以我甚至可以找到根本原因。

这似乎是FragmentManager.java(packageandroid.app)中抛出异常的函数:

1827     public boolean dispatchCreateOptionsMenu(Menu menu, MenuInflater inflater) {
1828         boolean show = false;
1829         ArrayList<Fragment> 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<Fragment>();
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之前,不会对其进行null检查。JB中的相同函数将(mActive != null)替换为(mAdded != null)。但我不知道我可以为ICS做些什么来避免这种情况。

有人有什么想法吗?我在StackOverflow上搜索过类似的问题,但到目前为止都是空的。谢谢如果我还有什么需要发布的,请告诉我,我会添加它。

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

对我来说,好消息是onCreateOptionsMenu()稍后会被调用,当它发生时,我会在onCreateOption_menu(()中保留对菜单的引用。我修改了活动代码的其余部分,以便在他们对操作栏执行任何操作之前检查此引用。这仍然执行得非常快,并且用户体验没有受到影响。

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

将其添加到您的活动标签:

android:configChanges="orientation"

并覆盖此方法:

public void onConfigurationChanged(Configuration newConfig)

您可以在以下位置找到更多信息:

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引用有时为空。我通过在可能的情况下将Fragment'sActivity引用更改为应用程序上下文引用,并通过测试Fragments中所有剩余的Activity引用是否为null来避免NPE问题:

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

相关内容

最新更新