如何正确预填充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
相关产品推荐
相关产品推荐

