使用collectAsState()收集Kotlin Flows无响应,Task未插入数据库
问题:Task数据无法插入数据库,UI不显示测试数据
问题现象
点击HomeScreen的添加按钮尝试插入10条测试Task数据,但数据未插入数据库,UI也未渲染出预期的测试数据。
关键代码片段
TaskViewModel.kt
var taskList: StateFlow<List<Task>> = repository.getAllTasks() .stateIn( scope = viewModelScope, started = SharingStarted.WhileSubscribed(5000), initialValue = emptyList() )
HomeScreen.kt
val tasks by taskViewModel.taskList.collectAsState()
完整代码
HomeScreen.kt
@Composable fun HomeScreen(taskViewModel: TaskViewModel, modifier: Modifier = Modifier) { val tasks by taskViewModel.taskList.collectAsState() Column( horizontalAlignment = Alignment.CenterHorizontally, verticalArrangement = Arrangement.Center, modifier = modifier.fillMaxSize() ) { IconButton( modifier = Modifier.size(100.dp), onClick = { //todo : remove after testing (1..10).map { index -> taskViewModel.insertTask( Task( id = index, title = "Task $index", isCompleted = index % 2 == 0, startTime = System.currentTimeMillis(), endTime = System.currentTimeMillis() + (index * 2) * 3600000 ) ) } }, ) { Icon( imageVector = Icons.Default.Add, contentDescription = null, modifier = Modifier.size(100.dp) ) } //Spacer(modifier = Modifier.height(10.dp)) Card( shape = RoundedCornerShape(8.dp), colors = CardDefaults.cardColors(Color.LightGray) ) { Text( text = "Tasks:", fontSize = 20.sp ) Spacer(modifier = Modifier.height(10.dp)) LazyColumn( modifier = Modifier .fillMaxSize() .padding(16.dp, 0.dp) ) { items(items = tasks, key = { it.id }) { TaskComponent(task = it) Spacer(modifier = Modifier.height(10.dp)) } } } } }
TaskComponent.kt
@Composable fun TaskComponent(task: Task, modifier: Modifier = Modifier) { Box( modifier = Modifier .fillMaxWidth() .background(color = Color.White, shape = RoundedCornerShape(8.dp)) .padding(start = 10.dp) .size(50.dp) ) { Text(text = task.title, fontSize = 20.sp) } }
TaskViewModel.kt
@HiltViewModel class TaskViewModel @Inject constructor(private val repository: TaskRepository) : ViewModel() { var task: Task by mutableStateOf( Task(0, "", false, 0, 0) ) private set var taskList: StateFlow<List<Task>> = repository.getAllTasks() .stateIn( scope = viewModelScope, started = SharingStarted.WhileSubscribed(5000), initialValue = emptyList() ) fun insertTask(task: Task) { viewModelScope.launch { repository.insert(task) } } fun updateTask(task: Task) { viewModelScope.launch { repository.update(task) } } fun deleteTask(task: Task) { viewModelScope.launch { repository.delete(task) } } fun getTaskById(id: Int) { viewModelScope.launch { repository.getTaskById(id) } } fun getAllTasks() { viewModelScope.launch { repository.getAllTasks() } } }
Task.kt
@Entity(tableName = "tasks_tbl") data class Task( @PrimaryKey(autoGenerate = true) val id: Int=0, val title: String, val isCompleted: Boolean, val startTime: Long, val endTime: Long )
TaskDao.kt
package com.example.crudapp.data import androidx.room.Dao import androidx.room.Delete import androidx.room.Insert import androidx.room.Query import androidx.room.Update import kotlinx.coroutines.flow.Flow @Dao interface TaskDao { @Insert suspend fun insertTask(task: Task) { } @Update suspend fun updateTask(task: Task) { } @Delete suspend fun deleteTask(task: Task) { } @Query("SELECT * FROM tasks_tbl WHERE id=:id") fun getTaskById(id: Int): Flow<Task> @Query("SELECT * FROM tasks_tbl") fun getAllTasks(): Flow<List<Task>> }
TaskDatabase.kt
@Database(entities = [Task::class], version = 1, exportSchema = false) abstract class TaskDatabase : RoomDatabase() { abstract fun taskDao(): TaskDao }
TaskRepository.kt
class TaskRepository(private val dao: TaskDao) { suspend fun insert(task: Task) { dao.insertTask(task) } suspend fun update(task: Task) { dao.updateTask(task) } suspend fun delete(task: Task) { dao.deleteTask(task) } fun getTaskById(id: Int): Flow<Task> { return dao.getTaskById(id) } fun getAllTasks(): Flow<List<Task>> { return dao.getAllTasks() } }
AppModule.kt
@Module @InstallIn(SingletonComponent::class) object AppModule { @Provides @Singleton fun providesLocalDatabase(@ApplicationContext context: Context): TaskDatabase { return Room.databaseBuilder(context, TaskDatabase::class.java, "task_db") .build() } @Provides @Singleton fun providesTaskDao(db: TaskDatabase): TaskDao { return db.taskDao() } @Provides @Singleton fun providesTaskRepository(dao: TaskDao): TaskRepository { return TaskRepository(dao) } }
预期结果
点击添加按钮后,UI能显示插入的10条测试Task数据。
问题原因及解决方案
核心问题1:TaskDao增删改方法为空实现
TaskDao.kt中的insertTask、updateTask、deleteTask方法自定义了空函数体,导致Room无法生成正确的数据库操作代码,数据根本没插入到数据库。
修复代码:
删除空函数体,让Room自动实现数据库操作:
@Dao interface TaskDao { @Insert suspend fun insertTask(task: Task) @Update suspend fun updateTask(task: Task) @Delete suspend fun deleteTask(task: Task) @Query("SELECT * FROM tasks_tbl WHERE id=:id") fun getTaskById(id: Int): Flow<Task> @Query("SELECT * FROM tasks_tbl") fun getAllTasks(): Flow<List<Task>> }
核心问题2:插入数据时手动指定id与自动生成冲突
Task实体的id设置了@PrimaryKey(autoGenerate = true),意味着主键由Room自动生成,但插入数据时手动给id赋值index,干扰了Room的主键逻辑,可能导致插入失败或数据覆盖。
修复代码:
插入时不指定id,使用默认值0:
// HomeScreen.kt中onClick内的代码修改 (1..10).map { index -> taskViewModel.insertTask( Task( title = "Task $index", isCompleted = index % 2 == 0, startTime = System.currentTimeMillis(), endTime = System.currentTimeMillis() + (index * 2) * 3600000 // 移除id参数,使用默认值 ) ) }
额外优化:确保StateFlow数据持续更新
当前taskList的stateIn使用SharingStarted.WhileSubscribed(5000),UI后台时可能停止发射数据,改为SharingStarted.Eagerly保证数据持续同步:
// TaskViewModel.kt中taskList的修改 var taskList: StateFlow<List<Task>> = repository.getAllTasks() .stateIn( scope = viewModelScope, started = SharingStarted.Eagerly, initialValue = emptyList() )
内容的提问来源于stack exchange,提问作者Hari Joshi
相关产品推荐
相关产品推荐

