10

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).

Mark Herscher
  • 1,761
  • 2
  • 19
  • 32
  • It looks like mViewPager is null, is there anywhere that could be set to null? – nasch Apr 20 '14 at 02:53
  • The stack trace has the exception inside Fragment.setUserVisibleHint(), so I don't think it's because mViewPager is null. And no, it's not null - it's only assigned in onCreate() and nowhere else. – Mark Herscher Apr 21 '14 at 14:36
  • Post your adapter code, as well as what the `activateFragment` method does. – Kevin Coppock Jun 02 '14 at 20:12

3 Answers3

4

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.

Anonsage
  • 8,030
  • 5
  • 48
  • 51
1

This one was a real brain twister for me. We removed all of the code that replaced the Fragments 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.

JohnnyLambada
  • 12,700
  • 11
  • 57
  • 61
0

I had a bad time of a very similar case (same Exception in Fragment.setUserVisibleHint) even if I used FragmentStatePagerAdapter. There was random (thus difficult to reproduce) crash of my app when the underlying datas where changed.

In my case, I finally was able to get rid of this crash by overriding the following method in the adapter :

@Override
public void restoreState(Parcelable state, ClassLoader loader) {
    // don't super !
}

I do think that there's a bug in this class, given the number of questions similar to this one in StackOverflow.

I hope this could be useful to someone.

Edit : I posted a similar answer to android.support.v4.app.Fragment.setUserVisibleHint null pointer on app resuming however, I'm not positively certain that these questions are duplicate. However, I think that this answer could help some people finding these for a similar symptom. How should I note this ?

Community
  • 1
  • 1
Orabîg
  • 11,718
  • 6
  • 38
  • 58