1.2: Fragment lifecycle and communications

Contents:

Like an Activity, a Fragment has its own lifecycle. Understanding the relationship between Activity and Fragment lifecycles helps you design fragments that can save and restore variables and communicate with activities.

An Activity that hosts a Fragment can send information to that Fragment, and receive information from that Fragment. This chapter describes the mechanisms for passing data and how to manage the Fragment lifecycle within an Activity.

Understanding the Fragment lifecycle

Using a Fragment lifecycle is a lot like using an Activity lifecycle (see The Activity Lifecycle for details). Within the Fragment lifecycle callback methods, you can declare how your Fragment behaves when it is in a certain state, such as active, paused, or stopped.

Fragment lifecycle states

The Fragment is added by an Activity (which acts as the host of the Fragment). Once added, the Fragment goes through three states, as shown in the figure below:

  • Active (or resumed)
  • Paused
  • Stopped  The fragment states in the lifecycle

How the Activity state affects the Fragment

Because a Fragment is always hosted by an Activity, the Fragment lifecycle is directly affected by the host Activity lifecycle. For example, when the Activity is paused, so are all Fragments in it, and when the Activity is destroyed, so are all Fragments.

Each lifecycle callback for the Activity results in a similar callback for each Fragment, as shown in the following table. For example, when the Activity receives onPause(), it triggers a Fragment onPause() for each Fragment in the Activity.

Activity State Fragment Callbacks Triggered Fragment Lifecycle
Created onAttach(), onCreate(), onCreateView(), onActivityCreated() Fragment is added and its layout is inflated.
Started onStart() Fragment is active and visible.
Resumed onResume() Fragment is active and ready for user interaction.
Paused onPause() Fragment is paused because the Activity is paused.
Stopped onStop() Fragment is stopped and no longer visible.
Destroyed onDestroyView(), onDestroy(), onDetach() Fragment is destroyed.

As with an Activity, you can save the variable assignments in a Fragment. Because data in a Fragment is usually relevant to the Activity that hosts it, your Activity code can use a callback to retrieve data from the Fragment, and then restore that data when recreating the Fragment. You learn more about communicating between an Activity and a Fragment later in this chapter.

Tip: For more information about the Activity lifecycle and saving state, see The Activity Lifecycle.

Using Fragment lifecycle callbacks

As you would with Activity lifecycle methods, you can override Fragment lifecycle methods to perform important tasks when the Fragment is in certain states. Most apps should implement at least the following methods for every Fragment:

  • onCreate(): Initialize essential components and variables of the Fragment in this callback. The system calls this method when the Fragment is created. Anything initialized in onCreate() is preserved if the Fragment is paused and resumed.
  • onCreateView(): Inflate the XML layout for the Fragment in this callback. The system calls this method to draw the Fragment UI for the first time. As a result, the Fragment is visible in the Activity. To draw a UI for your Fragment, you must return the root View of your Fragment layout. Return null if the Fragment does not have a UI.
  • onPause(): Save any data and states that need to survive beyond the destruction of the Fragment. The system calls this method if any of the following occurs:

    • The user navigates backward.
    • The Fragment is replaced or removed, or another operation is modifying the Fragment.
    • The host Activity is paused.

A paused Fragment is still alive (all state and member information is retained by the system), but it will be destroyed if the Activity is destroyed. If the user presses the Back button and the Fragment is returned from the back stack, the lifecycle resumes with the onCreateView() callback.

The Fragment class has other useful lifecycle callbacks:

  • onResume(): Called by the Activity to resume a Fragment that is visible to the user and actively running.
  • onAttach(): Called when a Fragment is first attached to a host Activity. Use this method to check if the Activity has implemented the required listener callback for the Fragment (if a listener interface was defined in the Fragment). After this method, onCreate()is called.
  • onActivityCreated(): Called when the Activity onCreate() method has returned. Use it to do final initialization, such as retrieving views or restoring state. It is also useful for a Fragment that uses setRetainInstance() to retain its instance, as this callback tells the Fragment when it is fully associated with the new Activity instance. The onActivityCreated() method is called after onCreateView() and before onViewStateRestored().
  • onDestroyView(): Called when the View previously created by onCreateView() has been detached from the Fragment. This call can occur if the host Activity has stopped, or the Activity has removed the Fragment. Use it to perform some action, such as logging a message, when the Fragment is no longer visible. The next time the Fragment needs to be displayed, a new View is created. The onDestroyView() method is called after onStop() and before onDestroy().

For a complete description of all Fragment lifecycle callbacks, see Fragment.

Using Fragment methods and the Activity context

An Activity can use methods in a Fragment by first acquiring a reference to the Fragment. Likewise, a Fragment can get a reference to its hosting Activity to access resources, such as a View.

Using the Activity context in a Fragment

When a Fragment is in the active or resumed state, it can get a reference to its hosting Activity instance using getActivity(). It can also perform tasks such as finding a View in the Activity layout:

View listView = getActivity().findViewById(R.id.list);

Note that if you call getActivity() when the Fragment is not attached to an Activity, getActivity() returns null.

Using the Fragment methods in the host Activity

Likewise, your Activity can call methods in the Fragment by acquiring a reference to the Fragment from FragmentManager, using findFragmentById(). For example, to call the getSomeData() method in the Fragment, acquire a reference first:

ExampleFragment fragment = (ExampleFragment)
                getFragmentManager().findFragmentById(R.id.example_fragment);
// ...
mData = fragment.getSomeData();

Adding the Fragment to the back stack

A significant difference between an Activity and a Fragment is how activities and fragments use their respective back stacks, so that the user can navigate back with the Back button (as discussed in Tasks and Back Stack).

  • For an Activity, the system automatically maintains a back stack of activities.
  • For a Fragment, the hosting Activity maintains a back stack, and you have to explicitly add a Fragment to that back stack by calling addToBackStack() during any transaction that adds the Fragment.

Keep in mind that when your app replaces or removes a Fragment, it's often appropriate to allow the user to navigate backward and "undo" the change. To allow the user to navigate backward through Fragment transactions, call addToBackStack() before you commit the FragmentTransaction:

fragmentTransaction.add(R.id.fragment_container, fragment);
fragmentTransaction.addToBackStack(null);
fragmentTransaction.commit();

Tip: The addToBackStack() method takes an optional string parameter that specifies a unique name for the transaction. Specify null because the name isn't needed unless you plan to perform advanced Fragment operations using the FragmentManager.BackStackEntry interface.

When removing a Fragment, remember that the hosting Activity maintains a back stack for the Fragment (if you add the transaction to it, as described above). However, the Fragment is not destroyed. If the user navigates back to restore the Fragment, it restarts.

Communicating between a Fragment and an Activity

The Activity that hosts a Fragment can send information to that Fragment, and receive information from that Fragment, as described in "Sending data from a fragment to its host activity" in this chapter.

However, a Fragment can't communicate directly with another Fragment. All Fragment-to-Fragment communication is done through the Activity that hosts them. One Fragment communicates with the Activity, and the Activity communicates to the other Fragment.  Communication between fragments and the activity

Sending data to a Fragment from an Activity

To send data to a Fragment from an Activity, set a Bundle and use the Fragment method setArguments(Bundle) to supply the construction arguments for the Fragment.

For example, if the user previously made a choice in the Fragment, and you want to send that choice back to the Fragment when starting it from the Activity, you would create the arguments Bundle and insert the String value of the choice into the Bundle mapping for the key ("choice").

The best practice for initializing the data in a Fragment is to perform this initialization in the Fragment in a factory method. As you learned in the lesson on fragments, you can create the Fragment instance with a newinstance() method in the Fragment itself:

public static SimpleFragment newInstance() {
        return new SimpleFragment();
}

Then instantiate the Fragment in an Activity by calling the newInstance() method in the Fragment:

SimpleFragment fragment = SimpleFragment.newInstance();

Tip: If you choose New > Fragment > Fragment Blank to add a Fragment to your Android Studio project, and you select the Include fragment factory methods option, Android Studio automatically adds a newinstance() method to the Fragment as a factory method to set arguments for the Fragment when the Fragment is called by the Activity.

For example, to open the Fragment with the user's previously selected choice, all you need to do in the Activity is to provide the preselected choice as an argument for the newInstance() method when instantiating the Fragment in the Activity:

SimpleFragment fragment = SimpleFragment.newInstance(mRadioButtonChoice);

The newInstance() factory method in the Fragment can use a Bundle and the setArguments(Bundle) method to set the arguments before returning the Fragment:

public static SimpleFragment newInstance(int choice) {
        SimpleFragment fragment = new SimpleFragment();
        Bundle arguments = new Bundle();
        arguments.putInt(CHOICE, choice);
        fragment.setArguments(arguments);
        return fragment;
}

