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

适配Fragment后App启动崩溃,Kotlin登录注册SharedPreferences问题排查

问题描述

我正在开发一款基于Kotlin的Android应用,打算用SharedPreferences实现登录注册功能,但运行时应用直接崩溃,无法启动。代码原本基于Activity编写,改成Fragment后出现这个问题,不确定是否和适配有关。我是Android Studio和Kotlin新手。


原代码

Login.kt

class Login : Fragment() {

    private lateinit var fbinding: FragmentLoginBinding
    var txtUsu: EditText? = null
    var txtPass: EditText? = null

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        txtUsu = fbinding.etUsuarioL.findViewById(R.id.et_usuarioL)
        txtPass = fbinding.etContrasenaL.findViewById(R.id.et_contrasenaL)
    }

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

    fun login(view: View){
        var pref = this.activity?.getSharedPreferences(txtUsu?.text.toString(), Context.MODE_PRIVATE)
        var pass = pref?.getString("Pass", "")
        if (pass == txtPass?.text.toString()){
            navLogin()
        }else{
            Toast.makeText(this.activity, "Error en las credenciales, intente de nuevo", Toast.LENGTH_SHORT).show()
            txtUsu?.setText("")
            txtPass?.setText("")
        }
    }

    private fun navLogin(){
        fbinding.btnInises.setOnClickListener {
            Navigation.findNavController(fbinding.root).navigate(R.id.action_login_to_menu2)
        }
    }

}

registro.kt(SignUp)

class registro : Fragment() {

    private lateinit var fBinding: FragmentRegistroBinding
    var txtUsu: EditText? = null
    var txtPass: EditText? = null

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        fBinding = FragmentRegistroBinding.inflate(layoutInflater)

        txtUsu = fBinding.etUsuarioR.findViewById(R.id.et_usuarioR)
        txtPass = fBinding.etContraseAR.findViewById(R.id.et_contraseñaR)
    }

    override fun onCreateView(
        inflater: LayoutInflater, container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View? {
        // Inflate the layout for this fragment
        return inflater.inflate(R.layout.fragment_registro, container, false)
    }

    fun guardar(view: View){
        var pref = this.activity?.getSharedPreferences(txtUsu?.text.toString(), Context.MODE_PRIVATE)
        var editor = pref?.edit()
        editor?.putString("pass", txtPass?.text.toString())
        Toast.makeText(this.activity, "Guardado", Toast.LENGTH_SHORT).show()
        txtUsu?.setText("")
        txtPass?.setText("")
    }

    fun irLogin(){
        fBinding.btnAceptarR.setOnClickListener{
            Navigation.findNavController(fBinding.root).navigate(R.id.action_registro_to_login)
        }
    }

}

崩溃原因及修复方案

核心问题总结

崩溃主要源于Fragment生命周期使用错误、ViewBinding初始化时机错误、空指针风险以及SharedPreferences操作不规范,和Fragment适配直接相关。

具体修复点

1. Login.kt修复

  • 移除onCreate中的控件初始化:Fragment的onCreate阶段未创建视图,fbinding此时未初始化,直接调用会触发UninitializedPropertyAccessException,所有控件初始化移到onCreateView中。
  • 简化EditText获取:ViewBinding生成的fbinding.etUsuarioL本身就是对应控件,无需额外调用findViewById。
  • 统一SharedPreferences键名:注册时存的是"pass",登录时取的是"Pass",大小写不匹配导致验证失败,统一为小写"pass"。
  • 调整登录逻辑:login方法中调用navLogin()是重复设置点击事件,应直接执行导航;增加空输入判断,避免空字符串问题。
  • 使用非空类型:将txtUsu、txtPass改为lateinit var非空类型,减少空安全处理麻烦。

2. registro.kt修复

  • 在onCreateView中初始化ViewBinding:Fragment的layoutInflater在onCreate阶段不可用,必须在onCreateView中初始化绑定,且返回fBinding.root保证视图一致。
  • 提交SharedPreferences编辑:调用editor?.putString后必须执行editor?.apply()或editor?.commit(),否则数据不会保存。
  • 调用点击事件设置方法:irLogin方法需要在onCreateView中调用,否则按钮点击事件无效。
  • 增加空输入判断:避免用户输入空字符串时创建无效的SharedPreferences文件。

修复后的代码

Login.kt

class Login : Fragment() {

    private lateinit var fbinding: FragmentLoginBinding
    private lateinit var txtUsu: EditText
    private lateinit var txtPass: EditText

    override fun onCreateView(
        inflater: LayoutInflater, container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View {
        fbinding = FragmentLoginBinding.inflate(inflater, container, false)
        txtUsu = fbinding.etUsuarioL
        txtPass = fbinding.etContrasenaL
        navLogin()
        return fbinding.root
    }

    fun login(view: View) {
        val username = txtUsu.text.toString().trim()
        val password = txtPass.text.toString().trim()
        
        if (username.isEmpty() || password.isEmpty()) {
            Toast.makeText(activity, "请输入用户名和密码", Toast.LENGTH_SHORT).show()
            return
        }
        
        val pref = activity?.getSharedPreferences(username, Context.MODE_PRIVATE)
        val savedPass = pref?.getString("pass", "")
        
        if (savedPass == password) {
            Navigation.findNavController(fbinding.root).navigate(R.id.action_login_to_menu2)
        } else {
            Toast.makeText(activity, "账号或密码错误,请重试", Toast.LENGTH_SHORT).show()
            txtUsu.setText("")
            txtPass.setText("")
        }
    }

    private fun navLogin() {
        fbinding.btnInises.setOnClickListener {
            login(it)
        }
    }
}

registro.kt(SignUp)

class registro : Fragment() {

    private lateinit var fBinding: FragmentRegistroBinding
    private lateinit var txtUsu: EditText
    private lateinit var txtPass: EditText

    override fun onCreateView(
        inflater: LayoutInflater, container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View {
        fBinding = FragmentRegistroBinding.inflate(inflater, container, false)
        txtUsu = fBinding.etUsuarioR
        txtPass = fBinding.etContraseAR
        irLogin()
        return fBinding.root
    }

    fun guardar(view: View) {
        val username = txtUsu.text.toString().trim()
        val password = txtPass.text.toString().trim()
        
        if (username.isEmpty() || password.isEmpty()) {
            Toast.makeText(activity, "请输入用户名和密码", Toast.LENGTH_SHORT).show()
            return
        }
        
        val pref = activity?.getSharedPreferences(username, Context.MODE_PRIVATE)
        val editor = pref?.edit()
        editor?.putString("pass", password)
        editor?.apply()
        
        Toast.makeText(activity, "注册成功", Toast.LENGTH_SHORT).show()
        txtUsu.setText("")
        txtPass.setText("")
    }

    private fun irLogin() {
        fBinding.btnAceptarR.setOnClickListener {
            Navigation.findNavController(fBinding.root).navigate(R.id.action_registro_to_login)
        }
        // 假设注册按钮id为btnGuardarR,绑定guardar方法
        fBinding.btnGuardarR?.setOnClickListener {
            guardar(it)
        }
    }
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.10 21:55:20