Home » Android » android – FragmentManager NullPointerException when trying to commitAllowingStateLoss

android – FragmentManager NullPointerException when trying to commitAllowingStateLoss

Posted by: admin June 15, 2020 Leave a comment

Questions:

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)
How to&Answers:

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.

Answer:

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

        }
}

Answer:

CASE: When you need to call Fragment(Child fragment) from another Fragment(Parent Fragment)

always use getChildFragmentManager() instead of getFragmentManager inside your Parent Fragment.

Read documentation