Fragment的构造函数

本文详细介绍了Fragment在Android应用中的构造函数使用规范及实例化过程,强调了空构造函数的重要性,解释了如何通过setArguments和getArguments来传递参数,并讨论了Fragment与Activity之间的交互点。

   

    默认的构造函数,每个Fragment必须有一个空的构造函数,这可以让它在Activity中恢复时实例化。强烈建议Fragment的子类不要拥有其他有参数的构造函数,因为这些构造器不会在Fragment重新实例化时被调用;取而代之, 调用者能以setArguments(Bundle)提供arguments,然后被Fragment以getArguments()取回。

    应用程序一般不要实现构造器。一个可以使用的Fragment的开始代码是onAttach(Activity),事实上这是Fragment和Activity交接的地方。有些应用程序也会去实现onInflate(Activity, AttributeSet, Bundle)方法从layout资源中获得属性,但是在这里应该特别小心,因为这发生在Fragment附加上它的Activity时候。

 

来自官方API:

Default constructor. Every fragment must have an empty constructor, so it can be instantiated when restoring its activity's state. 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; instead, arguments can be supplied by the caller with setArguments(Bundle) and later retrieved by the Fragment with getArguments().

Applications should generally not implement a constructor. The first place application code an run where the fragment is ready to be used is inonAttach(Activity), the point where the fragment is actually associated with its activity. Some applications may also want to implementonInflate(Activity, AttributeSet, Bundle) to retrieve attributes from a layout resource, though should take care here because this happens for the fragment is attached to its activity. 

   

这是一个非常典型的 **Fragment 内存泄漏或状态恢复问题**,当 `selectedCallback` 被写在 Fragment构造函数中并持有外部类(如 Activity 或 ViewModel)的引用,**在 Fragment 被重建时(例如横竖屏切换、语言切换等),如果引用的对象已被销毁或未重建,就可能导致崩溃**。 --- ### 🔍 问题分析 #### 示例代码(可能的构造函数写法): ```kotlin class MyFragment(val selectedCallback: ICommonItemGenericSelected<MyItem>) : Fragment() { // ... } ``` 当这个 Fragment 被系统重建时(如配置变更),它会尝试通过 `SavedInstanceState` 恢复实例,但由于构造函数参数不是由系统管理的,会导致: - 构造函数参数丢失,导致崩溃; - 或者使用 `@JvmOverloads` 添加默认构造函数,但 `selectedCallback` 为 null,导致空指针异常; - 如果 `selectedCallback` 持有外部 Activity/Context 的引用,还可能导致内存泄漏。 --- ### ✅ 解决方案 #### ✅ 方案一:使用 `Fragment.setArguments()` 替代构造函数传参(推荐) Fragment 应该使用 `arguments` 来传递可序列化或 `Parcelable` 的数据,而不是构造函数传参。 ##### ✅ 修改方式: 1. **使用工厂方法创建 Fragment:** ```kotlin class MyFragment : Fragment() { private var selectedCallback: ICommonItemGenericSelected<MyItem>? = null companion object { fun newInstance(callback: ICommonItemGenericSelected<MyItem>): MyFragment { val fragment = MyFragment() fragment.selectedCallback = callback return fragment } } } ``` > ⚠️ 注意:虽然这里还是赋值了接口,但我们可以进一步优化。 --- #### ✅ 方案二:使用接口回调 + Activity/ViewModel 通信(推荐用于正式项目) Fragment 不应该持有回调接口的强引用,而是应该通过: - `requireActivity()` 获取接口实现; - 或者使用 `ViewModel` + LiveData 共享数据; - 或者使用 `by activityViewModels()` 共享 ViewModel。 ##### 示例(使用接口回调): ```kotlin interface ItemSelectedCallback { fun onSelected(item: MyItem, position: Int) fun onPreChecked(item: MyItem): Boolean fun onSelectedAll(items: List<MyItem>) } class MyFragment : Fragment() { private lateinit var callback: ItemSelectedCallback override fun onAttach(context: Context) { super.onAttach(context) callback = context as? ItemSelectedCallback ?: throw IllegalStateException("Activity must implement ItemSelectedCallback") } // 在点击事件中调用 private fun onItemClick(item: MyItem) { callback.onSelected(item, 0) } } ``` 这样 Fragment 不再持有回调引用,而是从 `onAttach` 中获取,避免了构造函数传参的问题。 --- #### ✅ 方案三:使用 ViewModel + LiveData(更现代的写法) Fragment 与宿主 Activity 或父 Fragment 共享一个 `ViewModel`,通过 `LiveData` 或 `SharedFlow` 通信。 ```kotlin class SharedViewModel : ViewModel() { private val _itemSelected = MutableLiveData<MyItem>() val itemSelected: LiveData<MyItem> = _itemSelected fun selectItem(item: MyItem) { _itemSelected.value = item } } ``` Fragment 中观察 LiveData: ```kotlin val viewModel: SharedViewModel by activityViewModels() override fun onViewCreated(...) { viewModel.itemSelected.observe(viewLifecycleOwner) { item -> // 处理选中逻辑 } } ``` --- ### 📌 总结:推荐做法 | 方式 | 优点 | 缺点 | |------|------|------| | 构造函数传接口 | 简单直接 | 易导致崩溃、内存泄漏 | | 使用 arguments + 工厂方法 | 安全 | 不能直接传接口 | | 使用 `onAttach` 获取接口 | 符合 Fragment 生命周期 | 需要宿主实现接口 | | 使用 ViewModel + LiveData | 安全、解耦、适合 MVVM 架构 | 需要熟悉架构组件 | --- ### ✅ 最佳实践建议: - **永远不要在 Fragment 构造函数中传非基本类型或接口**; - 使用 `ViewModel` 或 `onAttach` 获取回调; - 使用 `arguments` 传参数; - 避免持有外部类的强引用,防止内存泄漏。 ---
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值