Kotlin+SQLite安卓应用读取表记录返回空列表问题求助
问题描述
开发安卓答题应用时,通过SQLite预先插入了12条默认记录,在SQLite工具中可正常查询到这些数据,但应用内ViewModel返回的列表为空。通过Inspector确认表存在但无数据,数据库访问无异常。相关代码如下:
Dao接口
@Dao interface QuizDao { @Query("SELECT * FROM quiz WHERE difficulty = :selectedDifficulty") fun loadQuiz(selectedDifficulty : String) : List<Quiz> }
Database类
@Database(entities = [Quiz::class] , version = 1) abstract class QuizDatabase : RoomDatabase(){ abstract fun getQuizDao() : QuizDao }
Repository类
class QuizRepository(var qdao: QuizDao) { fun loadQuestions(difficulty: String): List<Quiz> = qdao.loadQuiz(difficulty) }
ViewModel类
@HiltViewModel class GameViewModel @Inject constructor(var quizRepository: QuizRepository) : ViewModel() { val quizList = MutableLiveData<List<Quiz>>() init { loadAllQuestion() } private fun loadAllQuestion(){ CoroutineScope(Dispatchers.Main).launch { quizList.value = quizRepository.loadQuestions("Easy") } } }
Fragment代码
viewModel.quizList.observe(viewLifecycleOwner) { if (it.isNotEmpty()){ selectedQuestions = it.shuffled().take(10) displayCurrentQuestion() } else { Log.e("tag","Empty") } }
目前Fragment中观察到列表为空,输出"Empty"日志。
核心问题分析
应用内访问的数据库与SQLite工具查看的并非同一个文件,导致预插入的数据未加载到应用实际使用的数据库中;同时存在数据库操作线程违规的潜在问题。
排查与修复步骤
1. 确认数据库文件路径一致性
Room默认数据库存储路径为/data/data/[你的包名]/databases/,你用SQLite工具查看的可能是外部文件(如手动复制的测试文件、assets目录未正确拷贝的源文件)。可通过代码打印路径验证:
val dbPath = quizDatabase.openHelper.writableDatabase.path Log.d("DB_PATH", dbPath)
对比该路径与工具查看的路径是否一致。
2. 修复预插入数据的拷贝逻辑
若通过assets目录下的数据库文件拷贝到应用目录,需确保拷贝逻辑在数据库创建时执行,使用RoomDatabase.Callback实现:
@Database(entities = [Quiz::class], version = 1) abstract class QuizDatabase : RoomDatabase() { abstract fun getQuizDao(): QuizDao companion object { fun getInstance(context: Context): QuizDatabase { return Room.databaseBuilder(context, QuizDatabase::class.java, "quiz.db") .addCallback(object : Callback() { override fun onCreate(db: SupportSQLiteDatabase) { super.onCreate(db) copyDatabaseFromAssets(context) } }) .build() } private fun copyDatabaseFromAssets(context: Context) { val inputStream = context.assets.open("quiz.db") val outputFile = context.getDatabasePath("quiz.db") val outputStream = FileOutputStream(outputFile) val buffer = ByteArray(1024) var length: Int while (inputStream.read(buffer).also { length = it } > 0) { outputStream.write(buffer, 0, length) } outputStream.flush() outputStream.close() inputStream.close() } } }
注意:若数据库已创建过,onCreate不会重复执行,需卸载应用重装,或改用onOpen回调(需添加重复拷贝判断逻辑)。
3. 修正数据库操作线程问题
Room默认禁止在主线程执行数据库操作,原代码同步调用DAO方法会导致静默失败,需改为挂起函数配合协程:
- 修改DAO为挂起函数:
@Dao interface QuizDao { @Query("SELECT * FROM quiz WHERE difficulty = :selectedDifficulty") suspend fun loadQuiz(selectedDifficulty: String): List<Quiz> }
- Repository同步改为挂起函数:
class QuizRepository(var qdao: QuizDao) { suspend fun loadQuestions(difficulty: String): List<Quiz> = qdao.loadQuiz(difficulty) }
- ViewModel使用
viewModelScope在IO线程执行查询:
@HiltViewModel class GameViewModel @Inject constructor(var quizRepository: QuizRepository) : ViewModel() { val quizList = MutableLiveData<List<Quiz>>() init { loadAllQuestion() } private fun loadAllQuestion() { viewModelScope.launch(Dispatchers.IO) { val questions = quizRepository.loadQuestions("Easy") withContext(Dispatchers.Main) { quizList.value = questions } } } }
viewModelScope是ViewModel自带的协程作用域,会在ViewModel销毁时自动取消协程,避免内存泄漏。
4. 验证查询条件匹配性
确认预插入记录的difficulty字段值确实为"Easy"(注意大小写,SQLite默认大小写不敏感,但存储值为"easy"等格式时会导致查询为空)。可通过SQLite工具执行SELECT * FROM quiz WHERE difficulty LIKE '%easy%'验证。
内容的提问来源于stack exchange,提问作者Murad Axundov

