首页 > 解决方案 > 使用 MVVM + Coroutines 将实时数据库值获取到 ArrayList

问题描述

我想使用协程和 MVVM 从实时数据库中获取数据列表,并将它们放入 recyclerview。它运行,但来自实时数据库的数据是在 recyclerview.adapter 初始化之后添加的,因此将 list.size 返回为 0

视图模型.kt

class DasarhukumdetailsViewModel : ViewModel() {
val database = FirebaseDatabase.getInstance().reference
var dasarHukumList = ArrayList<DasarHukum>()

fun getDHData(KEYVALUE: String?) = liveData(Dispatchers.Main.immediate) {
    val postListener = object : ValueEventListener {
        override fun onDataChange(snapshot: DataSnapshot) {
            for (snapshot in snapshot.children) {
                val res = snapshot.getValue(DasarHukum::class.java)
                Log.d("dataAdd", "Adding: ${res?.filename}")
                dasarHukumList.add(res!!)
            }
        }

        override fun onCancelled(databaseError: DatabaseError) {
            // Getting Post failed, log a message
            Log.w("readDHList", "loadPost:onCancelled", databaseError.toException())
            throw databaseError.toException()
        }
    }
    try {
        if (KEYVALUE != null) {
            database.child("dasarhukum").child(KEYVALUE).addValueEventListener(postListener)
        }
        emit(Resource.success(dasarHukumList))
    } catch (e: Exception) {
        emit(Resource.error(
            null,
            e.message ?: "Unknown Error"
        ))
    }
}

片段.kt

override fun onCreateView(
    inflater: LayoutInflater, container: ViewGroup?,
    savedInstanceState: Bundle?
): View? {
   [...]
    observerSetup(KEYVALUE)
    rvSetup()
    return binding.root
}
fun observerSetup(keyvalue: String?) {
    viewModel.getDHData(keyvalue).observe(viewLifecycleOwner, {
        when (it.status) {
            Status.SUCCESS -> {
                it?.data.let { dhList ->
                    dasarHukumAdapter.dasarhukumList = dhList
                    dasarHukumAdapter.notifyDataSetChanged()
                }
            }
            Status.ERROR -> {
                Toast.makeText(context, "Error getting documents: ${it.message}", Toast.LENGTH_LONG)
                Log.e("realDB", it.message!!)
            }
        }
    })
}
fun rvSetup() {
    with(binding.rvDasarHukum) {
        layoutManager = LinearLayoutManager(context)
        setHasFixedSize(true)
        adapter = dasarHukumAdapter
    }
}

RVAdapter.kt

class DasarHukumAdapter : RecyclerView.Adapter<DasarHukumAdapter.DasarHukumViewHolder>() {
var dasarhukumList: List<DasarHukum>? = null
    set(value) {
        notifyDataSetChanged()
        field = value
    }

class DasarHukumViewHolder(private val binding: ItemDasarhukumBinding) :
    RecyclerView.ViewHolder(binding.root) {
    fun bind(dasarHukum: DasarHukum?) {
        binding.dasarhukum = dasarHukum
        binding.executePendingBindings()
    }
}

override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): DasarHukumViewHolder {
    val layoutInflater = LayoutInflater.from(parent.context)
    val binding = ItemDasarhukumBinding.inflate(layoutInflater, parent, false)
    return DasarHukumViewHolder(binding)
}

override fun onBindViewHolder(holder: DasarHukumViewHolder, position: Int) {
    val dasarHukum = dasarhukumList?.get(position)
    Log.d("dhVH", "Adding: ${dasarHukum?.name}")
    holder.bind(dasarHukum)
}

override fun getItemCount(): Int {
    Log.d("dhCount", "List size: ${dasarhukumList?.size}")
    return dasarhukumList?.size ?: 0
}

recyclerview如何等待viewmodel.getDHData()首先返回arraylist然后初始化,以便它可以显示到recyclerview?

标签: androidfirebasekotlinfirebase-realtime-databasekotlin-coroutines

解决方案


当您尝试使用以下代码行发出结果时:

emit(Resource.success(dasarHukumList))

数据尚未完成加载,因此列表的大小为零。Firebase API 是异步的,因此您需要等待数据才能在其他操作中使用它。因此,任何需要来自实时数据库的数据的代码都需要在“onDataChange()”方法中,或者从那里调用。因此,在这种情况下,最简单的解决方案是在回调内部移动有关发出结果的逻辑:

fun getDHData(KEYVALUE: String?) = liveData(Dispatchers.Main.immediate) {
    val postListener = object : ValueEventListener {
        override fun onDataChange(snapshot: DataSnapshot) {
            for (snapshot in snapshot.children) {
                val res = snapshot.getValue(DasarHukum::class.java)
                Log.d("dataAdd", "Adding: ${res?.filename}")
                dasarHukumList.add(res!!)
            }
            try {
                if (KEYVALUE != null) {
                    database.child("dasarhukum").child(KEYVALUE).addValueEventListener(postListener)
                }
                emit(Resource.success(dasarHukumList))
            } catch (e: Exception) {
            emit(Resource.error(
                null,
                e.message ?: "Unknown Error"))
            }
        }

        override fun onCancelled(databaseError: DatabaseError) {
            // Getting Post failed, log a message
            Log.w("readDHList", "loadPost:onCancelled", databaseError.toException())
            throw databaseError.toException()
        }
    }
}

推荐阅读