Android Fragment 全解析

Android Fragment 全解析

在 Android 开发中,Fragment 是构建灵活界面的核心组件 —— 它既能像 “迷你 Activity” 一样包含布局和逻辑,又能灵活地嵌入到不同 Activity 中复用。无论是平板的多面板布局,还是手机的单页切换,Fragment 都能让界面适配更高效。但 Fragment 的生命周期、与 Activity 的交互、回退栈管理等知识点也让很多开发者头疼:“为什么 Fragment 重叠了?”“getActivity () 为什么会返回 null?”“如何正确处理 Fragment 的跳转?”

本文将从 Fragment 的基础概念出发,系统讲解其生命周期、创建方式、与 Activity 的通信,再到 ViewPager2 结合 Fragment、懒加载等高级用法,最后总结常见问题及解决方案,帮你彻底掌握 Fragment 的使用精髓。

一、Fragment 核心概念:为什么需要 Fragment?

1.1 Fragment 的定义与核心价值

Fragment(碎片)是一种可以嵌入到 Activity 中的 UI 片段,它拥有自己的布局、生命周期和事件处理逻辑。其核心价值在于:

界面复用:一个 Fragment 可在多个 Activity 中使用(如 “用户信息 Fragment” 同时嵌入 “个人中心” 和 “设置” 页面);

适配不同屏幕:在平板上显示多 Fragment(如左侧列表 + 右侧详情),在手机上显示单个 Fragment(点击列表后跳转详情);

模块化开发:将复杂界面拆分为多个 Fragment,每个 Fragment 负责一部分功能(如电商详情页拆分为 “商品信息”“评价”“推荐” 三个 Fragment),便于团队协作。

形象比喻:如果把 Activity 比作 “房间”,Fragment 就是 “家具”—— 房间可以摆放不同家具(Activity 嵌入多个 Fragment),同一家具也可以放到不同房间(Fragment 复用)。

1.2 Fragment 与 Activity 的关系

Fragment 不能独立存在,必须依赖于 Activity—— 它的生命周期受 Activity 影响(如 Activity 销毁时,Fragment 也会销毁),但又有自己的独立生命周期。

所属关系:一个 Activity 可以包含多个 Fragment,一个 Fragment 只能属于一个 Activity;

通信方式:Fragment 通过接口或 ViewModel 与 Activity 通信,Activity 可直接调用 Fragment 的方法;

布局容器:Fragment 需通过 Activity 的布局容器(如 FrameLayout)显示,或通过代码动态添加。

二、Fragment 生命周期:比 Activity 更复杂的状态管理

Fragment 的生命周期比 Activity 多了几个关键方法(如与 Activity 关联、视图创建相关),理解这些方法是正确使用 Fragment 的基础。

2.1 完整生命周期方法及触发时机

生命周期方法

触发时机

核心职责

对应 Activity 方法

onAttach()

Fragment 与 Activity 关联时调用

获取 Activity 引用,初始化与 Activity 的通信

-

onCreate()

Fragment 创建时调用(早于视图创建)

初始化非视图数据(如从 Arguments 取参数)

onCreate()

onCreateView()

Fragment 创建视图时调用

加载布局(inflate 布局文件),初始化控件

onCreateView()

onViewCreated()

视图创建完成后调用

初始化视图逻辑(如设置点击事件、加载数据)

-

onActivityCreated()

宿主 Activity 的 onCreate () 完成后调用

可安全使用 Activity 的资源(如 ActionBar)

-

onStart()

Fragment 可见时调用

启动动画、注册广播等

onStart()

onResume()

Fragment 可交互时调用

开始监听用户输入、启动传感器等

onResume()

onPause()

Fragment 失去焦点时调用

暂停动画、保存临时数据

onPause()

onStop()

Fragment 不可见时调用

停止耗时操作、取消广播注册

onStop()

onDestroyView()

Fragment 视图销毁时调用

清理视图资源(如解绑 View 监听、回收 Bitmap)

-

onDestroy()

Fragment 销毁时调用

释放非视图资源(如取消网络请求)

onDestroy()

onDetach()

Fragment 与 Activity 解除关联时调用

移除与 Activity 的引用(避免内存泄漏)

-

2.2 生命周期执行流程(首次加载)

