You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Room数据库登录验证崩溃求助:输入含特殊字符点击按钮崩溃

问题描述

我在LoginFragment中实现了邮箱和密码输入框,需求是验证数据库中的邮箱与密码是否匹配。点击按钮时,数据库需读取数据并通过邮箱+密码查找对应账户。assets目录下已存放数据库文件,但输入框包含特殊字符时,点击按钮会导致应用崩溃。以下是相关代码:


数据库类

package Room.Database
import Room.DAO.DAO
import Room.Repository.Admindata
import android.content.Context
import androidx.room.Database
import androidx.room.Room
import androidx.room.RoomDatabase

@Database (entities = [Admindata::class], version = 2, exportSchema = true)
abstract class AdminDatabase:RoomDatabase(){
abstract fun getDAO(): DAO
companion object{
    fun getadminDB(context: Context):AdminDatabase{
        return Room.databaseBuilder(context.applicationContext,AdminDatabase::class.java,"admindatabase").createFromAsset("SQLDB/dbforpetproject.db").build()
    }
}
}

DAO接口

package Room.DAO
import Room.Repository.Admindata
import androidx.room.Dao
import androidx.room.Query

@Dao
interface DAO {
  @Query("select * from admin_table where email = :email")
  suspend fun findbyEmail(email:String) : Admindata

  @Query("select * from admin_table  where password = :password")
  suspend fun findbypassword(password:String) : Admindata
}

实体类Admindata

package Room.Repository
import androidx.room.ColumnInfo
import androidx.room.Entity
import androidx.room.PrimaryKey

@Entity(tableName= "admin_table")
data class Admindata(
    @PrimaryKey
    val id:Int = 1,
    @ColumnInfo(name = "email")
    val email:String = "admin@gmail.com",
    @ColumnInfo(name = "password")
    val password:String = "12345678"
)

LoginFragment代码

package com.example.myapplication
import Room.Database.AdminDatabase
import Room.Repository.Admindata
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.Toast
import androidx.fragment.app.Fragment
import androidx.navigation.fragment.findNavController
import com.example.myapplication.databinding.FragmentLoginBinding
import kotlinx.coroutines.CoroutineName
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.launch

class LoginFragment : Fragment() {
private lateinit var binding : FragmentLoginBinding
private lateinit var db:AdminDatabase
private lateinit var admin:Admindata

val scope = CoroutineScope(CoroutineName("Scope"))
override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    db = AdminDatabase.getadminDB(requireContext())
}
    override fun onCreateView(
        inflater: LayoutInflater, container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View? {
       binding = FragmentLoginBinding.inflate(inflater)
        return binding.root
    }

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)
        binding.continuebutton.setOnClickListener {
            if(binding.EmailSpace.length() == 0){
                Toast.makeText(context,"The space is empty write email",Toast.LENGTH_SHORT).show()
            }

            if(binding.PasswordSpace.length() == 0){
                Toast.makeText(context,"The space is empty write password",Toast.LENGTH_SHORT).show()
            }
           if(binding.PasswordSpace.length() <8){
               Toast.makeText(context,"Password is too small",Toast.LENGTH_SHORT).show()
           }
      if(binding.EmailSpace.length() >0){ // 我的错误实现
          scope.launch {
              findbyEmail(email = admin.email)
          } 
findNavController().navigate(R.id.action_loginFragment_to_lastfragment)
      }
else{
    Toast.makeText(context,"Email is wrong",Toast.LENGTH_SHORT).show()
} // 我的错误实现
            if(binding.PasswordSpace.length() >0){ // 我的错误实现
                scope.launch {
                    findbyPassword(password = admin.password)
                }
                findNavController().navigate(R.id.action_loginFragment_to_lastfragment)
            }
            else{
                Toast.makeText(context,"Password is wrong",Toast.LENGTH_SHORT).show() // 我的错误实现
            }
        }
    }
    @Override
    private suspend fun findbyEmail(email:String){
        val tuple = db.getDAO().findbyEmail(email)
        if(tuple.email != email){
            Toast.makeText(context,"Email is wrong",Toast.LENGTH_SHORT).show()
        }
    }
    @Override
    private suspend fun findbyPassword(password:String){
        val tuple = db.getDAO().findbypassword(password)
        if(tuple.password != password){
            Toast.makeText(context,"Password is wrong",Toast.LENGTH_SHORT).show()
        }
    }
}

