Published on

Android Lifecycle: Differences Between Ways to Launch Fragments in Android Applications

Continuing the article about Android Lifecycle. In this post, I will write in detail about the differences between the ways to launch Fragments in an Android application.

Reviewing Fragment Basics

What is a Fragment

A Fragment is a unit of UI representation in an Android application. An Android Activity can contain multiple Fragments, and each Fragment can also contain multiple child Fragments within it.

One unique aspect of Fragments is that they can be used as a Layout View. This means that on one screen, at one time, multiple Fragments can be displayed, and this is allowed by the Android operating system. However, due to performance issues, using multiple Fragments displayed on one Activity screen is not recommended by the community.

Managing Fragments in an Activity

In Android, you can manage Fragments through the AppCompatActivity class, which inherits from FragmentActivity. When you dive into the code of FragmentActivity, you will find a getter method called supportFragmentManager. AppCompatActivity / FragmentActivity uses this supportFragmentManager to manage the lifecycle of the child Fragments within it.

Managing Fragments within a Fragment

Looking at the Fragment class, there are two FragmentManagers: getParentFragmentManager and getChildFragmentManager. Let me explain the difference between these two FragmentManagers.

  • getParentFragmentManager: This is the Fragment Manager that the current Fragment belongs to. Simply put, if the Fragment is shown from an Activity, this is the supportFragmentManager of that Activity.
  • getChildFragmentManager: This is the Fragment Manager that the Fragment creates to manage its child Fragments.

FAQ:

  1. Suppose FragmentA uses getParentFragmentManager to show FragmentB. What is the getParentFragmentManager of FragmentB in this case?
  2. Suppose FragmentA uses getChildFragmentManager to show FragmentB. What is the getParentFragmentManager of FragmentB in this case?

Remember these concepts! Nowadays, we have the help of the Jetpack Navigation Component for managing navigation between multiple Fragments. However, understanding the operations of supportFragmentManager is essential for a deeper understanding of the system, which is very useful for debugging.

Methods of FragmentManager to Launch a Fragment

Method beginTransaction.add()

There are many add methods in fragmentManager.beginTransaction, which makes learning Fragments challenging because learners cannot know all the use cases of each method. The add method I am referring to here is:

@NonNull
public FragmentTransaction add(@IdRes int containerViewId, @NonNull Fragment fragment) {
    doAddOp(containerViewId, fragment, null, OP_ADD);
    return this;
}

This method takes a containerViewId and a Fragment object.

containerViewId is a View Id defined earlier in the Layout XML file (of the Fragment or FragmentActivity layout). The purpose of passing this View Id is for FragmentManager to locate the correct ViewId and 'add' the Fragment object on top of the located View Id.

The interesting part of using the add method is that if the containerViewId is an empty layout, it will add the Fragment object on top. However, if the containerViewId already has content inside it, or even already has a Fragment inside, you can still add another Fragment. The newly added Fragment will be on top. Refer to my GitHub Repo for source code and customize it as you wish.

Method beginTransaction.replace()

Similar to the add method, here is the replace method I am referring to:

@NonNull
public FragmentTransaction replace(@IdRes int containerViewId, @NonNull Fragment fragment,
        @Nullable String tag)  {
    if (containerViewId == 0) {
        throw new IllegalArgumentException("Must use non-zero containerViewId");
    }
    doAddOp(containerViewId, fragment, tag, OP_REPLACE);
    return this;
}

This method also takes a containerViewId and a Fragment object to represent a Fragment.

The requirements for containerViewId and Fragment are entirely similar to the add method. However, since this is a replace method, FragmentManager will replace the Fragment currently in the containerViewId with a new Fragment. This means that the existing Fragments in the containerViewId will be removed and replaced with the new Fragment.

Try it out by checking out my GitHub Repo and running the examples. It will be very interesting!

Saving Backstack State in FragmentManager

A common issue for newbies is that once they master and understand the add and replace methods in FragmentManager, they wonder how to manage them in a Backstack.

Backstack here is understood as a Stack that manages the order of states. This model is applied to help us manage screens easily. Backstack helps remember the screens shown and recover them when needed.

Specific case:

The app shows MainActivity, then adds FragmentA to MainActivity. At this point, the User is on FragmentA. Then the User performs an action to transition to FragmentB to read some information. After using FragmentB, the User wants to go back to FragmentA. What should the developer do to show FragmentA again?

To handle this case, you cannot call the add or replace method again to show a new FragmentA. This is because the previous state of FragmentA has not been saved. Showing a new FragmentA would require additional logic to restore the previous state of FragmentA correctly.

Google's Android developers understood this need and equipped FragmentManager with a method called addToBackStack. The addToBackStack method saves the data of the Fragments added to a containerViewId and automatically restores them when needed (e.g., popping the Fragment Backstack, User pressing the back button, or activity being re-created).

Methods beginTransaction# remove, hide, show, ...

The essence of FragmentManager is a complex data structure that allows developers to add, remove, replace, hide, and show Fragments within an Activity or Fragment. Google's Android developers have provided all the necessary methods for us to manage our Fragments through FragmentManager. However, in practice, I rarely memorize all these methods and use them. What we need is to understand the overall operation and know how to use them when needed. If you want to dive deeper into the remove, hide, show, ... methods, refer to Google's documentation for more information. In this article, I will not provide additional examples for these methods.

Understanding Fragment Transactions

If you look into the source code of FragmentManager, you will see the beginTransaction method:

@NonNull
public FragmentTransaction beginTransaction() {
    return new BackStackRecord(this);
}

Here, a Transaction can be understood as a BackstackRecord (BackstackRecord inherits from the FragmentTransaction class), and this BackstackRecord is managed by FragmentManager. Inside BackstackRecord, there are data structures to manage each action interacting with FragmentManager.

private final FragmentFactory mFragmentFactory;
private final ClassLoader mClassLoader;

ArrayList mOps = new ArrayList();
int mEnterAnim;
int mExitAnim;
int mPopEnterAnim;
int mPopExitAnim;
int mTransition;
boolean mAddToBackStack;
boolean mAllowAddToBackStack = true;
@Nullable String mName;

int mBreadCrumbTitleRes;
CharSequence mBreadCrumbTitleText;
int mBreadCrumbShortTitleRes;
CharSequence mBreadCrumbShortTitleText;

ArrayList mSharedElementSourceNames;
ArrayList mSharedElementTargetNames;
boolean mReorderingAllowed = false;

ArrayList mCommitRunnables;

These data structures are used in FragmentTransaction to manage actions in FragmentManager.

Understanding this will make it easier for you to read project code related to Fragment handling through FragmentManager.

Good luck!