1.2: Fragment lifecycle and communications
Contents:
- Understanding the Fragment lifecycle
- Using Fragment lifecycle callbacks
- Using Fragment methods and the Activity context
- Communicating between a Fragment and an Activity
- Related practical
- Learn more
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
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 theFragment
in this callback. The system calls this method when theFragment
is created. Anything initialized inonCreate()
is preserved if theFragment
is paused and resumed.onCreateView()
: Inflate the XML layout for theFragment
in this callback. The system calls this method to draw theFragment
UI for the first time. As a result, theFragment
is visible in theActivity
. To draw a UI for yourFragment
, you must return the rootView
of yourFragment
layout. Returnnull
if theFragment
does not have a UI.onPause()
: Save any data and states that need to survive beyond the destruction of theFragment
. 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 theFragment
. - 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 theActivity
to resume aFragment
that is visible to the user and actively running.onAttach()
: Called when aFragment
is first attached to a hostActivity
. Use this method to check if theActivity
has implemented the required listener callback for theFragment
(if a listener interface was defined in theFragment
). After this method,onCreate()
is called.onActivityCreated()
: Called when theActivity
onCreate()
method has returned. Use it to do final initialization, such as retrieving views or restoring state. It is also useful for aFragment
that usessetRetainInstance()
to retain its instance, as this callback tells theFragment
when it is fully associated with the newActivity
instance. TheonActivityCreated()
method is called afteronCreateView()
and beforeonViewStateRestored()
.onDestroyView()
: Called when theView
previously created byonCreateView()
has been detached from theFragment
. This call can occur if the hostActivity
has stopped, or theActivity
has removed theFragment
. Use it to perform some action, such as logging a message, when theFragment
is no longer visible. The next time theFragment
needs to be displayed, a newView
is created. TheonDestroyView()
method is called afteronStop()
and beforeonDestroy()
.
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 hostingActivity
maintains a back stack, and you have to explicitly add aFragment
to that back stack by callingaddToBackStack()
during any transaction that adds theFragment
.
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
.
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
:
- Define a listener interface, with one or more callback methods to communicate with the
Activity
. - Override the
onAttach()
lifecycle method to make sure the hostActivity
implements the interface. - Call the interface callback method to pass data as a parameter.
In the host Activity
, follow these steps:
- Implement the interface defined in the
Fragment
. (All theActivity
classes that use theFragment
have to implement the interface.) - 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
.
Related practical
The related practical documentation is Communicating with a Fragment.
Learn more
Android developer documentation: