Kotlin中Firebase实时数据库嵌套数据获取失败求助
问题描述
开发应用时,尝试从Firebase实时数据库获取嵌套数据并在HomeFragment的卡片中展示,但始终无法实现。已通过谷歌、YouTube搜索未找到解决方案,以下是相关代码与数据库结构:
HomeFragment 获取数据函数
private lateinit var mList : MutableList<all_job_details_to_show> private fun getDataFromFirebase() { databaseRef.addValueEventListener(object : ValueEventListener{ override fun onDataChange(snapshot: DataSnapshot) { mList.clear() for (taskSnapshot in snapshot.children){ val job_list= taskSnapshot.key?.let { val jobDetails = taskSnapshot.child("Jobs") all_job_details_to_show( it, all_job_details_to_show.JobDetails( jobDetails.child("startTime").value as String, jobDetails.child("endTime").value as String, taskSnapshot.child("payPerDay").value as Double, taskSnapshot.child("jobTitle").value as String, taskSnapshot.child("description").value as String, ), taskSnapshot.child("businessName").value.toString(), taskSnapshot.child("businessLogo").value.toString(), taskSnapshot.child("address").value.toString(), taskSnapshot.child("ratings").value.toString()?.toDoubleOrNull() ?: 0.0, taskSnapshot.child("jobId").value.toString() ) } if (job_list != null){ mList.add(job_list) } } adapter.notifyDataSetChanged() } override fun onCancelled(error: DatabaseError) { Toast.makeText(activity, error.message, Toast.LENGTH_SHORT).show() } }) }
数据库引用代码
val uid1 = FirebaseAuth.getInstance().uid getDatabaseRef = FirebaseDatabase.getInstance().reference.child("user_business/$uid1")
数据类定义
data class all_job_details_to_show( val businessName : String, val businessLogo : String, val address :String, val ratings: Double, val jobId: String ){ constructor() : this( "","","",0.0,"") data class JobDetails( val startTime: String, val endTime: String, val payPerDay: Double, val jobTitle: String, val description: String, ){constructor() : this( "","",0.0,"","")} }
RecyclerView 适配器代码
class addedJobListAdapter(private val list: MutableList<all_job_details_to_show>): RecyclerView.Adapter<addedJobListAdapter.addedJobListViewHolder>(){ private var listener: addedJobListAdapterClickListener? = null fun setListener(listener: addedJobListAdapterClickListener){ this.listener=listener } inner class addedJobListViewHolder( val binding: JobDescriptionCardBinding): RecyclerView.ViewHolder(binding.root) override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): addedJobListViewHolder { val binding = JobDescriptionCardBinding.inflate(LayoutInflater.from(parent.context),parent,false) return addedJobListViewHolder(binding) } override fun getItemCount(): Int { return list.size } override fun onBindViewHolder(holder: addedJobListViewHolder, position: Int) { with(holder){ with(list[position]){ binding.address.text= address binding.brandName.text = businessName binding.ratings.text = ratings.toString() binding.jobName.text = JobDetails.jobTitle.toString() // access properties of JobDetails instance binding.startTime.text = JobDetails.startTime binding.endTime.text = JobDetails.endTime binding.rates.text = JobDetails.payPerDay.toString() binding.description.text = JobDetails.description.toString() Glide.with(itemView.context) .load(businessLogo) .into(binding.brandLogo) if (ratings >= 4.50) { binding.ratings.setTextColor(Color.parseColor("#318F2F")) // light green } else if (ratings >= 4.00 && ratings < 4.50) { binding.ratings.setTextColor(Color.parseColor("#3CC93A")) // light green } else if (ratings >= 3.50 && ratings < 4.00) { binding.ratings.setTextColor(Color.parseColor("#B1AB16")) }else if (ratings >= 3.00 && ratings < 3.50) { binding.ratings.setTextColor(Color.parseColor("#E5DE21")) } else if (ratings >= 2.50 && ratings < 3.00) { binding.ratings.setTextColor(Color.parseColor("#B18616")) }else if (ratings >= 2.00 && ratings < 2.50) { binding.ratings.setTextColor(Color.parseColor("#FFC225")) }else { binding.ratings.setTextColor(Color.parseColor("#FF3F25")) } binding.editButton.setOnClickListener{ listener?.onDeleteButtonClicked(this) } binding.deleteBtn.setOnClickListener { listener?.onEditButtonClicked(this) } } } } interface addedJobListAdapterClickListener{ fun onDeleteButtonClicked(allJobDetailsToShow: all_job_details_to_show) fun onEditButtonClicked(allJobDetailsToShow: all_job_details_to_show) } }
Firebase 数据库结构
根节点为user_business,其下包含业务用户的UID节点,每个UID节点内有Jobs子节点(包含startTime、endTime等字段)以及businessName、businessLogo等其他子节点。
数据库结构截图
- 用户业务根节点与Jobs子节点关系:

- user_business节点下的其他子节点:

- Jobs节点详情:

解决方案
问题1:数据类定义缺失核心属性
all_job_details_to_show主数据类未定义存储嵌套数据的JobDetails属性,导致数据库数据无法被正确存储,适配器也无法访问嵌套字段。
修复后的数据类:
data class all_job_details_to_show( val businessName : String, val businessLogo : String, val address :String, val ratings: Double, val jobId: String, // 添加JobDetails属性,对应数据库嵌套结构 val jobDetails: JobDetails ){ // 无参构造器同步包含jobDetails的默认值 constructor() : this( "","","",0.0,"", JobDetails()) data class JobDetails( val startTime: String, val endTime: String, val payPerDay: Double, val jobTitle: String, val description: String, ){ constructor() : this( "","",0.0,"","") } }
问题2:Firebase数据解析逻辑错误
- 数据库引用已定位到单个UID节点,无需遍历
snapshot.children; payPerDay、jobTitle等字段属于Jobs子节点,而非UID节点的直接子节点;- 原初始化
all_job_details_to_show时参数顺序错误; - 避免强制类型转换,改用安全转换避免空值崩溃。
修复后的数据获取函数:
private fun getDataFromFirebase() { databaseRef.addValueEventListener(object : ValueEventListener{ override fun onDataChange(snapshot: DataSnapshot) { mList.clear() val jobDetailsSnapshot = snapshot.child("Jobs") val jobItem = all_job_details_to_show( snapshot.child("businessName").value.toString(), snapshot.child("businessLogo").value.toString(), snapshot.child("address").value.toString(), snapshot.child("ratings").value.toString()?.toDoubleOrNull() ?: 0.0, snapshot.child("jobId").value.toString(), all_job_details_to_show.JobDetails( jobDetailsSnapshot.child("startTime").value.toString(), jobDetailsSnapshot.child("endTime").value.toString(), jobDetailsSnapshot.child("payPerDay").value.toString()?.toDoubleOrNull() ?: 0.0, jobDetailsSnapshot.child("jobTitle").value.toString(), jobDetailsSnapshot.child("description").value.toString() ) ) mList.add(jobItem) adapter.notifyDataSetChanged() } override fun onCancelled(error: DatabaseError) { Toast.makeText(activity, error.message, Toast.LENGTH_SHORT).show() } }) }
问题3:适配器访问嵌套字段方式错误
原代码通过JobDetails.jobTitle调用静态属性,实际应访问当前数据实例的jobDetails成员属性。同时修正了点击事件逻辑颠倒的问题。
修复后适配器的onBindViewHolder方法:
override fun onBindViewHolder(holder: addedJobListViewHolder, position: Int) { with(holder){ with(list[position]){ binding.address.text= address binding.brandName.text = businessName binding.ratings.text = ratings.toString() // 改为访问当前实例的jobDetails属性 binding.jobName.text = jobDetails.jobTitle binding.startTime.text = jobDetails.startTime binding.endTime.text = jobDetails.endTime binding.rates.text = jobDetails.payPerDay.toString() binding.description.text = jobDetails.description Glide.with(itemView.context) .load(businessLogo) .into(binding.brandLogo) // 评级颜色逻辑保持不变... binding.editButton.setOnClickListener{ listener?.onEditButtonClicked(this) } binding.deleteBtn.setOnClickListener { listener?.onDeleteButtonClicked(this) } } } }
额外注意事项
- 确认Firebase实时数据库规则允许读取
user_business/$uid1节点数据,否则会触发onCancelled回调; - 所有数据库字段读取都采用
toString()或toDoubleOrNull()的安全方式,避免空值导致的崩溃。
内容的提问来源于stack exchange,提问作者Akhil Bhatnagar
相关产品推荐
相关产品推荐

