FragmentManager NullPointerException when trying to commitAllowingStateLoss

10,810

Solution 1

The FragmentManager manages all Fragments at the Activity level, and their lifecycle will be tied to that parent Activity. The child Fragment manager manages all Fragments at the Fragment level, and their lifecycle will be tied to that parent Fragment.

So for your phone architecture, add your InnerFragment to your Activity using getFragmentManager(). When the Activity destroys for good (via back button / finish()), the FragmentManager will destroy and release the InnerFragment for you.

For your tablet architecture, add your InnerFragments to your Fragment using getChildFragmentManager() (in the latest support library). When the Fragment destroys for good, the FragmentManager will destroy and release the InnerFragments for you.

You should not have to manage releasing and destroying your Fragments yourself. I'd recommend logging the lifecycle events of your Activities and Fragments so you can watch them go through their states and ensure correct behavior.

Solution 2

The NullPointerException is caused by the fact that Activity's Handler is unset from the FragmentManager, so a "solution" that will prevent the crash is the following:

public void onDestroy(){
        super.onDestroy();
        try {
            Field mActivityField = getFragmentManager().getClass().getDeclaredField("mActivity");
            mActivityField.setAccessible(true);
            mActivityField.set(getFragmentManager(), this);

            Field mPendingActionsField = getFragmentManager().getClass().getDeclaredField("mPendingActions");
            mPendingActionsField.setAccessible(true);
            mPendingActionsField.set(getFragmentManager(), null);


            Field f = Activity.class.getDeclaredField("mHandler");
            f.setAccessible(true);
            Handler handler = (Handler) f.get(this);
            handler.close();
        } catch (Throwable e) {

        }
}
Share:
10,810
Bogdan Zurac
Author by

Bogdan Zurac

Android developer with an unhealthy passion for mobile and technology.

Updated on June 11, 2022

Comments

  • Bogdan Zurac
    Bogdan Zurac about 2 years

    Context: I have an Activity with a Fragment and 3 InnerFragments. When the Fragment onDestroy() is called, I want to remove the inner fragments from the FragmentManager. The code from onDestroy() is below.

    Problem: FragmentManager throws NullPointerException, probably when commitAllowingStateLoss() is called. I don't understand why.

    @Override
    public void onDestroy()
    {
        super.onDestroy();
        if (getFragmentManager().findFragmentById(R.id.fragment_framelayout_left) != null)
        {
            FragmentTransaction fragmentTransaction = getFragmentManager().beginTransaction();
            fragmentTransaction.remove(mLeftFragment);
            fragmentTransaction.commitAllowingStateLoss();
        }
    }
    

    Stack trace:

    02-11 12:15:14.162: E/AndroidRuntime(25911): FATAL EXCEPTION: main
    02-11 12:15:14.162: E/AndroidRuntime(25911): java.lang.NullPointerException
    02-11 12:15:14.162: E/AndroidRuntime(25911):    at android.support.v4.app.FragmentManagerImpl.execPendingActions(FragmentManager.java:1419)
    02-11 12:15:14.162: E/AndroidRuntime(25911):    at android.support.v4.app.FragmentManagerImpl$1.run(FragmentManager.java:429)
    02-11 12:15:14.162: E/AndroidRuntime(25911):    at android.os.Handler.handleCallback(Handler.java:725)
    02-11 12:15:14.162: E/AndroidRuntime(25911):    at android.os.Handler.dispatchMessage(Handler.java:92)
    02-11 12:15:14.162: E/AndroidRuntime(25911):    at android.os.Looper.loop(Looper.java:137)
    02-11 12:15:14.162: E/AndroidRuntime(25911):    at android.app.ActivityThread.main(ActivityThread.java:5039)
    02-11 12:15:14.162: E/AndroidRuntime(25911):    at java.lang.reflect.Method.invokeNative(Native Method)
    02-11 12:15:14.162: E/AndroidRuntime(25911):    at java.lang.reflect.Method.invoke(Method.java:511)
    02-11 12:15:14.162: E/AndroidRuntime(25911):    at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:793)
    02-11 12:15:14.162: E/AndroidRuntime(25911):    at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:560)
    02-11 12:15:14.162: E/AndroidRuntime(25911):    at dalvik.system.NativeStart.main(Native Method)
    
  • Bogdan Zurac
    Bogdan Zurac over 11 years
    Awesome. Thanks a lot. This solved it. One slight problem. If I use ChildFragmentManager, I can't setRetainInstance(true) on the child Fragments. Is there a workaround ? Or why should it behave like this ?
  • Steven Byle
    Steven Byle over 11 years
    Using setRetainInstance(true) is actually already a workaround. The Google suggested approach is to save any variable state in onSaveInstanceState() and restore it in onCreate and onCreateView. At any time, the OS may run low on memory and need to destroy your Fragments and/or Activities, so it is their responsibility to be able to save how they currently are, and restore that state to appear to the user as nothing has changed when they return. Here's how Google recommends you save state developer.android.com/training/basics/activity-lifecycle/….
  • Steven Byle
    Steven Byle over 11 years
    And here's some more info about setRetainInstance(true) - stackoverflow.com/questions/11182180/…
  • Bogdan Zurac
    Bogdan Zurac over 11 years
    Yeah, I knew how to save the state for Activities. For fragments I always used setRetainInstance. But I'll have a look into it. Thanks a lot mate ! You've been more than helpful !
  • Steven Byle
    Steven Byle over 11 years
    Ah good, well saving state for Fragments is very similar to Activitys. The trick to remember is to restore your variables in onCreate (not onCreateView) when savedInstanceState != null, and then setup your views and set those restored values to them in onCreateView. If you have your Fragments on a back stack, when you hit back to return to them, onCreateView gets called, but onCreate does not. So if you try to restore/default variables in onCreateView (like I first did), things will get messed up. Hope this helps.