当 Fragment 被添加到 Activity 时,完整流程为:

onAttach() → onCreate() → onCreateView() → onViewCreated() → onActivityCreated() → onStart() → onResume()

关键节点:

onCreateView()是视图创建的核心(必须返回布局 View);

onViewCreated()中才能安全操作 View(此时 View 已创建);

onActivityCreated()确保 Activity 已初始化完成(可安全调用getActivity())。

2.3 生命周期与 Activity 的联动

Fragment 的生命周期受 Activity 影响,例如:

Activity 执行onPause() → 所有 Fragment 执行onPause();

Activity 旋转销毁(onDestroy()) → Fragment 执行onDestroyView()→onDestroy()→onDetach(),重建时重新执行完整生命周期;

若 Fragment 被移除(remove()) → 执行onDestroyView()→onDestroy()→onDetach()。

三、Fragment 基础使用:创建、添加与移除

使用 Fragment 的核心步骤是 “创建 Fragment→添加到 Activity→管理其生命周期”,根据添加方式可分为 “静态添加” 和 “动态添加”。

3.1 静态添加:布局中直接声明(简单场景)

静态添加是在 Activity 的布局文件中直接声明 Fragment,适合固定不变的界面(如始终显示的标题栏 Fragment)。

步骤 1:创建 Fragment 子类

public class StaticFragment extends Fragment {

private static final String TAG = "StaticFragment";

// 必须有默认构造方法(避免重建时崩溃)

public StaticFragment() {}

@Nullable

@Override

public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {

// 加载布局(第三个参数必须为false,避免重复添加到container)

return inflater.inflate(R.layout.fragment_static, container, false);

}

@Override

public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {

super.onViewCreated(view, savedInstanceState);

// 初始化控件(如设置文本)

view.findViewById(R.id.btn_static).setOnClickListener(v ->

Toast.makeText(getContext(), "静态Fragment点击", Toast.LENGTH_SHORT).show()

);

}

}

对应的布局fragment_static.xml:

android:layout_width="match_parent"

android:layout_height="wrap_content"

android:background="#f0f0f0"

android:padding="16dp">

android:id="@+id/btn_static"

android:layout_width="wrap_content"

android:layout_height="wrap_content"

android:text="静态Fragment按钮"/>

步骤 2:在 Activity 布局中声明 Fragment

在 Activity 的布局文件(如activity_main.xml)中添加标签:

android:layout_width="match_parent"

android:layout_height="match_parent"

android:orientation="vertical">

android:id="@+id/fragment_static"

android:name="com.example.fragmentdemo.StaticFragment"

android:layout_width="match_parent"

android:layout_height="wrap_content"/>

android:layout_width="match_parent"

android:layout_height="wrap_content"

android:text="Activity中的内容"/>

注意:

android:name必须指定 Fragment 的完整类名;

必须设置android:id(否则 Fragment 无法被找到);

静态添加的 Fragment 无法在运行时移除或替换(灵活性低)。

3.2 动态添加:代码中控制(推荐方式)

动态添加是通过代码将 Fragment 添加到 Activity 的容器中,支持运行时添加、移除、替换,是开发中最常用的方式。

步骤 1:创建可复用的 Fragment

public class DynamicFragment extends Fragment {

private static final String ARG_TITLE = "title"; // 传递参数的key

private String mTitle; // 接收的参数

// 提供工厂方法创建Fragment(推荐,避免直接new)

public static DynamicFragment newInstance(String title) {

DynamicFragment fragment = new DynamicFragment();

// 通过Bundle传递参数

Bundle args = new Bundle();

args.putString(ARG_TITLE, title);

fragment.setArguments(args);

return fragment;

}

@Override

public void onCreate(@Nullable Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

// 从Arguments获取参数(避免在构造方法中传参,防止重建丢失)

if (getArguments() != null) {

mTitle = getArguments().getString(ARG_TITLE);

}

}

@Nullable

@Override

public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {

return inflater.inflate(R.layout.fragment_dynamic, container, false);

}

@Override

public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {

super.onViewCreated(view, savedInstanceState);

// 设置标题

TextView tvTitle = view.findViewById(R.id.tv_title);

tvTitle.setText(mTitle);

// 添加点击事件(示例:点击替换为其他Fragment)

view.findViewById(R.id.btn_replace).setOnClickListener(v -> {

if (getActivity() instanceof MainActivity) {

((MainActivity) getActivity()).replaceFragment(DynamicFragment.newInstance("新的Fragment"));

}

});

}

}

