如何在Jetpack Compose按钮中捕获Room操作的SQLiteException
解决Jetpack Compose中Room唯一约束插入的SQLiteException捕获问题
问题根源
当前代码里,ViewModel的addfloor函数通过viewModelScope.launch启动了独立协程,该协程的异常会被viewModelScope默认的异常处理器拦截,无法传递到Composable按钮点击事件的try-catch块中;再加上全局异常捕获模块的拦截,导致你无法在目标位置捕获SQLiteException。
解决方案步骤
1. 将ViewModel中的插入方法改为挂起函数
移除ViewModel内部的协程启动,让函数直接挂起,使异常能向上传递到调用方:
class InventoryViewModel(val repository: InventoryRepository): ViewModel() { // 改为挂起函数,不再内部启动协程 suspend fun addfloor(floor: FloorEntity) { repository.addFloorToRoom(floor) } // 其他函数 }
2. 确保Repository和DAO的异常抛出逻辑正确
DAO层保留@Throws(SQLiteException::class)注解,确保插入冲突时抛出异常:
interface FloorDAO { @Insert(onConflict = OnConflictStrategy.ABORT ) @Throws(SQLiteException::class) suspend fun addFloor(floorEntity: FloorEntity) // 其他DAO函数 }
Repository层直接调用DAO方法,无需额外处理:
class InventoryRepository (val inventoryDB: InventoryDB){ suspend fun addFloorToRoom(floorEntity: FloorEntity){ inventoryDB.floorDao().addFloor(floorEntity) } // 其他函数 }
3. 修改Composable按钮的点击事件逻辑
移除withContext(Dispatchers.IO)(Room的挂起函数默认已在IO线程执行),直接在协程中调用ViewModel的挂起函数并包裹try-catch:
Column( modifier = Modifier .padding(top = 22.dp, start = 6.dp, end = 6.dp) .fillMaxWidth(), horizontalAlignment = Alignment.CenterHorizontally ) { val coroutineScope = rememberCoroutineScope() Button( onClick = { coroutineScope.launch { try { viewModel.addfloor( FloorEntity( 0, houseId!!.toLong(), infloor, "$location.$infloor" ) ) mToast("Floor Added", mContext) } catch (e: SQLiteException) { msgbox( mContext, e.message.toString(), "Floor Could not be Added" ) } } }, colors = ButtonDefaults.buttonColors(Color.Blue) ) { Text(text = "Add Floor in to House") } }
4. 处理全局异常捕获的影响
如果全局异常捕获模块仍拦截该异常,需在全局处理器中添加判断:若异常是SQLiteException且来自Room插入逻辑,不拦截该异常,允许其向上传递到业务代码的try-catch中。
内容的提问来源于stack exchange,提问作者jetberrocal
相关产品推荐
相关产品推荐

