Android Fragments and initialisation data

I’ve seen too many people initialising fragments with data by overriding the fragment constructor, passing the data in the constructor parameters, and assigning it to private fields where it can be used throughout the fragment. Or they create a public accessor in the fragment’s owning activity and cast getActivity() in the fragment to the activity type so they can use the accessor.

This is just plain wrong and will lead to runtime exceptions. The correct way is not difficult, so I thought I’d write a quick post about why it’s wrong, and how to do it properly.

The Requirement

Fragments don’t exist in isolation, they are part of the whole app structure, and interact with the user with inputs and/or outputs. Therefore it is quite common when creating a fragment from an activity that you need to initialise it with some data. For example, if you have an activity where the user can enter a search string, when they click on the search button you might want to create a fragment that retrieves data that matches the user’s search term and displays that data.

The (Wrong) Solution

A fragment in your app is just a class that extends Android’s Fragment class or one of it’s subclasses. It can, therefore, have a custom constructor (or many if you so desire) that can be used to pass data to the fragment. You could then create a field in your fragment class and assign the constructor parameter to this field where it can be used anywhere in the fragment:

You could then use this in your activity like this:

This seems a reasonable solution, and in fact it will work quite adequately most of the time, but there are hidden dangers with this approach.

 The Problem

The problem with this solution is related to the activity and fragment lifecycle. The OS can re-create your fragment independently of the activity, for example due to a configuration change such as screen rotation, or when your app is re-launched from the background. In this case Android will create a new copy of the fragment using the default (parameterless) constructor. This will lead to a runtime instantiation exception because there is no default constructor on our fragment class.

You could get around this by creating a default parameterless class for this fragment, but then of course the searchString field will not be initialised when the OS uses this constructor, so you will get a null pointer exception when trying to use it as we are in the onCreateView method.

The Correct Solution

As I said in the introduction, the solution to this is not difficult. All we need to do is create a static initialisation method in the fragment, pass the required data to this method, and then save the data in the fragment’s argument bundle. The data can then be got from the argument bundle when it is needed:

And you would use it like this:

So why does this work? When we first create the fragment we are calling the newInstance method, passing the required data and saving it to the argument bundle. This bundle is automatically saved by the OS when the fragment is destroyed and re-created, therefore all the data that is saved in the bundle is automatically available in the new version of the fragment that the OS created using the default constructor.

So not only do we have a nice simple way of using fragments without crashes, I think it is a much neater pattern for our classes, with a nicely defined separation. You could even put some logic in the fragment’s onCreate or onCreateView to make sure the required data has been set in the argument bundle, for example by throwing an exception if it’s not there, that way if another developer re-used your fragment without passing the required data they would find out as soon as they tried to run it.

Leave a Reply

Your email address will not be published. Required fields are marked *