布局fragment_dynamic.xml:

android:layout_width="match_parent"

android:layout_height="match_parent"

android:gravity="center"

android:orientation="vertical">

android:id="@+id/tv_title"

android:layout_width="wrap_content"

android:layout_height="wrap_content"

android:textSize="18sp"/>

android:id="@+id/btn_replace"

android:layout_width="wrap_content"

android:layout_height="wrap_content"

android:text="替换Fragment"/>

步骤 2:在 Activity 中添加容器与管理代码

Activity 布局(含 Fragment 容器):

android:layout_width="match_parent"

android:layout_height="match_parent"

android:orientation="vertical">

android:id="@+id/container"

android:layout_width="match_parent"

android:layout_height="0dp"

android:layout_weight="1"/>

android:id="@+id/btn_add"

android:layout_width="wrap_content"

android:layout_height="wrap_content"

android:text="添加Fragment"/>

Activity 代码(管理 Fragment):

public class MainActivity extends AppCompatActivity {

private FragmentManager mFragmentManager;

@Override

protected void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

setContentView(R.layout.activity_main);

// 获取Fragment管理器(AndroidX使用getSupportFragmentManager())

mFragmentManager = getSupportFragmentManager();

// 初始添加Fragment(避免旋转屏幕重复添加)

if (savedInstanceState == null) {

addFragment(DynamicFragment.newInstance("初始Fragment"));

}

// 添加按钮点击事件

findViewById(R.id.btn_add).setOnClickListener(v ->

addFragment(DynamicFragment.newInstance("新添加的Fragment"))

);

}

// 添加Fragment

public void addFragment(Fragment fragment) {

// 开启事务

FragmentTransaction transaction = mFragmentManager.beginTransaction();

// 添加到容器(container是FrameLayout的id)

transaction.add(R.id.container, fragment);

// 添加到回退栈(按返回键可返回上一个Fragment)

transaction.addToBackStack(null);

// 提交事务

transaction.commit();

}

// 替换Fragment(移除现有Fragment,添加新的)

public void replaceFragment(Fragment fragment) {

FragmentTransaction transaction = mFragmentManager.beginTransaction();

// 替换容器中的Fragment(会移除现有所有Fragment)

transaction.replace(R.id.container, fragment);

transaction.addToBackStack(null);

transaction.commit();

}

// 移除Fragment

public void removeFragment(Fragment fragment) {

FragmentTransaction transaction = mFragmentManager.beginTransaction();

transaction.remove(fragment);

transaction.commit();

}

}

核心 API 解析

FragmentManager:Fragment 的管理器,负责添加、移除、查找 Fragment;

FragmentTransaction:事务,用于执行 Fragment 的一系列操作(添加、替换等),需调用commit()生效;

addToBackStack():将事务添加到回退栈,按返回键时会恢复到上一个状态(如添加 A→添加 B,按返回键回到 A);

newInstance():推荐的 Fragment 创建方式,通过 Bundle 传递参数(避免直接 new,防止重建时参数丢失)。

四、Fragment 与 Activity 的交互:数据传递与通信

Fragment 与 Activity 的交互是开发中的高频需求(如 Fragment 点击按钮通知 Activity 更新标题,Activity 传递数据给 Fragment 显示)。常用的通信方式有三种:接口回调、ViewModel 共享数据、EventBus 事件总线。

4.1 接口回调:最经典的通信方式

通过定义接口,让 Activity 实现接口,Fragment 调用接口方法传递数据,适合简单场景。

步骤 1:在 Fragment 中定义接口