修复方案

1. 修正DAO接口逻辑

当前DAO分别查询邮箱和密码,既不符合"同时匹配邮箱+密码"的业务逻辑,且查询无结果时会抛出EmptyResultSetException导致崩溃。修改为同时验证邮箱和密码,并返回可空对象:

package Room.DAO
import Room.Repository.Admindata
import androidx.room.Dao
import androidx.room.Query

@Dao
interface DAO {
    // 同时验证邮箱和密码,返回可空对象避免无结果时崩溃
    @Query("select * from admin_table where email = :email and password = :password")
    suspend fun findByEmailAndPassword(email: String, password: String): Admindata?
}

2. 重构LoginFragment核心逻辑

  • 移除未初始化的admin变量,直接获取输入框文本
  • 统一前端校验流程,避免分散判断
  • 协程内完成数据库验证,验证通过后再跳转页面
  • 确保Toast在主线程调用,避免线程异常
  • 处理数据库查询无结果的情况

修改后的代码:

package com.example.myapplication
import Room.Database.AdminDatabase
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.Toast
import androidx.fragment.app.Fragment
import androidx.navigation.fragment.findNavController
import com.example.myapplication.databinding.FragmentLoginBinding
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext

class LoginFragment : Fragment() {
    private lateinit var binding: FragmentLoginBinding
    private lateinit var db: AdminDatabase

    // 使用IO线程处理数据库操作,主线程处理UI
    private val scope = CoroutineScope(Dispatchers.IO)

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        db = AdminDatabase.getadminDB(requireContext())
    }

    override fun onCreateView(
        inflater: LayoutInflater, container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View? {
        binding = FragmentLoginBinding.inflate(inflater)
        return binding.root
    }

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)
        binding.continuebutton.setOnClickListener {
            val inputEmail = binding.EmailSpace.text.toString().trim()
            val inputPassword = binding.PasswordSpace.text.toString().trim()

            // 前端校验
            when {
                inputEmail.isEmpty() -> {
                    showToast("请输入邮箱")
                    return@setOnClickListener
                }
                inputPassword.isEmpty() -> {
                    showToast("请输入密码")
                    return@setOnClickListener
                }
                inputPassword.length < 8 -> {
                    showToast("密码长度不能少于8位")
                    return@setOnClickListener
                }
                else -> {
                    // 数据库验证
                    scope.launch {
                        val matchedAdmin = db.getDAO().findByEmailAndPassword(inputEmail, inputPassword)
                        // 切换到主线程处理UI
                        withContext(Dispatchers.Main) {
                            if (matchedAdmin != null) {
                                findNavController().navigate(R.id.action_loginFragment_to_lastfragment)
                            } else {
                                showToast("邮箱或密码错误")
                            }
                        }
                    }
                }
            }
        }
    }

    // 封装Toast,确保在主线程调用
    private fun showToast(message: String) {
        Toast.makeText(context, message, Toast.LENGTH_SHORT).show()
    }
}

3. 额外注意事项

  • 确认assets目录结构:createFromAsset("SQLDB/dbforpetproject.db")需与实际文件路径一致,确保assets下存在SQLDB文件夹及对应数据库文件
  • Room参数化查询会自动处理特殊字符转义,无需手动处理,避免SQL注入风险
  • 确保Room依赖版本一致,避免版本冲突导致异常

内容的提问来源于stack exchange,提问作者user20498005

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.07 00:10:20