FragmentManager NullPointerException when trying to commitAllowingStateLoss
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) {
}
}
![Bogdan Zurac](https://i.stack.imgur.com/MbNQe.jpg?s=256&g=1)
Bogdan Zurac
Android developer with an unhealthy passion for mobile and technology.
Updated on June 11, 2022Comments
-
Bogdan Zurac about 2 years
Context: I have an
Activity
with aFragment
and 3InnerFragments
. When theFragment
onDestroy()
is called, I want to remove the inner fragments from theFragmentManager
. The code fromonDestroy()
is below.Problem:
FragmentManager
throwsNullPointerException
, probably whencommitAllowingStateLoss()
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 over 11 yearsAwesome. 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 over 11 yearsUsing
setRetainInstance(true)
is actually already a workaround. The Google suggested approach is to save any variable state inonSaveInstanceState()
and restore it inonCreate
andonCreateView
. At any time, the OS may run low on memory and need to destroy yourFragments
and/orActivities
, 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 over 11 yearsAnd here's some more info about
setRetainInstance(true)
- stackoverflow.com/questions/11182180/… -
Bogdan Zurac over 11 yearsYeah, 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 over 11 yearsAh good, well saving state for
Fragment
s is very similar toActivity
s. The trick to remember is to restore your variables inonCreate
(notonCreateView
) whensavedInstanceState != null
, and then setup your views and set those restored values to them inonCreateView
. If you have yourFragment
s on a back stack, when you hit back to return to them,onCreateView
gets called, butonCreate
does not. So if you try to restore/default variables inonCreateView
(like I first did), things will get messed up. Hope this helps.