在 Android Kotlin Room 中从数据库获取数据时出现问题

问题描述 投票:0回答:2

我是移动开发的新手。我正在尝试从片段中的 Room 数据库获取 LiveData,我没有收到任何错误,但 Observer 回调为空。我的数据库包含数据,我可以访问登录名。欢迎所有帮助:)

计步

@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>>
}

步数存储库

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()
}
android kotlin android-room android-livedata
2个回答
1
投票

这与所提出的问题略有不同 数据流中不推荐使用 LiveData(source)。

如果您查看Advanced Kotlin Coroutines code lab,我认为它会帮助您开发该功能。

举个例子:

StepCountDao

@Dao
interface StepCountDao{
   @Query("SELECT * FROM stepcount")
   fun getAllStepTable(): Flow<List<StepCount>>
}

StepCountRepository

class StepCountRepository(private val dao: StepCountDao){
 private var _eventProductList = MutableSharedFlow<List<StepCount>>()
    val eventProductList = _eventProductList.asSharedFlow()

    private suspend fun eventAllStepTable(allStepTable: List<StepCount>){
        withContext(Dispatchers.Main){
            _eventProductList.emit(allStepTable)
        }
    }

    suspend fun getAllStepsTable() {
        withContext(Dispatchers.IO){
            eventAllStepTable(dao.getAllStepTable().shareIn(CoroutineScope(Dispatchers.IO), SharingStarted.Lazily).first())
        }
    }
}

StepCountViewModel

class StepCountViewModel(...){
    val event : SharedFlow<List<StepCount>>
        get() = repository.eventProductList

    fun getStepTable(){
        viewModelScope.launch(Dispatchers.IO) {
            repository.getAllStepsTable()
        }
    }
   }
}
    

DayFragment

class DayFragment: Fragment(){
   ... 
   override onCreateView(...){
     repeatOnStarted {
            viewModel.event.collect{
                Log.d("MainActivity", "onCreate: $it")
            }
        }
     viewModel.getStepTable()
   }
}

扩展

fun LifecycleOwner.repeatOnStarted(block: suspend CoroutineScope.() -> Unit) {
    lifecycleScope.launch {
        lifecycle.repeatOnLifecycle(Lifecycle.State.STARTED) {
            block()
        }
    }
}

0
投票

LiveData 必须被动使用。此代码:

fun getAllStepsTable() {
    viewModelScope.launch {
      _allStepsTable.postValue(repository.getAllStepsTable().value)
    }
}

获取 LiveData 的初始

null
值并将其传递给另一个 LiveData,仅一次,因此它将始终为空。

在这里启动一个协程实际上是没有用的,因为你没有做任何需要在主线程之外完成的事情。获取 LiveData 值和发布值都是非阻塞操作。

但是你这样做也毫无意义。您可以只传递原始 LiveData,因为您没有修改它的值。您的 ViewModel 可以更改为:

class StepCountViewModel(private val repository: StepCountRepository, application: Application) : AndroidViewModel(application), Observable {
    val allStepsTable: LiveData<List<StepCount>> = repository.getAllStepsTable()
}

一般来说,您唯一应该阅读

LiveData.value
的时间是在发布之前使用旧值来帮助计算下一个值。

© www.soinside.com 2019 - 2024. All rights reserved.