Android Kotlin Room获取LiveData时Observer回调返回null问题
问题描述
我是移动开发新手,尝试在Fragment中从Room数据库获取LiveData,无报错但Observer回调返回null。数据库中存在数据,且可正常访问登录功能,恳请各位提供帮助。
相关代码
StepCount 实体类
@Entity(tableName = "stepcount", foreignKeys = [ForeignKey(entity = Users::class, parentColumns = ["userId"], childColumns = ["userId"], onDelete = CASCADE)]) data class StepCount ( @PrimaryKey(autoGenerate = true) val id: Int = 0, val userId: Int, val date: String, val hour: Int, var steps: Int = 0 )
StepCountDao
@Dao interface StepCountDao { @Query("SELECT * FROM stepcount") fun getAllStepTable(): LiveData<List<StepCount>> }
StepCountRepository
class StepCountRepository(private val dao: StepCountDao) { fun getAllStepsTable() : LiveData<List<StepCount>> { return dao.getAllStepTable() } }
StepCountViewModelFactory
class StepCountViewModelFactory( private val repository: StepCountRepository, private val application: Application): ViewModelProvider.Factory { override fun <T : ViewModel?> create(modelClass: Class<T>): T { if(modelClass.isAssignableFrom(StepCountViewModel::class.java)) { return StepCountViewModel(repository, application) as T } throw IllegalArgumentException("Unknown View Model Class") } }
StepCountViewModel
class StepCountViewModel(private val repository: StepCountRepository, application: Application) : AndroidViewModel(application), Observable { private var _allStepsTable = MutableLiveData<List<StepCount>>() val allStepsTable : LiveData<List<StepCount>> get() = _allStepsTable fun getAllStepsTable() { viewModelScope.launch { _allStepsTable.postValue(repository.getAllStepsTable().value) } } }
DayFragment
class DayFragment : Fragment() { private lateinit var stepCountViewModel: StepCountViewModel override fun onCreateView( inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle? ): View? { val application = requireNotNull(this.activity).application val dao = UsersDatabase.getInstance(application).stepCountDao() val repository = StepCountRepository(dao) val factory = StepCountViewModelFactory(repository, application) stepCountViewModel = ViewModelProvider(this, factory).get(StepCountViewModel::class.java) val prefs = application.getSharedPreferences("prefs", Context.MODE_PRIVATE) val userId = prefs.getInt("userId", -1) Log.d("userId", userId.toString()) stepCountViewModel.allStepsTable.observe(viewLifecycleOwner, Observer { allStepsTable -> if (allStepsTable !== null) Log.i("DAYFRAG_obs_all_steps", allStepsTable.toString()) }) stepCountViewModel.getAllStepsTable() }
问题原因
你的StepCountViewModel存在关键错误:
- Room返回的
LiveData<List<StepCount>>是异步加载的,初始调用.value时数据还未加载完成,因此拿到的是null - 你直接将这个
null通过postValue设置到自己的_allStepsTable中,且未观察Room返回的LiveData以接收后续加载完成的数据
修正方案
以下两种方案任选其一即可:
方案1:直接暴露Repository返回的LiveData(推荐)
移除ViewModel中的MutableLiveData,直接返回Repository的LiveData,Fragment观察该LiveData后会自动接收数据更新:
class StepCountViewModel(private val repository: StepCountRepository, application: Application) : AndroidViewModel(application), Observable { // 直接暴露Repository的LiveData val allStepsTable: LiveData<List<StepCount>> = repository.getAllStepsTable() // 无需保留getAllStepsTable()方法 }
修改Fragment,删除调用getAllStepsTable()的代码:
class DayFragment : Fragment() { private lateinit var stepCountViewModel: StepCountViewModel override fun onCreateView( inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle? ): View? { // ... 初始化ViewModel的代码保持不变 ... stepCountViewModel.allStepsTable.observe(viewLifecycleOwner, Observer { allStepsTable -> if (allStepsTable !== null) Log.i("DAYFRAG_obs_all_steps", allStepsTable.toString()) }) // 删除该行:stepCountViewModel.getAllStepsTable() }
方案2:观察Repository的LiveData并转发数据
若需在ViewModel中处理数据,可保留MutableLiveData,但需正确观察Repository的LiveData:
class StepCountViewModel(private val repository: StepCountRepository, application: Application) : AndroidViewModel(application), Observable { private var _allStepsTable = MutableLiveData<List<StepCount>>() val allStepsTable : LiveData<List<StepCount>> get() = _allStepsTable init { // ViewModel初始化时观察Repository的LiveData,转发数据 repository.getAllStepsTable().observeForever { steps -> _allStepsTable.postValue(steps) } } // 无需保留getAllStepsTable()方法 }
同样,Fragment中删除调用getAllStepsTable()的代码。
额外建议
当前查询会返回所有用户的步数数据,而你已获取到当前登录用户的userId,建议修改Dao的查询逻辑,仅返回当前用户的数据,避免数据冗余:
@Dao interface StepCountDao { @Query("SELECT * FROM stepcount WHERE userId = :userId") fun getStepTableByUserId(userId: Int): LiveData<List<StepCount>> }
后续在Repository、ViewModel中传递userId参数,Fragment传入当前用户的userId即可。
内容的提问来源于stack exchange,提问作者michjea
相关产品推荐
相关产品推荐