public class CallbackFragment extends Fragment {

// 定义回调接口

public interface OnDataListener {

void onDataChanged(String data); // Fragment传递数据给Activity

}

// 持有接口引用

private OnDataListener mListener;

// 绑定Activity时检查是否实现接口

@Override

public void onAttach(@NonNull Context context) {

super.onAttach(context);

// 确保Activity实现了接口

if (context instanceof OnDataListener) {

mListener = (OnDataListener) context;

} else {

throw new RuntimeException(context + "必须实现OnDataListener");

}

}

@Override

public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {

super.onViewCreated(view, savedInstanceState);

// 点击按钮时调用接口传递数据

view.findViewById(R.id.btn_send).setOnClickListener(v -> {

if (mListener != null) {

mListener.onDataChanged("来自Fragment的数据");

}

});

}

// 解除绑定时置空接口(避免内存泄漏)

@Override

public void onDetach() {

super.onDetach();

mListener = null;

}

}

步骤 2:Activity 实现接口接收数据

public class MainActivity extends AppCompatActivity implements CallbackFragment.OnDataListener {

private TextView mTvResult;

@Override

protected void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

setContentView(R.layout.activity_main);

mTvResult = findViewById(R.id.tv_result);

// 添加Fragment

if (savedInstanceState == null) {

getSupportFragmentManager().beginTransaction()

.add(R.id.container, new CallbackFragment())

.commit();

}

}

// 实现接口方法,接收Fragment的数据

@Override

public void onDataChanged(String data) {

mTvResult.setText("收到数据:" + data);

}

}

4.2 ViewModel:共享数据(AndroidX 推荐)

ViewModel 是 Jetpack 组件,可在 Activity 和 Fragment 之间共享数据,且不受生命周期影响(如屏幕旋转数据不丢失),适合复杂场景。

步骤 1:添加依赖(AndroidX)

implementation 'androidx.lifecycle:lifecycle-viewmodel:2.6.2'

implementation 'androidx.lifecycle:lifecycle-livedata:2.6.2'

步骤 2:创建共享的 ViewModel

// 共享的ViewModel,存储需要传递的数据

public class SharedViewModel extends ViewModel {

// LiveData:数据变化时通知观察者

private MutableLiveData mSharedData = new MutableLiveData<>();

// 设置数据

public void setData(String data) {

mSharedData.setValue(data);

}

// 获取LiveData(对外暴露不可变的LiveData)

public LiveData getData() {

return mSharedData;

}

}

步骤 3:Fragment 发送数据

public class SenderFragment extends Fragment {

private SharedViewModel mViewModel;

@Override

public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {

super.onViewCreated(view, savedInstanceState);

// 获取与Activity共享的ViewModel(使用Activity的ViewModelStore)

mViewModel = new ViewModelProvider(requireActivity()).get(SharedViewModel.class);

// 点击按钮发送数据

view.findViewById(R.id.btn_send).setOnClickListener(v -> {

mViewModel.setData("来自SenderFragment的数据");

});

}

}

步骤 4:Activity 接收数据

public class MainActivity extends AppCompatActivity {

private SharedViewModel mViewModel;

@Override

protected void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

setContentView(R.layout.activity_main);

// 获取ViewModel

mViewModel = new ViewModelProvider(this).get(SharedViewModel.class);

// 观察数据变化

mViewModel.getData().observe(this, data -> {

// 更新UI

((TextView) findViewById(R.id.tv_result)).setText(data);

});

// 添加发送数据的Fragment

getSupportFragmentManager().beginTransaction()

.add(R.id.container, new SenderFragment())

.commit();

}

}

优势:

无需接口定义,代码更简洁;

数据在 Activity 旋转时不会丢失;

支持多个 Fragment 与 Activity 共享数据。

4.3 EventBus:解耦的事件传递(复杂场景)

EventBus 是第三方库,通过发布 - 订阅模式实现组件间通信,适合多个 Fragment、Activity 之间的跨组件通信。

步骤 1:添加依赖

implementation 'org.greenrobot:eventbus:3.3.1'

步骤 2:定义事件类

// 事件类(存储需要传递的数据)

public class MessageEvent {

private String message;

public MessageEvent(String message) {

this.message = message;

}

public String getMessage() {

return message;

}

}

步骤 3:Fragment 发布事件

public class EventFragment extends Fragment {

@Override

public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {

super.onViewCreated(view, savedInstanceState);

// 点击按钮发布事件

view.findViewById(R.id.btn_send).setOnClickListener(v -> {

// 发布事件

EventBus.getDefault().post(new MessageEvent("来自EventFragment的消息"));

});

}

}

步骤 4:Activity 订阅事件

