单Activity架构下Fragment调用Floating Action Button空指针异常如何解决?
报错根因
横竖屏切换触发Activity重建时,Fragment的onViewCreated执行时机早于Activity视图加载完成流程,此时调用requireActivity().findViewById还找不到已经被销毁重建的FAB实例,因此抛出空指针异常。
解决方案
方案1:调整生命周期+BaseFragment封装(适配现有逻辑快速修复)
- 初始化时机选择:将FAB的初始化逻辑从
onViewCreated移到onStart方法中,onStart执行时Activity已完成重建、视图加载完毕,不会出现空指针。 - 补充回收逻辑:在
onStop中清空FAB的点击事件,避免Activity持有的FAB引用Fragment实例导致内存泄漏。 - BaseFragment统一封装示例:
abstract class BaseFabFragment : Fragment() { // 子类实现返回当前页面对应的FAB图标 abstract fun getFabIconRes(): Int // 子类实现FAB点击的业务逻辑 abstract fun onFabClick() override fun onStart() { super.onStart() val fab = requireActivity().findViewById<FloatingActionButton>(R.id.fab_btn_add) fab.setImageResource(getFabIconRes()) fab.setOnClickListener { onFabClick() } } override fun onStop() { super.onStop() requireActivity().findViewById<FloatingActionButton>(R.id.fab_btn_add)?.setOnClickListener(null) } }
所有需要用到FAB的Fragment只要继承BaseFabFragment,实现两个抽象方法即可,无需重复编写FAB逻辑。
方案2:共享ViewModel解耦(推荐)
从根源避免Fragment持有Activity的View实例,完全规避生命周期同步导致的空指针问题,耦合度更低:
- 定义Activity级别的共享ViewModel:
class FabSharedViewModel : ViewModel() { // 存储FAB图标配置 val fabIconRes = MutableLiveData<Int>() // FAB点击事件流 private val _fabClickEvent = MutableSharedFlow<Unit>() val fabClickEvent = _fabClickEvent.asSharedFlow() fun sendFabClick() { viewModelScope.launch { _fabClickEvent.emit(Unit) } } }
- Activity中统一处理FAB的全局逻辑:
class MainActivity : AppCompatActivity() { private lateinit var binding: ActivityMainBinding private val fabViewModel by viewModels<FabSharedViewModel>() override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) binding = ActivityMainBinding.inflate(layoutInflater) setContentView(binding.root) // 观察ViewModel配置修改FAB图标 fabViewModel.fabIconRes.observe(this) { iconRes -> binding.fabBtnAdd.setImageResource(iconRes) } // FAB点击时发送事件到ViewModel binding.fabBtnAdd.setOnClickListener { fabViewModel.sendFabClick() } } }
- 业务Fragment中只需订阅对应逻辑即可,无需接触FAB实例:
class HomeFragment : Fragment() { // 获取Activity级别的共享ViewModel private val fabViewModel by activityViewModels<FabSharedViewModel>() override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) // 设置当前页面对应的FAB图标 fabViewModel.fabIconRes.value = R.drawable.ic_add // 订阅FAB点击事件 lifecycleScope.launch { repeatOnLifecycle(Lifecycle.State.STARTED) { fabViewModel.fabClickEvent.collect { // 执行当前页面的新增条目业务逻辑 } } } } }
该方案下横竖屏切换时ViewModel不会销毁,配置和事件都不会丢失,完全不会出现空指针问题。
内容的提问来源于stack exchange,提问作者Scoto
相关产品推荐
相关产品推荐

