我是移动开发的新手。我正在尝试从片段中的 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()
}
这与所提出的问题略有不同 数据流中不推荐使用 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()
}
}
}
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
的时间是在发布之前使用旧值来帮助计算下一个值。