如何从Firebase数据库返回读取值?附代码正确性疑问
问题
我有一段Kotlin代码,用于从Firebase Firestore读取用户的姓名:
fun readNameSurnameByEmail(email: String): String { // 从数据库读取除密码外的所有数据,每个功能单独写函数,参数为邮箱 val database = FirebaseFirestore.getInstance() val myRef = database.collection("Users").document(email) myRef.get() .addOnSuccessListener { document -> if (document != null) { val fieldValue = document.get("name_surname") as String Log.d(TAG, "姓名读取成功") } else { Log.d(TAG, "文档为空") } } .addOnFailureListener { exception -> if (exception is FirebaseFirestoreException) { Log.e(TAG, "获取文档出错: ", exception) } } }
我在addOnSuccessListener里创建了fieldValue,请问如何返回它?另外,通过文档名称(邮箱)获取字段值的方式是否正确?
之后我更新了代码,请问这样写是否正确?
fun readNameSurnameByEmail(email: String): String { // 从数据库读取除密码外的所有数据,每个功能单独写函数,参数为邮箱 val database = FirebaseFirestore.getInstance() val myRef = database.collection("Users").document(email) var fieldValue = "" myRef.get() .addOnSuccessListener { document -> if (document != null) { fieldValue = document.get("name_surname") as String Log.d(TAG, "姓名读取成功") } else { Log.d(TAG, "文档为空") } } .addOnFailureListener { exception -> if (exception is FirebaseFirestoreException) { Log.e(TAG, "获取文档出错: ", exception) } } return fieldValue }
解答
1. 通过文档名称获取字段的方式是正确的
Firestore里collection("Users").document(email)的写法没问题,只要你的用户文档确实是用邮箱作为文档ID存储的,这种直接通过文档ID定位文档的方式高效且符合规范。
2. 关于返回fieldValue的问题
你更新后的代码不正确,核心问题是Firestore的get()是异步操作:
- 调用
myRef.get()后,代码会立刻跳到return fieldValue执行,此时addOnSuccessListener里的逻辑还没跑,返回的只是初始的空字符串"",根本拿不到数据库里的真实数据。
正确的解决方式
在Kotlin里处理Firestore异步请求,常用几种方案:
方案一:使用回调函数
定义一个回调接口,在成功获取数据后触发回调返回结果:
// 定义回调接口 interface OnNameSurnameLoadedListener { fun onSuccess(nameSurname: String) fun onFailure(error: Exception) } fun readNameSurnameByEmail(email: String, listener: OnNameSurnameLoadedListener) { val database = FirebaseFirestore.getInstance() val myRef = database.collection("Users").document(email) myRef.get() .addOnSuccessListener { document -> if (document != null && document.exists()) { // 建议加document.exists()判断文档是否存在 val fieldValue = document.getString("name_surname") ?: "" // 用getString避免强转,同时处理null listener.onSuccess(fieldValue) Log.d(TAG, "姓名读取成功") } else { listener.onFailure(IllegalArgumentException("文档不存在或为空")) Log.d(TAG, "文档不存在或为空") } } .addOnFailureListener { exception -> listener.onFailure(exception) Log.e(TAG, "获取文档出错: ", exception) } } // 调用示例 readNameSurnameByEmail("user@example.com", object : OnNameSurnameLoadedListener { override fun onSuccess(nameSurname: String) { // 在这里处理拿到的姓名 } override fun onFailure(error: Exception) { // 处理错误情况 } })
方案二:使用Kotlin协程(推荐)
借助Firestore的协程扩展(需添加依赖),可以用同步写法处理异步操作:
先添加依赖(在build.gradle中):
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-play-services:1.7.3"
然后把函数改成挂起函数:
import kotlinx.coroutines.tasks.await suspend fun readNameSurnameByEmail(email: String): String? { return try { val database = FirebaseFirestore.getInstance() val myRef = database.collection("Users").document(email) val document = myRef.get().await() // 等待异步操作完成 if (document.exists()) { document.getString("name_surname") } else { Log.d(TAG, "文档不存在") null } } catch (e: FirebaseFirestoreException) { Log.e(TAG, "获取文档出错: ", e) null } } // 调用示例(必须在协程作用域内) CoroutineScope(Dispatchers.IO).launch { val nameSurname = readNameSurnameByEmail("user@example.com") // 切换到主线程更新UI等操作 withContext(Dispatchers.Main) { // 处理结果 } }
补充提示
- 尽量避免用
as String强转,推荐用document.getString("name_surname"),它会返回String?,能安全处理字段不存在的情况。 - 建议加上
document.exists()判断,区分“文档不存在”和“文档为空”两种场景。
内容的提问来源于stack exchange,提问作者real4you
相关产品推荐
相关产品推荐

