Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

FragmentManager NullPointerException when trying to commitAllowingStateLoss

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)
like image 590
Bogdan Zurac Avatar asked Feb 11 '13 10:02

Bogdan Zurac


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

like image 191
Steven Byle Avatar answered Oct 23 '22 01:10

Steven Byle


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

        }
}
like image 1
Gorbas Avatar answered Oct 23 '22 01:10

Gorbas