ViewModel读取Room数据库数据到TextView时应用崩溃问题解决
问题解答:Room空数据导致TextView显示崩溃,RecyclerView却正常的原因及修复方案
问题描述
开发了一个结合TextView与Room数据库的简单应用:数据库有数据时,将内容显示在TextView中;首次启动数据库为空时,通过ViewModel读取数据并显示到TextView会导致应用崩溃,但使用RecyclerView仅显示空列表不会崩溃。
相关代码
Fragment代码
class HomeFragment : Fragment() { private lateinit var viewModel: MyViewModel override fun onCreateView( inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle? ): View? { // Inflate the layout for this fragment return inflater.inflate(R.layout.fragment_home, container, false) } override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) viewModel = ViewModelProvider(this)[MyViewModel::class.java] viewModel.getAllDataObservers().observe(viewLifecycleOwner){ //textview for show data txt_show_number.text = it[0].question_number txt_show_question.text = it[0].question } }
ViewModel代码
class MyViewModel(application: Application) : AndroidViewModel(application) { var allUsers: MutableLiveData<List<EntityStudent>> = MutableLiveData() fun getAllDataObservers(): MutableLiveData<List<EntityStudent>> { getAllData() return allUsers } fun getAllData() { val dao = RoomDb.getAppDatabase((getApplication()))?.mainDao() val list = dao?.getAllData() allUsers.postValue(list!!) } fun insert(entityStudent: EntityStudent) { val dao = RoomDb.getAppDatabase((getApplication()))?.mainDao() val list = dao?.insert(entityStudent) getAllData() }
报错信息
E/AndroidRuntime: FATAL EXCEPTION: main Process: com.example.student, PID: 2679 java.lang.IndexOutOfBoundsException: Index: 0, Size: 0 at java.util.ArrayList.get(ArrayList.java:437) at com.example.student.HomeFragment.onViewCreated$lambda-0(HomeFragment.kt:32) at com.example.student.HomeFragment.$r8$lambda$kFVd0uSVK9JwnEU47m0mSdTzXmo(Unknown Source:0) at com.example.student.HomeFragment$$ExternalSyntheticLambda0.onChanged(Unknown Source:4)
第32行代码:txt_show_number.text = it[0].question_number
原因分析
- TextView崩溃原因:数据库为空时,Room返回的
List<EntityStudent>是空列表(Size为0),但你直接通过it[0]去取第一个元素,触发了IndexOutOfBoundsException数组越界异常,直接导致应用崩溃。 - RecyclerView正常原因:RecyclerView的Adapter内部默认会处理空列表的情况——当数据源为空时,Adapter不会执行绑定视图的逻辑,自然不会触发越界异常,只会显示空白列表。
修复方案
1. Fragment中添加空列表判断(最直接修复)
在LiveData的观察者中,先判断列表是否为空,再决定是显示数据还是空提示:
viewModel.getAllDataObservers().observe(viewLifecycleOwner){ studentList -> if (studentList.isNotEmpty()) { txt_show_number.text = studentList[0].question_number txt_show_question.text = studentList[0].question } else { // 数据库为空时显示提示文本 txt_show_number.text = "暂无数据" txt_show_question.text = "请添加数据" } }
2. 优化ViewModel中的代码(避免潜在空指针)
ViewModel中使用了非空断言!!,如果Room数据库初始化失败或者Dao获取失败,list!!会直接抛出空指针异常,建议改为安全处理:
fun getAllData() { val dao = RoomDb.getAppDatabase(getApplication())?.mainDao() val list = dao?.getAllData() ?: emptyList() // 为空时返回空列表,而非null allUsers.postValue(list) }
3. 可选:默认插入初始化数据(按需使用)
如果应用首次启动必须要有数据显示,可以在数据库创建时插入默认数据,比如在RoomDatabase的callback中实现:
@Database(entities = [EntityStudent::class], version = 1) abstract class RoomDb : RoomDatabase() { abstract fun mainDao(): MainDao companion object { fun getAppDatabase(context: Context): RoomDb? { return Room.databaseBuilder(context, RoomDb::class.java, "student_db") .addCallback(object : Callback() { override fun onCreate(db: SupportSQLiteDatabase) { super.onCreate(db) // 插入默认数据 CoroutineScope(Dispatchers.IO).launch { getAppDatabase(context)?.mainDao()?.insert(EntityStudent(question_number = "1", question = "默认问题")) } } }) .build() } } }
内容的提问来源于stack exchange,提问作者Hosein.m.barazandeh
相关产品推荐
相关产品推荐

