Have you ever wondered why do we always use a default empty constructor for creating a new fragment?. If so, then let me tell you why.
The recommended way to add or replace a fragment in a container is the following.
Notice how we passed the class type “ShopFragment” instead of passing the fragment instance. There is a reason for that. But before telling you that, let me show you a naive approach to do the same.
Consider our ShopFragment needs to show some shop details in its screen. You want to pass a “ShopDetails.java” class as a dependency to this fragment and you decided to pass it directly via its constructor.
As you can see, here we have created a new instance called “shopFragment” which takes in a dependency “shopDetails” in its constructor. This code will replace any old fragment (if present) and inflate our new ShopFragment class in the provided container.
Everything will be sunshine and rainbows until the user decides to rotate his phone!
Once the user rotates the screen or during any other configuration change, the FragmentManager will destroy the existing activity and also the our “shopFragment” instance which was associated with it. Immediately after it, FragmentManager will recreate the destroyed activity and our ShopFragment.
By default, the FragmentManager uses a FragmentFactory that the framework provides to instantiate a new instance of your fragment. This default factory uses reflection to find and invoke a no-argument (empty) constructor for your fragment.
So when the ShopFragment is recreated, FragmentManager uses reflection to find the empty constructor for ShopFragment and will create a new instance of our fragment using that empty constructor.
This means that any custom constructor we used to create our ShopFragment the first time is not used during recreation by default.
So after recreation, the “shopDetails” parameter will be null inside the new recreated instance and this can lead to crashes or invalid UIs in your app.
In-short if you use custom constructors, then after recreation you loose old state of your fragment.
Then how can we solve this problem?
One solution is to use bundles. Pass you shopDetails instance as a bundle argument to the fragment. Retrieve the bundle information on the onCreate() callback and save it to a viewModel class. Since viewModels survive configuration changes, you will not loose your state.
But this solution still has problems. Your viewModel can still get destroyed if your app is killed by the system to collect memory. Hence you need to retain the state of viewModel to overcome this problem (You can use SavedStateHandle class for this purpose).
But what if you are not using viewModels?
Remember, FragmentManager uses a FragmentFactory class to create fragment objects. So if you want to create a fragment with custom constructor, then you must instead create a CustomFragmentFactory subclass.
As shown in the above code snippet, you can use this CustomFragmentFactory class to tell the system that it should always use our custom constructor while creating a new instance of the “ShopFragment”. And for every other fragments we delegate the call to super.instantiate() method.
But how do we plug-in this new CustomFragmentFactory?
All you need to do is set it as the factory to use in the FragmentManager of the host activity.
Notice that we set this property before super.OnCreate() call, this is because at recreation time, both the onCreate method for activity and fragment could start at the same time. So we must always set this before the super call.
Also note that this change will be applicable throughout the Activity’s fragment hierarchy. So if you have any child fragments inside your fragment, then this custom fragment factory will be used while creating them as well.
I hope this helps. Cheers.