The following shows how you would use getSupportFragmentManager() to get an instance of FragmentManager, create the fragmentTransaction, acquire the Fragment using the layout resource, and then create the Bundle.

// Get the FragmentManager and start a transaction.
FragmentManager fragmentManager = getSupportFragmentManager();
FragmentTransaction fragmentTransaction = fragmentManager
                                          .beginTransaction();
// Instantiate the fragment.
SimpleFragment fragment =
                SimpleFragment.newInstance(mRadioButtonChoice);
// Add the fragment.
fragmentTransaction.add(R.id.fragment_container, fragment).commit();

The SimpleFragment newInstance() method receives the value of the mRadioButtonChoice argument in choice:

public static SimpleFragment newInstance(int choice) {
        SimpleFragment fragment = new SimpleFragment();
        Bundle arguments = new Bundle();
        arguments.putInt(CHOICE, choice);
        fragment.setArguments(arguments);
        return fragment;
}

Before you draw the View for the Fragment, retrieve the passed-in arguments from the Bundle. To do this, use getArguments() in the Fragment onCreate() or onCreateView() callback, and if mRadioButtonChoice is not NONE, check the radio button for the choice:

if (getArguments().containsKey(CHOICE)) {
    // A choice was made, so get the choice.
    mRadioButtonChoice = getArguments().getInt(CHOICE);
    // Check the radio button choice.
    if (mRadioButtonChoice != NONE) {
         radioGroup.check
             (radioGroup.getChildAt(mRadioButtonChoice).getId());
    }
}

Sending data from a Fragment to its host Activity

To have a Fragment communicate to its host Activity, follow these steps in the Fragment:

  1. Define a listener interface, with one or more callback methods to communicate with the Activity.
  2. Override the onAttach() lifecycle method to make sure the host Activity implements the interface.
  3. Call the interface callback method to pass data as a parameter.

In the host Activity, follow these steps:

  1. Implement the interface defined in the Fragment. (All the Activity classes that use the Fragment have to implement the interface.)
  2. Implement the Fragment callback method(s) to retrieve the data.

The following is an example of defining the OnFragmentInteractionListener interface in the Fragment, including the onRadioButtonChoice() callback to communicate to the host Activity. The Activity must implement this interface. The onAttach() method gets a reference to the listener if the Activity implemented this interface; if not, this method throws an exception:

// Interface definition and onFeedbackChoice() callback.
interface OnFragmentInteractionListener {
    void onRadioButtonChoice(int choice);
}

@Override
public void onAttach(Context context) {
    super.onAttach(context);
    if (context instanceof OnFragmentInteractionListener) {
        mListener = (OnFragmentInteractionListener) context;
    } else {
        throw new ClassCastException(context.toString()
                    + " must implement OnFragmentInteractionListener");
    }
}

The following shows how the Fragment uses the onCheckedChanged() listener for checked radio buttons in the Fragment, and uses the onRadioButtonChoice() callback to provide data to the host Activity.

public void onCheckedChanged(RadioGroup group, int checkedId) {
    View radioButton = radioGroup.findViewById(checkedId);
    int index = radioGroup.indexOfChild(radioButton);
    switch (index) {
        case YES: // User chose "Yes."
            textView.setText(R.string.yes_message);
            mRadioButtonChoice = YES;
            mListener.onRadioButtonChoice(YES);
            break;
        case NO: // User chose "No."
            textView.setText(R.string.no_message);
            mRadioButtonChoice = NO;
            mListener.onRadioButtonChoice(NO);
            break;
        default: // No choice made.
            mRadioButtonChoice = NONE;
            mListener.onRadioButtonChoice(NONE);
            break;
    }
}

To use the Fragment callback method(s) to retrieve data, the Activity must implement the interface defined in the Fragment class:

public class MainActivity extends AppCompatActivity
                implements SimpleFragment.OnFragmentInteractionListener {
   //...
}

The Activity can then use the onRadioButtonChoice() callback to get the data.

@Override
public void onRadioButtonChoice(int choice) {
    mRadioButtonChoice = choice;
    Toast.makeText(this, "Choice is " + Integer.toString(choice),
                                            LENGTH_SHORT).show();
}

Some data in a Fragment is may be relevant to the Activity that hosts it. Your Activity code can use a callback to retrieve relevant data from the Fragment. The Activity can then send that data to the Fragment when recreating the Fragment.

The related practical documentation is Communicating with a Fragment.

Learn more

Android developer documentation:

results matching ""

    No results matching ""