Android使用Kotlin+Room注册用户名校验时无法访问数据库数据问题
核心问题
你当前的代码存在4处直接导致数据库访问失败的错误:
- Room规定,所有返回非
LiveData/Flow类型的数据库增删查操作,必须在子线程执行,在主线程直接调用会直接抛出崩溃异常 - DAO层查询方法参数类型不匹配:
readUsernnames方法的入参定义为User类型,但SQL语句中:username是字符串类型字段,参数类型不匹配会导致查询逻辑失效 - Repository层存在大量无效代码:
readSomeData里调用usersDao.toString()拿到的是DAO对象的内存地址字符串,完全不是数据库数据;readAllData直接赋值DAO对象本身,没有调用查询方法 - DAO接口末尾多写了一个闭合大括号,会直接导致编译失败
修正方案
1. 修正DAO层代码
统一给非监听型的增、查方法加suspend关键字适配协程线程切换,修正参数类型,删除无用方法,修正命名:
@Dao interface UsersDao { @Insert(onConflict = OnConflictStrategy.ABORT) suspend fun insertUser(user: User) @Query("Select * from users_table Order By id desc") fun readAllData(): LiveData<List<User>> // 修正入参类型为String,和数据库username字段类型匹配 @Query("Select * from users_table Where username = :username") suspend fun getUserByUsername(username: String): User? @Query("Select * from users_table Where email = :email") suspend fun getUserByEmail(email: String): User? }
提示:不需要单独写按密码查询的方法,也绝对不要在数据库中存储明文密码,注册时请对密码做加盐哈希处理后再存储,避免数据泄露。
2. 修正Repository层代码
删除无效逻辑,封装直接给上层调用的校验方法:
class UsersRepository(private val usersDao: UsersDao) { val allUsers: LiveData<List<User>> = usersDao.readAllData() suspend fun addUser(user: User) { usersDao.insertUser(user) } // 直接返回布尔值结果,上层无需额外判空 suspend fun isUsernameExist(username: String): Boolean { return usersDao.getUserByUsername(username.trim()) != null } suspend fun isEmailExist(email: String): Boolean { return usersDao.getUserByEmail(email.trim()) != null } }
你写的UsersDatabase单例实现没有问题,不需要修改。
3. 注册按钮点击逻辑正确调用
在点击事件中通过协程切换到IO线程执行数据库操作,校验通过后再走注册流程:
// 以Activity中为例,Fragment中使用viewLifecycleOwner.lifecycleScope即可 binding.btnRegister.setOnClickListener { val inputUsername = binding.etUsername.text.toString().trim() val inputEmail = binding.etEmail.text.toString().trim() val inputPwd = binding.etPwd.text.toString().trim() // 先做基础空值校验 if (inputUsername.isEmpty() || inputEmail.isEmpty() || inputPwd.isEmpty()) { Toast.makeText(this, "请填写完整注册信息", Toast.LENGTH_SHORT).show() return@setOnClickListener } lifecycleScope.launch { // 初始化数据库和仓库 val db = UsersDatabase.getDatabase(this@RegisterActivity) val repo = UsersRepository(db.usersDao()) // 校验用户名是否已存在 if (repo.isUsernameExist(inputUsername)) { withContext(Dispatchers.Main) { Toast.makeText(this@RegisterActivity, "用户名已存在,请更换", Toast.LENGTH_SHORT).show() } return@launch } // 可选校验邮箱是否已注册 if (repo.isEmailExist(inputEmail)) { withContext(Dispatchers.Main) { Toast.makeText(this@RegisterActivity, "该邮箱已被注册", Toast.LENGTH_SHORT).show() } return@launch } // 所有校验通过,写入新用户数据 val newUser = User( username = inputUsername, email = inputEmail, password = inputPwd // 实际使用请替换为哈希加密后的密码 ) repo.addUser(newUser) // 切回主线程做UI跳转 withContext(Dispatchers.Main) { Toast.makeText(this@RegisterActivity, "注册成功", Toast.LENGTH_SHORT).show() // 后续跳转登录页/首页逻辑 } } }
- 如果你没有引入协程依赖,也可以用原生线程池、RxJava处理数据库操作的线程切换,但协程是目前Android官方推荐的最简实现方案。
内容的提问来源于stack exchange,提问作者semicn
相关产品推荐
相关产品推荐