public class MainActivity extends AppCompatActivity {

@Override

protected void onStart() {

super.onStart();

// 注册EventBus

EventBus.getDefault().register(this);

}

@Override

protected void onStop() {

super.onStop();

// 解注册EventBus(必须调用,避免内存泄漏)

EventBus.getDefault().unregister(this);

}

// 订阅事件(方法名任意,需加@Subscribe注解)

@Subscribe(threadMode = ThreadMode.MAIN) // 在主线程处理

public void onMessageEvent(MessageEvent event) {

// 更新UI

((TextView) findViewById(R.id.tv_result)).setText(event.getMessage());

}

}

适用场景:跨多个 Fragment、Activity 的通信(如首页 Fragment 通知个人中心 Fragment 更新数据)。

五、Fragment 高级用法:ViewPager2 结合与懒加载

在实际开发中,Fragment 常与 ViewPager2 结合实现滑动切换(如首页的 “推荐”“热点”“关注” 标签页),并需要懒加载优化(仅在 Fragment 可见时加载数据)。

5.1 ViewPager2 + Fragment:滑动切换的标签页

ViewPager2 是 ViewPager 的升级版,支持垂直滑动、RTL 布局,且与 RecyclerView 共享适配器,更适合与 Fragment 结合。

步骤 1:添加 ViewPager2 依赖

implementation 'androidx.viewpager2:viewpager2:1.0.0'

implementation 'androidx.fragment:fragment-ktx:1.5.5' // Fragment相关依赖

步骤 2:创建 ViewPager2 的适配器

public class ViewPager2Adapter extends FragmentStateAdapter {

private List mTitles; // 每个Fragment的标题

public ViewPager2Adapter(@NonNull FragmentActivity fragmentActivity, List titles) {

super(fragmentActivity);

mTitles = titles;

}

// 创建对应位置的Fragment

@NonNull

@Override

public Fragment createFragment(int position) {

// 返回对应位置的Fragment(传递标题作为参数)

return PagerFragment.newInstance(mTitles.get(position));

}

// 返回Fragment数量

@Override

public int getItemCount() {

return mTitles.size();

}

}

步骤 3:创建 ViewPager2 的每个页面 Fragment

public class PagerFragment extends Fragment {

private static final String ARG_TITLE = "title";

private String mTitle;

public static PagerFragment newInstance(String title) {

PagerFragment fragment = new PagerFragment();

Bundle args = new Bundle();

args.putString(ARG_TITLE, title);

fragment.setArguments(args);

return fragment;

}

@Override

public void onCreate(@Nullable Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

if (getArguments() != null) {

mTitle = getArguments().getString(ARG_TITLE);

}

}

@Nullable

@Override

public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {

return inflater.inflate(R.layout.fragment_pager, container, false);

}

@Override

public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {

super.onViewCreated(view, savedInstanceState);

((TextView) view.findViewById(R.id.tv_title)).setText(mTitle);

}

}

步骤 4:在 Activity 中配置 ViewPager2

public class ViewPager2Activity extends AppCompatActivity {

private ViewPager2 mViewPager2;

private TabLayout mTabLayout; // 用于显示标签

@Override

protected void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

setContentView(R.layout.activity_view_pager2);

// 初始化数据(标签标题)

List titles = Arrays.asList("推荐", "热点", "关注", "视频");

// 配置ViewPager2

mViewPager2 = findViewById(R.id.view_pager2);

mViewPager2.setAdapter(new ViewPager2Adapter(this, titles));

// 禁止滑动(可选)

// mViewPager2.setUserInputEnabled(false);

// 关联TabLayout和ViewPager2(需添加TabLayoutMediator)

mTabLayout = findViewById(R.id.tab_layout);

new TabLayoutMediator(mTabLayout, mViewPager2, (tab, position) -> {

tab.setText(titles.get(position)); // 设置标签文本

}).attach(); // 必须调用attach()

}

}

布局文件

activity_view_pager2.xml:

android:layout_width="match_parent"

android:layout_height="match_parent"

android:orientation="vertical">

android:id="@+id/tab_layout"

android:layout_width="match_parent"

android:layout_height="wrap_content"/>

android:id="@+id/view_pager2"

android:layout_width="match_parent"

android:layout_height="0dp"

