Mobile Development 30 min read

Master Android Fragments: Basics, Lifecycle, and Back Stack

This comprehensive guide explores Android Fragments in depth, covering their definition, modular advantages, core classes, dependency on support libraries, basic and dynamic usage, lifecycle methods, transaction handling, back‑stack mechanics, communication patterns, DialogFragment creation, ViewPager integration, lazy loading techniques, and practical code examples.

Tencent TDS Service
Tencent TDS Service
Tencent TDS Service
Master Android Fragments: Basics, Lifecycle, and Back Stack
Fragment is one of the most fundamental concepts in Android development. This article starts with why Fragments exist and then covers all aspects of Fragments, including basic definitions and usage, internal implementation of the back stack, communication, DialogFragment, ViewPager+Fragment usage, nested Fragments, lazy loading, and more.

The source code for the demo is available at https://github.com/xiazdong/FragmentDemo.

Basic Concepts

Fragment, abbreviated as "碎片", was introduced in Android 3.0 (API 11). To support lower versions, the support‑v4 library also provides a Fragment API, compatible back to Android 1.6.

Since version 24.2.0 the support‑v4 library has been modularized into several jars (support‑fragment, support‑ui, support‑media‑compat, etc.) to reduce APK size. Include only the modules you need.

To include the whole support‑v4 library: 'com.android.support:support-v4:24.2.1' To include only the fragment module:

com.android.support:support-fragment:24.2.1
Because the support libraries are continuously updated, it is recommended to use android.support.v4.app.Fragment instead of the system android.app.Fragment . When using the support Fragment, the Activity must extend FragmentActivity (or its subclass AppCompatActivity ).

Official definition:

A Fragment represents a behavior or a portion of user interface in an Activity. You can combine multiple fragments in a single activity to build a multi‑pane UI and reuse a fragment in multiple activities. A fragment has its own lifecycle, receives its own input events, and can be added or removed while the activity is running.

Fragment depends on an Activity and cannot exist independently.

An Activity can host multiple Fragments.

A Fragment can be reused by multiple Activities.

Fragment has its own lifecycle and can receive input events.

Fragments can be added or removed dynamically at runtime.

Advantages of Fragments

Modularity: code can be split into separate Fragment classes instead of a monolithic Activity.

Reusability: the same Fragment can be used in different Activities.

Adaptability: layouts can be adjusted easily for different screen sizes and orientations.

Core Fragment Classes Fragment: base class for all Fragments. FragmentManager: manages and maintains Fragments (abstract; implementation is FragmentManagerImpl). FragmentTransaction: performs add, remove, replace, etc., via a transaction (abstract; implementation is BackStackRecord).

Nested Fragments (supported from Android 4.2) can be managed via getChildFragmentManager() and getParentFragment().

Basic Usage

Define a class that extends Fragment:

public class Fragment1 extends Fragment {
    private static String ARG_PARAM = "param_key";
    private String mParam;
    private Activity mActivity;
    @Override
    public void onAttach(Context context) {
        mActivity = (Activity) context;
        mParam = getArguments().getString(ARG_PARAM); // get argument
    }
    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        View root = inflater.inflate(R.layout.fragment_1, container, false);
        TextView view = root.findViewById(R.id.text);
        view.setText(mParam);
        return root;
    }
    public static Fragment1 newInstance(String str) {
        Fragment1 frag = new Fragment1();
        Bundle bundle = new Bundle();
        bundle.putString(ARG_PARAM, str);
        frag.setArguments(bundle);
        return frag;
    }
}

The most commonly overridden method is onCreateView(). When inflating the layout, the third parameter must be false to avoid adding the view twice, which would cause an IllegalStateException.

Caused by: java.lang.IllegalStateException: The specified child already has a parent. You must call removeView() on the child's parent first.

Pass arguments to a Fragment via setArguments(Bundle) rather than a constructor, because the system can recreate the Fragment after a process kill and will restore the arguments.

It is strongly recommended that subclasses do not have other constructors with parameters, since these constructors will not be called when the fragment is re-instantiated.

In onAttach() you can retrieve arguments with getArguments(). Avoid calling getActivity() directly; instead cast the Context passed to onAttach() to Activity.

Adding a Fragment to an Activity can be done statically in XML (static) or dynamically at runtime (dynamic). Dynamic addition is preferred:

<FrameLayout
    android:id="@+id/container"
    android:layout_width="match_parent"
    android:layout_height="match_parent"/>
if (bundle == null) {
    getSupportFragmentManager().beginTransaction()
        .add(R.id.container, Fragment1.newInstance("hello world"), "f1")
        .commit();
}

Key points for dynamic transactions:

Use getSupportFragmentManager() when using the support library. add(), remove(), replace() are the basic operations; the first argument is the container ID, the second is the Fragment, the third is an optional tag.

Multiple operations can be chained in a single transaction (e.g., add().remove().replace()). commit() is asynchronous; the actual execution is posted to the main thread handler. commitNow() is the synchronous version.

Adding addToBackStack("name") puts the transaction on the back stack, allowing the user to reverse it with the back button.

Common exception

java.lang.IllegalStateException: Can not perform this action after onSaveInstanceState

occurs when committing after onSaveInstanceState(). Use commitAllowingStateLoss() only as a last resort.

