You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.24 03:07:43