android:layout_weight="1"/>

fragment_pager.xml:

android:layout_width="match_parent"

android:layout_height="match_parent"

android:gravity="center">

android:id="@+id/tv_title"

android:layout_width="wrap_content"

android:layout_height="wrap_content"

android:textSize="20sp"/>

5.2 Fragment 懒加载:提升性能的关键

ViewPager2 默认会预加载相邻的 Fragment(如显示第 1 个,预加载第 2 个),若 Fragment 需要加载网络数据,会导致浪费流量和性能。懒加载指 “仅当 Fragment 可见时才加载数据”。

实现方式(基于 ViewPager2)

public class LazyLoadFragment extends Fragment {

private static final String ARG_TITLE = "title";

private String mTitle;

private boolean isLoaded = false; // 标记是否已加载数据

private boolean isVisible = false; // 标记是否可见

public static LazyLoadFragment newInstance(String title) {

LazyLoadFragment fragment = new LazyLoadFragment();

Bundle args = new Bundle();

args.putString(ARG_TITLE, title);

fragment.setArguments(args);

return fragment;

}

@Override

public void onCreate(@Nullable Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

if (getArguments() != null) {

mTitle = getArguments().getString(ARG_TITLE);

}

}

@Nullable

@Override

public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {

return inflater.inflate(R.layout.fragment_lazy_load, container, false);

}

@Override

public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {

super.onViewCreated(view, savedInstanceState);

((TextView) view.findViewById(R.id.tv_title)).setText(mTitle);

// 视图创建完成后,若已可见则加载数据

if (isVisible && !isLoaded) {

loadData();

}

}

// ViewPager2中Fragment可见性变化时调用

@Override

public void setUserVisibleHint(boolean isVisibleToUser) {

super.setUserVisibleHint(isVisibleToUser);

isVisible = isVisibleToUser;

// 可见且视图已创建且未加载数据,则加载

if (isVisibleToUser && getView() != null && !isLoaded) {

loadData();

}

}

// 加载数据(模拟网络请求)

private void loadData() {

Log.d("LazyLoad", mTitle + "开始加载数据");

new Thread(() -> {

try {

Thread.sleep(1000); // 模拟耗时

// 更新UI

requireActivity().runOnUiThread(() -> {

((TextView) getView().findViewById(R.id.tv_content))

.setText(mTitle + "数据加载完成");

isLoaded = true; // 标记为已加载

});

} catch (InterruptedException e) {

e.printStackTrace();

}

}).start();

}

}

核心逻辑:

setUserVisibleHint():ViewPager2 中 Fragment 可见性变化时调用(isVisibleToUser为 true 表示可见);

isLoaded标记:避免重复加载数据;

仅当 “可见 + 视图已创建 + 未加载” 时才调用loadData()。

六、Fragment 常见问题及解决方案

Fragment 的使用中存在很多 “坑”,稍不注意就会导致崩溃或异常行为,以下是高频问题及解决办法。

6.1 Fragment 重叠问题(旋转屏幕或配置变化)

现象:旋转屏幕后,Fragment 重复显示(多个相同 Fragment 叠加)。

原因:Activity 重建时,FragmentManager 会自动恢复 Fragment,若在onCreate()中再次add(),就会导致重复添加。

解决方案:在onCreate()中添加 Fragment 前判断savedInstanceState == null:

@Override

protected void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

// 仅在首次创建时添加Fragment,重建时不添加

if (savedInstanceState == null) {

getSupportFragmentManager().beginTransaction()

.add(R.id.container, new MyFragment())

.commit();

}

}

6.2 getActivity () 返回 null 导致崩溃

现象:调用getActivity()时返回 null,触发空指针异常。

原因:Fragment 已与 Activity 解除关联(如onDetach()后),仍调用getActivity()。

解决方案:

避免在异步回调中使用getActivity()(如网络请求回调,此时 Fragment 可能已销毁);

使用requireActivity()替代getActivity()(内部会检查,为 null 时抛出明确异常);

在onAttach()中保存 Activity 引用,onDetach()中置空:

private Activity mActivity;

@Override

public void onAttach(@NonNull Context context) {

super.onAttach(context);

mActivity = (Activity) context;

}

@Override

