I am at a loss for this. I am switching tabs manually in my ViewPager. I have this code inside my Activity:
@Override
public void onBackPressed()
{
if (childFragmentStack.empty())
{
// Go to the devices screen
Intent intent = new Intent(this, SelectDeviceActivity.class);
startActivity(intent);
}
else
{
Fragment fragment = childFragmentStack.pop();
if (fragment == null)
{
return;
}
processingBackStack = true;
if (fragment instanceof ViewChildFragment)
{
viewFragment.activateFragment((ViewChildFragment) fragment);
mViewPager.setCurrentItem(VIEW_FRAGMENT_INDEX, true);
}
else if (fragment instanceof SetupChildFragment)
{
setupFragment.activateFragment((SetupChildFragment) fragment);
mViewPager.setCurrentItem(SETUP_FRAGMENT_INDEX, true); //**
}
else if (fragment == homeFragment)
{
mViewPager.setCurrentItem(HOME_FRAGMENT_INDEX, true); //**
}
processingBackStack = false;
}
}
If I scroll between tabs I add them to a Stack (the 'childFragmentStack'). I am using FragmentPagerAdapter to handle the fragments. What happens is if I do something like View->Setup->View->Setup and then reverse it, it only gets as far as Setup->View->CRASH. It's like when I press Back the Setup Fragment is no longer valid for what I'm doing, but it is never recreated! The Setup fragment is only created in MainActivity.onCreate(), so it should still be around and valid.
The NPE happens on the lines I marked **. Here is the full stack trace:
04-18 16:04:57.096: E/AndroidRuntime(13072): FATAL EXCEPTION: main
04-18 16:04:57.096: E/AndroidRuntime(13072): java.lang.NullPointerException
04-18 16:04:57.096: E/AndroidRuntime(13072): at android.support.v4.app.Fragment.setUserVisibleHint(Fragment.java:841)
04-18 16:04:57.096: E/AndroidRuntime(13072): at android.support.v4.app.FragmentPagerAdapter.setPrimaryItem(FragmentPagerAdapter.java:130)
04-18 16:04:57.096: E/AndroidRuntime(13072): at android.support.v4.view.ViewPager.populate(ViewPager.java:1066)
04-18 16:04:57.096: E/AndroidRuntime(13072): at android.support.v4.view.ViewPager.setCurrentItemInternal(ViewPager.java:550)
04-18 16:04:57.096: E/AndroidRuntime(13072): at android.support.v4.view.ViewPager.setCurrentItemInternal(ViewPager.java:509)
04-18 16:04:57.096: E/AndroidRuntime(13072): at android.support.v4.view.ViewPager.setCurrentItem(ViewPager.java:501)
04-18 16:04:57.096: E/AndroidRuntime(13072): at com.lochinvar.serf.MainActivity.onBackPressed(MainActivity.java:234)
04-18 16:04:57.096: E/AndroidRuntime(13072): at android.app.Activity.onKeyUp(Activity.java:2131)
04-18 16:04:57.096: E/AndroidRuntime(13072): at android.view.KeyEvent.dispatch(KeyEvent.java:2633)
04-18 16:04:57.096: E/AndroidRuntime(13072): at android.app.Activity.dispatchKeyEvent(Activity.java:2361)
04-18 16:04:57.096: E/AndroidRuntime(13072): at com.android.internal.policy.impl.PhoneWindow$DecorView.dispatchKeyEvent(PhoneWindow.java:1819)
04-18 16:04:57.096: E/AndroidRuntime(13072): at android.view.ViewRootImpl.deliverKeyEventPostIme(ViewRootImpl.java:3577)
04-18 16:04:57.096: E/AndroidRuntime(13072): at android.view.ViewRootImpl.handleImeFinishedEvent(ViewRootImpl.java:3547)
04-18 16:04:57.096: E/AndroidRuntime(13072): at android.view.ViewRootImpl$ViewRootHandler.handleMessage(ViewRootImpl.java:2797)
04-18 16:04:57.096: E/AndroidRuntime(13072): at android.os.Handler.dispatchMessage(Handler.java:99)
04-18 16:04:57.096: E/AndroidRuntime(13072): at android.os.Looper.loop(Looper.java:137)
04-18 16:04:57.096: E/AndroidRuntime(13072): at android.app.ActivityThread.main(ActivityThread.java:4745)
04-18 16:04:57.096: E/AndroidRuntime(13072): at java.lang.reflect.Method.invokeNative(Native Method)
04-18 16:04:57.096: E/AndroidRuntime(13072): at java.lang.reflect.Method.invoke(Method.java:511)
04-18 16:04:57.096: E/AndroidRuntime(13072): at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:786)
04-18 16:04:57.096: E/AndroidRuntime(13072): at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:553)
04-18 16:04:57.096: E/AndroidRuntime(13072): at dalvik.system.NativeStart.main(Native Method)
[EDIT] I forgot to mention I overrode FragmentPagerAdapter.getPageTitle() and it never returns null (default case goes to a string).
Finally! I'm now able to reliably recreate this error!
Another way that I found to recreate error is to close activity/app, and quickly reopen page with the ViewPager fragment. You may have to try a few times because in my tests I had to reopen the app within about 30ms. This time may be slower or faster for different speed devices.
The problem was that I only explicitly created the Fragment (using new
) once, and kept a reference to that instance so that I could reuse it. One simple fix to this problem is to always return a new
instance of the Fragment the FragmentPagerAdapter.getItem(...)
, as shown below.
public class ViewPagerAdapter extends FragmentPagerAdapter {
...
@Override
public Fragment getItem(int position) {
switch (position) {
case 0: return mMyFragment; // Error. Has the edge-case crash.
case 1: return new MyFragment(); // Works.
default: return new MyDefaultFragment();
}
}
}
ps - The root problem likely has something to do with the Fragment lifecycle and trying to use it again while it's being destroyed.
pps - This solution fixes the NullPointerException for android.app.Fragment.setUserVisibleHint(Fragment.java:997)
and should also work for android.support.v4.app.Fragment.setUserVisibleHint
.
This one was a real brain twister for me. We removed all of the code that replaced the Fragment
s and kept the same fragments for the entire lifecycle of the Activity
and still had this problem. It wasn't until we viewPager.setOffscreenPageLimit(TABS);
where TABS
is the number of tabs (in our case 4) that we stopped getting the referenced NullPointerException
.
FWIW -- I'm pretty sure the problem is in Google's code. We couldn't get this to fail on a Nexus 5 running Lollipop, but it fails across Samsung devices running Kitkat. When I traced through the error itself, it looked like the failure happens because the Fragment
being referenced has already gone through the internal Fragment.initState
function because the Fragment
's id
is -1.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With