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

使用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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.23 20:24:53