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

如何正确预填充Room数据库并获取数据?Kotlin开发求助

问题:Room数据库无法正确获取内容,显示Coroutine对象信息

开发文本冒险游戏时,使用Room数据库存储任务数据,调用getStoryById(1)后,文本控件显示的是StandaloneCoroutine{Active}@6c601d9而非数据库中的story内容,相关代码如下:

Dao代码

@Dao
interface Dao {
    @Query("SELECT * FROM quests")
    fun readAll(): Flow<List<QuestEntity>>

    @Query("SELECT story FROM quests WHERE id = :id")
    suspend fun getStoryById(id: Int): String
}

Database代码

@Database(entities = [QuestEntity::class], version = 1)
abstract class DataBase : RoomDatabase() {
    abstract fun getDao(): Dao

    companion object {
        @Volatile
        private var INSTANCE: DataBase? = null

        fun getDatabase(context: Context): DataBase {
            if (INSTANCE == null) {
                synchronized(this) {
                    INSTANCE =
                        Room.databaseBuilder(context, DataBase::class.java, "quests")
                            .createFromAsset("quests.db")
                            .build()
                }
            }
            return INSTANCE!!
        }
    }
}

Entity代码

@Entity(tableName = "quests")
data class QuestEntity (
    @PrimaryKey(autoGenerate = true)
    val id: Int,

    val story: String,
    val choice1: String,
    val choice2: String,
    val next1: Int,
    val next2: Int
)

ViewModel代码

class MainViewModel(database: DataBase): ViewModel() {

    val dao = database.getDao()
    var allQuests: LiveData<List<QuestEntity>> = dao.readAll().asLiveData()
    
    fun getStoryById(storyID: Int) = viewModelScope.launch {
        dao.getStoryById(storyID)
    }

    class MainViewModelFactory(val database: DataBase): ViewModelProvider.Factory{
        override fun <T : ViewModel> create(modelClass: Class<T>): T {
            if (modelClass.isAssignableFrom(MainViewModel::class.java)){
                @Suppress("UNCHECKED_CAST")
                return MainViewModel(database) as T
            }
            throw IllegalArgumentException("Unknown ViewModelClass")
        }
    }
}

MainApp代码

class MainApp: Application() {
    val database by lazy{ DataBase.getDatabase(this)}
}

MainActivity代码

class MainActivity : AppCompatActivity() {

    lateinit var bindingClass: ActivityMainBinding
    
    private val viewModel: MainViewModel by viewModels{
        MainViewModel.MainViewModelFactory((applicationContext as MainApp).database)
    }

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        bindingClass = ActivityMainBinding.inflate(layoutInflater)
        setContentView(bindingClass.root)

        bindingClass.textStory.text = viewModel.getStoryById(1).toString() // 未获取到数据库中的story,显示协程对象信息
    }
}

解决方案

1. 核心问题分析

  • ViewModel中的getStoryById仅启动协程调用DAO,但未将查询结果传递给UI;
  • Activity中直接调用viewModel.getStoryById(1)得到的是协程的Job对象,而非数据库数据,toString自然显示协程信息。

2. 修改ViewModel,通过LiveData传递结果

class MainViewModel(database: DataBase): ViewModel() {

    val dao = database.getDao()
    var allQuests: LiveData<List<QuestEntity>> = dao.readAll().asLiveData()
    
    // 用MutableLiveData存储单个story结果,对外暴露不可变的LiveData
    private val _currentStory = MutableLiveData<String>()
    val currentStory: LiveData<String> = _currentStory

    fun getStoryById(storyID: Int) = viewModelScope.launch {
        val story = dao.getStoryById(storyID)
        _currentStory.postValue(story) // 切换到主线程更新LiveData
    }

    // 工厂类保持不变
    class MainViewModelFactory(val database: DataBase): ViewModelProvider.Factory{
        override fun <T : ViewModel> create(modelClass: Class<T>): T {
            if (modelClass.isAssignableFrom(MainViewModel::class.java)){
                @Suppress("UNCHECKED_CAST")
                return MainViewModel(database) as T
            }
            throw IllegalArgumentException("Unknown ViewModelClass")
        }
    }
}

3. 修改Activity,观察LiveData更新UI

class MainActivity : AppCompatActivity() {

    lateinit var bindingClass: ActivityMainBinding
    
    private val viewModel: MainViewModel by viewModels{
        MainViewModel.MainViewModelFactory((applicationContext as MainApp).database)
    }

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        bindingClass = ActivityMainBinding.inflate(layoutInflater)
        setContentView(bindingClass.root)

        // 观察LiveData,数据变化时自动更新UI
        viewModel.currentStory.observe(this) { story ->
            bindingClass.textStory.text = story
        }

        // 触发数据库查询操作
        viewModel.getStoryById(1)
    }
}

4. 预填充数据库的额外注意事项

  • 确保assets/quests.db中的表结构与QuestEntity完全匹配,包括表名quests、列名id/story等,且存在id=1的记录;
  • 若预填充数据库中的id是手动设置的,需将Entity的主键注解改为@PrimaryKey(autoGenerate = false),避免Room自动生成新id覆盖原有数据:
@Entity(tableName = "quests")
data class QuestEntity (
    @PrimaryKey(autoGenerate = false)
    val id: Int,
    val story: String,
    val choice1: String,
    val choice2: String,
    val next1: Int,
    val next2: Int
)

内容的提问来源于stack exchange,提问作者Alena Vakh

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.19 20:57:23