I have an application that uses tabs on the action bar in ICS, where each tab has a fragment inside it. Under certain circumstances, after I clicked the button in the options menu on the action bar, then rotate the device, I get a NullPointerException. I can reliably play it with the same set of steps, but there are some cases (for example, if I do not click the buttons on the action bar) do not throw an exception. The exception does not seem to refer to any line in my code, but occurs during the recreation of activity after a change in orientation.
Here's the 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
And here is my activity code (removed some unrelated code for ease of displaying it here)
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 few notes about my application and code to explain things:
- The application displays the main fragment and fragment of the sidebar
- The options menu contains three buttons: one to switch the sidebar from one side of the screen to the other, one to hide the sidebar and one to lock the orientation
- The main fragment is always the first in the list of tabs and always has the type MainFragment
- I run this on two devices running ICS (Asus Trans Prime, 4.0.4, HTC Vivid, 4.0.3) and an emulator (ICS 4.0.3 and JB 4.1). This only happens in ICS.
An exception occurs with the following sequence:
- Launch the application
- click the button to hide the sidebar.
- rotate device
If anything else happens before the device rotates, an exception does not occur. For example, if the sidebar is not hidden, I get no exception. If the device is first rotated, an exception will never occur, therefore, even if the sidebar is hidden and the device is rotated again, I get no exception. And the stack trace does not refer to a single function in my code, so I can even find the root cause.
This seems to be a function in FragmentManager.java (package android.app) that throws an exception:
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 }
There is no zero check on mAdded before trying to use it. The same function in JB replaces (mActive != null) with (mAdded != null) . But I have no idea what I can do for ICS as a workaround to avoid this.
Does anyone have any ideas? I looked at StackOverflow, looking for similar problems, but so far I got to nothing. Thank you If I need to post anything else, let me know and I will add it.