Fragment Lifecycle onAttach(): Fragment is attached to its Activity.

onCreate()
onCreateView()
onActivityCreated()
onStart()
onResume()
onPause()
onStop()
onDestroyView()
onDestroy()
onDetach()

Only onCreateView() does not require a call to super; all other lifecycle methods should invoke the superclass implementation.

Fragment Transaction and Back Stack

Fragment transactions are represented by BackStackRecord, which implements FragmentTransaction, FragmentManager.BackStackEntry, and Runnable. Adding a transaction to the back stack records the entire operation chain for later reversal.

public FragmentTransaction add(int containerViewId, Fragment fragment, String tag) {
    doAddOp(containerViewId, fragment, tag, OP_ADD);
    return this;
}

The operation is stored in an internal linked list of Op objects, each describing a command (add, remove, replace, etc.) and the target Fragment.

static final class Op {
    Op next;
    Op prev;
    int cmd; // e.g., OP_ADD, OP_REMOVE
    Fragment fragment;
}

When commit() is called, the transaction is enqueued to the main‑thread handler and later executed by BackStackRecord.run(), which performs the lifecycle callbacks and view insertion.

Back‑stack operations: popBackStack(): Pops the top transaction. popBackStack(String name, int flag): Pops to a specific named entry; flag can be 0 or FragmentManager.POP_BACK_STACK_INCLUSIVE. popBackStackImmediate() executes synchronously.

Fragment Communication

Fragment → Activity

Define an interface inside the Fragment and let the Activity implement it. In onAttach() cast the context to the interface and invoke the callback when needed.

public interface OnFragmentInteractionListener {
    void onItemClick(String str);
}

FABridge (annotation‑based communication)

FABridge removes the boilerplate of defining interfaces. Add the compiler and API dependencies, annotate a method with @FCallbackId(id = FAB_ITEM_CLICK), and call it from the Fragment via Fabridge.call(mActivity, FAB_ITEM_CLICK, "data").

Activity → Fragment

Obtain the Fragment instance and call a public method, e.g., fragment.setString("hello").

Fragment ↔ Fragment

Direct communication is discouraged; use the hosting Activity as a mediator.

DialogFragment

Subclass DialogFragment and override onCreateView() to provide a custom dialog UI that survives configuration changes.

public class ProgressDialogFragment extends DialogFragment {
    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        getDialog().requestWindowFeature(Window.FEATURE_NO_TITLE);
        getDialog().getWindow().setBackgroundDrawable(new ColorDrawable(Color.TRANSPARENT));
        setCancelable(false);
        View root = inflater.inflate(R.layout.fragment_progress_dialog, container);
        return root;
    }
    public static ProgressDialogFragment newInstance() {
        return new ProgressDialogFragment();
    }
}

Show the dialog with:

ProgressDialogFragment fragment = ProgressDialogFragment.newInstance();
fragment.show(getSupportFragmentManager(), "tag");

ViewPager + Fragment

Use FragmentPagerAdapter or FragmentStatePagerAdapter as the adapter for a ViewPager. The adapter must implement getItem(int) and getCount(). The ViewPager caches adjacent pages; setOffscreenPageLimit(int) can adjust the cache size.

Lazy Loading

Lazy loading defers heavy data loading until a Fragment becomes visible. Implement it by checking getUserVisibleHint(), a prepared flag, and an initialized flag inside setUserVisibleHint() and onCreateView().

public class LazyFragment extends Fragment {
    private View mRootView;
    private boolean mIsInited;
    private boolean mIsPrepared;
    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        mRootView = inflater.inflate(R.layout.fragment_lazy, container, false);
        mIsPrepared = true;
        lazyLoad();
        return mRootView;
    }
    public void lazyLoad() {
        if (getUserVisibleHint() && mIsPrepared && !mIsInited) {
            loadData();
        }
    }
    private void loadData() {
        new Thread() {
            @Override
            public void run() {
                // load data, update UI, set mIsInited = true
            }
        }.start();
    }
    @Override
    public void setUserVisibleHint(boolean isVisibleToUser) {
        super.setUserVisibleHint(isVisibleToUser);
        if (isVisibleToUser) {
            lazyLoad();
        }
    }
    public static LazyFragment newInstance() {
        return new LazyFragment();
    }
}

Layout example for lazy loading:

<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical" android:layout_width="match_parent"
    android:layout_height="match_parent">

    <RelativeLayout
        android:id="@+id/container_empty"
        android:layout_width="match_parent"
        android:layout_height="match_parent">
        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_centerInParent="true"
            android:text="Loading..." />
    </RelativeLayout>
    <RelativeLayout
        android:id="@+id/container"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:visibility="gone">
        ...
    </RelativeLayout>
</FrameLayout>

References include Ray Wenderlich tutorial, O'Reilly presentation, Vinsol blog, Android I/O talks, StackOverflow discussions, and Android design patterns articles.

AndroidLifecycleFragmentViewPagerLazyLoadingBackStack
Tencent TDS Service
Written by

Tencent TDS Service

TDS Service offers client and web front‑end developers and operators an intelligent low‑code platform, cross‑platform development framework, universal release platform, runtime container engine, monitoring and analysis platform, and a security‑privacy compliance suite.

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.