9、Fragment入门:Fragment生命周期、静态与动态加载、Fragment与Activity通信
Fragment,说白了就是Activity里的“子页面”。
我刚开始做Android开发时,总觉得Fragment是个多余的东西。一个Activity搞定一切不好吗?直到我接手了一个平板和手机双端适配的项目……嗯,那之后我才真正理解Fragment的价值。
9.1 Fragment的生命周期
Fragment的生命周期比Activity复杂一些。它依附于Activity,所以Activity的生命周期会直接影响Fragment。
我个人习惯把Fragment生命周期分成三个阶段:
- 创建阶段:onAttach → onCreate → onCreateView → onViewCreated → onActivityCreated
- 运行阶段:onStart → onResume
- 销毁阶段:onPause → onStop → onDestroyView → onDestroy → onDetach
这里有个关键点——onCreateView和onViewCreated的区别。我在项目中遇到过不少新手把初始化逻辑写在onCreateView里,结果视图还没完全创建好就操作控件,导致空指针。正确的做法是:onCreateView只负责inflate布局,真正的视图初始化放到onViewCreated里做。
核心要点:Fragment的生命周期回调顺序是固定的,但具体执行时机受Activity状态影响。比如Activity处于后台时,Fragment的onStart不会被调用。
个人经验:我曾经在onDestroyView里忘记释放资源,导致内存泄漏。后来我养成了一个习惯——所有绑定视图的引用都在onDestroyView里置空,避免Fragment重建时持有旧视图。
9.2 静态加载Fragment
静态加载,说白了就是在布局文件里直接写<fragment>标签。这种方式最简单,适合那些不需要动态切换的场景。
举个例子:
<!-- activity_main.xml -->
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<fragment
android:id="@+id/header_fragment"
android:name="com.example.HeaderFragment"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
<fragment
android:id="@+id/content_fragment"
android:name="com.example.ContentFragment"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1" />
</LinearLayout>
静态加载的优点是简单直接,缺点也很明显——你没法在运行时替换它。所以,我一般只在固定布局(比如标题栏、底部导航)里用静态加载。
注意:静态加载的Fragment,它的生命周期是由Activity直接管理的。Activity销毁时,Fragment也会跟着销毁。如果你需要保持Fragment的状态,记得在onSaveInstanceState里保存数据。
9.3 动态加载Fragment
动态加载才是Fragment的真正威力所在。你可以在运行时添加、替换、移除Fragment,实现灵活的页面切换。
核心API就三个:
add():添加Fragment到容器replace():替换容器中的Fragmentremove():移除Fragment
操作步骤很简单:
// 获取FragmentManager
FragmentManager fragmentManager = getSupportFragmentManager();
FragmentTransaction transaction = fragmentManager.beginTransaction();
// 创建Fragment实例
MyFragment fragment = new MyFragment();
// 替换容器中的Fragment
transaction.replace(R.id.fragment_container, fragment);
transaction.addToBackStack(null); // 可选,加入返回栈
transaction.commit();
这里有个坑——commit()是异步的,它会在主线程的下一个空闲时机执行。如果你需要立即执行,可以用commitNow()。但要注意,commitNow()不能在onSaveInstanceState之后调用,否则会抛异常。
避坑指南:我曾经在Activity的onCreate里直接调用commit(),结果Fragment没有显示。后来发现是因为onCreate执行时,Activity的视图还没完全准备好。正确的做法是在onResume或onPostCreate里提交事务。
9.4 Fragment与Activity通信
Fragment和Activity之间怎么传数据?这是初学者最常问的问题。我总结了三种常用方式:
方式一:接口回调(推荐)
这是最规范的方式。Fragment定义接口,Activity实现接口。
// Fragment中定义接口
public class MyFragment extends Fragment {
public interface OnItemClickListener {
void onItemClick(String data);
}
private OnItemClickListener listener;
@Override
public void onAttach(Context context) {
super.onAttach(context);
if (context instanceof OnItemClickListener) {
listener = (OnItemClickListener) context;
}
}
// 在合适的地方调用
private void notifyClick(String data) {
if (listener != null) {
listener.onItemClick(data);
}
}
}
// Activity中实现接口
public class MainActivity extends AppCompatActivity
implements MyFragment.OnItemClickListener {
@Override
public void onItemClick(String data) {
// 处理Fragment传来的数据
}
}
方式二:ViewModel共享数据
如果你用了Jetpack,ViewModel是最优雅的方式。多个Fragment和Activity共享同一个ViewModel实例。
// 定义ViewModel
public class SharedViewModel extends ViewModel {
private MutableLiveData<String> selectedItem = new MutableLiveData<>();
public LiveData<String> getSelectedItem() {
return selectedItem;
}
public void selectItem(String item) {
selectedItem.setValue(item);
}
}
// Fragment中使用
SharedViewModel viewModel = new ViewModelProvider(requireActivity()).get(SharedViewModel.class);
viewModel.getSelectedItem().observe(getViewLifecycleOwner(), item -> {
// 更新UI
});
方式三:Bundle传参
适合简单的初始化数据传递。注意,Bundle只能传基本类型和序列化对象。
// 创建Fragment时传参
MyFragment fragment = new MyFragment();
Bundle args = new Bundle();
args.putString("key", "value");
fragment.setArguments(args);
// Fragment中接收
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
String value = getArguments().getString("key");
}
我的建议:能用ViewModel就别用接口回调。ViewModel的生命周期更清晰,而且天然支持数据持久化。接口回调适合那些需要即时响应的场景,比如按钮点击。
9.5 知识体系总览
下面这张图是我自己整理的Fragment知识结构,你可以对照着复习:
9.6 避坑总结
最后,我把自己踩过的坑整理成了一张表,你写代码时对照着看:
| 常见问题 | 原因 | 解决方案 |
|---|---|---|
| Fragment不显示 | commit()在视图未准备好时调用 | 在onResume或onPostCreate中提交 |
| 空指针异常 | 在onCreateView中操作视图 | 移到onViewCreated中操作 |
| Fragment重叠 | Activity重建时重复添加Fragment | 使用findFragmentByTag检查是否已存在 |
| 内存泄漏 | Fragment持有Activity引用未释放 | 在onDestroyView中置空视图引用 |
| 数据丢失 | Fragment重建时未保存状态 | 在onSaveInstanceState中保存数据 |
最后提醒一句:Fragment不是银弹。如果你的页面逻辑很简单,用View或自定义View可能更合适。别为了用Fragment而用Fragment,这是很多新手容易犯的毛病。
公众号:蓝海资料掘金营,微信deep3321