如何通过多参数方法更新LiveData避免空指针异常
问题根因
空指针异常由几个问题共同触发:
- ViewModel中请求失败时主动给
_events赋值null,但Fragment观察LiveData的回调里直接对入参调用map,Kotlin非空类型检查遇到null直接抛NPE loadEvents声明了dateTo入参,但调用时完全没传该参数,易导致接口请求参数异常、返回失败,进一步触发空值赋值逻辑- Fragment里多余嵌套了一层
lifecycleScope.launch调用loadEvents,但loadEvents内部已经用viewModelScope处理协程,外层协程反而可能导致调用时机错乱 - 观察回调里重复给RecyclerView设置adapter,会导致列表滚动状态丢失,属于不必要的逻辑
修复方案
1. 修正ViewModel层逻辑
不要给列表型LiveData发射null值,用空列表做兜底,补全缺失的请求参数,注意协程内子线程更新数据要用postValue:
private val _events = MutableLiveData<List<BookEvent>>() // 不要用自定义getter,直接赋值即可,避免重复触发实例访问逻辑 val events: LiveData<List<BookEvent>> = _events fun loadEvents( dateTo: String, dateFrom: String?, dateTime: String?, pageIndex: Int, types: String ){ viewModelScope.launch { val response = eventsBooksRepository.getEvents( pageSize = 100, pageIndex = pageIndex + 1, types = types, dateTime= dateTime, dateFrom = dateFrom, dateTo = dateTo // 补全之前漏传的参数 ) if(response.isSuccessful){ // 接口返回data为null时用空列表兜底,绝不发射null _events.postValue(response.data ?: emptyList()) } else { // 请求失败可以保留旧数据,也可以赋值空列表,不要赋值null // 错误提示单独用一个一次性事件LiveData分发,不要和列表数据混放 _events.postValue(emptyList()) } } }
注意:
MutableLiveData.value只能在主线程赋值,协程切到子线程执行请求时必须用postValue更新数据,否则会触发线程安全问题。
2. 修正Fragment层逻辑
去掉多余的协程嵌套,把adapter初始化移到视图创建阶段,不要在观察回调里重复设置adapter:
// 在onViewCreated等视图初始化阶段完成adapter绑定、观察者注册 override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) binding.eventsRecycler.adapter = eventsAdapter getEvents() // 页面初始化时就注册观察者,不要等触发请求才注册 } private fun retrieveEvents(){ // 去掉外层多余的lifecycleScope,直接调用方法即可 myViewModel.loadEvents( dateTo = "", // 按业务实际值传入 dateTime= if (eventsByDate) dateTime else "", dateFrom = if (previousEvents) dayDateFrom else "", pageIndex = 1, types = myViewModel.filtersApplied ) } private fun getEvents(){ myViewModel.events.observe(viewLifecycleOwner){ list -> // 这里拿到的list永远非空,不需要额外做空判断 val mappedEvents = list.map { event -> val descriptionPair = getEventIcon(event) bookEventModel.bookEventItem( bookEventModel( event.eventId, event.eventType, event.eventDate, event.bookState ) ) } // 直接更新adapter数据即可,不要重复setAdapter eventsAdapter.submitList(mappedEvents) // 如果是普通RecyclerView.Adapter,就先clear再addAll } }
修改完成后,你可以在任意业务场景直接调用retrieveEvents(),传入不同参数触发数据更新,不会再出现空指针异常。
内容的提问来源于stack exchange,提问作者dazai
相关产品推荐
相关产品推荐

