Kotlin中lateinit修饰的action属性未初始化及Firebase检测的赋值报错修复
报错根因
- 「lateinit property action has not been initialized」崩溃:DialogFragment会在系统配置变更(屏幕旋转、后台进程被回收重建)时自动生成新实例,你通过
setListener手动赋值的lateinit修饰的action属于实例临时状态,重建后会被清空。当用户完成时间选择触发onTimeSet时,未完成初始化的action就会直接抛出运行时异常。 - Firebase检测标记
setListener位置错误:直接给Fragment实例赋值外部回调的写法,会让Fragment隐式持有宿主(Activity/父Fragment)的强引用,当Fragment被系统回收时,外部引用无法被正常释放,会引发内存泄漏,属于Android官方明确禁止的Fragment通信写法,因此会被性能检测规则捕获。
标准修复方案
采用AndroidX官方推荐的FragmentResult跨组件通信机制替代自定义setter回调,彻底解决崩溃和内存泄漏问题:
- 调整
TimePickerFragment内部逻辑,移除原有action属性和setListener方法,用户选中时间后通过系统提供的结果通道分发数据 - 在弹出选择器的宿主页面(Activity/父Fragment)注册结果监听,接收选中的时间值,不需要在弹窗前给Fragment传递任何回调
修改后的TimePickerFragment完整代码:
import android.app.Dialog import android.app.TimePickerDialog import android.os.Bundle import android.text.format.DateFormat import android.widget.TimePicker import androidx.fragment.app.DialogFragment import java.util.* class TimePickerFragment : DialogFragment(), TimePickerDialog.OnTimeSetListener { companion object { // 定义唯一的请求Key,用于匹配回调监听 const val REQUEST_KEY = "time_picker_request" // 定义结果存储的Key,用于读取选中的时间 const val RESULT_TIME_KEY = "selected_time" } override fun onCreateDialog(savedInstanceState: Bundle?): Dialog { val c = Calendar.getInstance() val hour = c.get(Calendar.HOUR_OF_DAY) val minute = c.get(Calendar.MINUTE) return TimePickerDialog(activity, this, hour, minute, DateFormat.is24HourFormat(activity)) } override fun onTimeSet(view: TimePicker, hourOfDay: Int, minute: Int) { val min = if(minute < 10) "0$minute" else minute.toString() // 将选中的时间封装为结果,通过系统通道分发 val result = Bundle().apply { putString(RESULT_TIME_KEY, "$hourOfDay:$min") } parentFragmentManager.setFragmentResult(REQUEST_KEY, result) dismiss() } }
宿主页面(以Activity为例)的适配代码:
// 在页面初始化阶段(比如onCreate方法中)注册结果监听 private fun registerTimePickerCallback() { supportFragmentManager.setFragmentResultListener( TimePickerFragment.REQUEST_KEY, this ) { _, bundle -> val selectedTime = bundle.getString(TimePickerFragment.RESULT_TIME_KEY) // 拿到选中的时间字符串后,执行后续业务逻辑 // 比如更新输入框、存储选中值等 } } // 弹出时间选择器时,直接实例化调用show即可,不需要额外设置回调 fun openTimePicker() { val picker = TimePickerFragment() picker.show(supportFragmentManager, "time_picker") }
临时应急方案(不推荐长期使用)
如果暂时无法改动整体通信逻辑,可以通过两个最小调整避免崩溃:
- 将
lateinit var action : (String) -> Unit修改为可空属性:var action: ((String) -> Unit)? = null- 在
onTimeSet中调用回调时增加判空:action?.invoke("$hourOfDay:$min")
该方案仅能规避空指针崩溃,依然存在内存泄漏风险,也无法解决配置变更时回调丢失的问题。
内容的提问来源于stack exchange,提问作者Rajiv Mochammad Iqbal Alhadad
相关产品推荐
相关产品推荐