public void onDetach() {

super.onDetach();

mActivity = null; // 解除引用

}

// 使用时检查非空

if (mActivity != null) {

// 操作mActivity

}

6.3 Fragment 事务提交异常(Can not perform this action after onSaveInstanceState)

现象:在onSaveInstanceState()后调用commit()提交事务,抛出异常。

原因:onSaveInstanceState()后,Activity 状态已保存,此时提交事务可能导致状态不一致。

解决方案:

使用commitAllowingStateLoss()替代commit()(允许状态丢失,适合非关键事务);

避免在onPause() onStop()等生命周期后期提交事务;

若必须提交,确保在onResumeFragments()中执行。

6.4 回退栈问题(按返回键直接退出 Activity)

现象:添加 Fragment 到回退栈后,按返回键未显示上一个 Fragment,而是直接退出 Activity。

原因:

未调用addToBackStack()(事务未加入回退栈);

回退栈为空(没有可恢复的事务)。

解决方案:

添加事务时必须调用addToBackStack(null);

在 Activity 中重写onBackPressed(),判断回退栈是否为空:

@Override

public void onBackPressed() {

FragmentManager fm = getSupportFragmentManager();

if (fm.getBackStackEntryCount() > 0) {

// 回退栈有内容,弹出栈顶事务

fm.popBackStack();

} else {

// 回退栈为空,退出Activity

super.onBackPressed();

}

}

6.5 Fragment 内存泄漏

现象:Fragment 销毁后仍被引用,导致无法回收,内存泄漏。

常见原因:

Fragment 持有 Activity 的强引用(如匿名内部类new Thread()引用 Fragment,Fragment 引用 Activity);

异步任务(如网络请求)未取消,持有 Fragment 引用;

onDetach()后仍使用getActivity()。

解决方案:

异步任务在onDestroy()中取消(如cancel());

使用弱引用持有 Activity 或 Fragment:

// 弱引用持有Activity

private WeakReference mActivityRef;

@Override

public void onAttach(@NonNull Context context) {

super.onAttach(context);

mActivityRef = new WeakReference<>((Activity) context);

}

// 使用时获取

Activity activity = mActivityRef.get();

if (activity != null && !activity.isFinishing()) {

// 安全操作

}

避免在 Fragment 中使用静态变量持有视图或数据。

七、Fragment 最佳实践总结

使用 Fragment 的核心原则是 “模块化、低耦合、生命周期安全”,结合前文内容,最佳实践可总结为:

1.创建与实例化:

始终使用newInstance()工厂方法创建 Fragment,通过Bundle传递参数;禁止在构造方法中传递参数(Activity 重建时会丢失)。

2.生命周期管理:

在onViewCreated()中初始化视图操作(避免在onCreateView()中操作 View);onDestroyView()中清理视图资源(如ImageView.setImageBitmap(null));onDetach()中移除所有与 Activity 的引用。

3.与 Activity 通信:

简单通信用接口回调,复杂通信用 ViewModel;跨组件通信考虑 EventBus,但需注意注册与解注册。

4.性能优化:

结合 ViewPager2 时使用懒加载,避免预加载数据;避免在 Fragment 中做耗时操作(移到子线程);复用 Fragment 实例(如 ViewPager2 的适配器缓存 Fragment)。

5.异常处理:

调用getActivity()前先检查是否为 null(或用requireActivity());提交事务时注意时机,避免在onSaveInstanceState()后提交。

八、总结:Fragment 的核心价值与未来

Fragment 作为 Android 界面构建的核心组件,其 “模块化” 和 “复用性” 的设计理念贯穿了 Android 开发的始终。从早期的Fragment到现在与ViewPager2、ViewModel的结合,Fragment 始终是构建灵活界面的最佳选择。

掌握 Fragment 的关键在于理解其生命周期与 Activity 的联动关系,以及如何通过合理的通信方式降低耦合。无论是简单的页面拆分,还是复杂的多面板适配,Fragment 都能提供优雅的解决方案。

未来,随着 Jetpack 组件的发展(如Navigation组件对 Fragment 的管理),Fragment 的使用会更加简化,但核心原理和最佳实践始终不变 —— 理解这些本质,才能在各种场景中灵活运用 Fragment,构建出高效、稳定、可维护的 Android 应用。