kotlin 如何在使用Android时处理Firebase中的异步调用

wz8daaqr  于 2022-12-04  发布在  Kotlin
关注(0)|答案(1)|浏览(197)

我很难解决一个问题。所以基本上我正在尝试将我的数据库从Android中的Room迁移到Firebase。我已经能够按照我试图保存在Room Database中的类似结构在Firebase中存储我的值。
现在我面临的主要问题是从Firebase中检索值。更具体地说,我正在使用嵌套的回收器视图,所以我有一个有点复杂的结构。我将在下面解释它。
因此,数据的工作原理是,有楼层,每层楼有房间,每个房间有机器。所以它在层次结构中。当我使用本地数据库时,我创建了一个函数,在我的ViewModel中处理此功能:
它看起来是这样的:

fun load() {

        //Observing all floors
        getAllFloors.observeForever(Observer {
            viewModelScope.launch(Dispatchers.Main) {

                /** Converting list of floors to a distinct and sorted floor list
                 * Input -> [0,0,1,2,3,4,2,4,1,3], Output -> [0,1,2,3,4]
                 */
                val distinctFloorNames = it.distinct().sorted()
                val floorsList = mutableListOf<FloorsDataClass>()
                val devicesList = mutableListOf<String>()

                //Loop over distinct floors for getting each floor
                for (floorName in distinctFloorNames) {

                    //At each floor prepare a list of rooms
                    val rooms = repository.getAllRooms(floorName)
                    //Getting distinct (in case rooms gets repeated -> only during testing) and sorted rooms
                    val distinctRoomNames = rooms.distinct().sorted()
                    Timber.d("Floor: $floorName, Rooms: $distinctFloorNames")
                    val roomsList = mutableListOf<RoomsDataClass>()

                    //Loop over rooms in the floor
                    for (roomName in distinctRoomNames) {

                        //In each room prepare a list of devices
                        val devicesName = repository.getAllDevices(roomName)
                        val distinctDeviceName = devicesName.distinct().sorted()

                        //Transform the list of string to list of DeviceClassObject
                        val deviceData = mutableListOf<DevicesDataClass>()

                        //For each device get the attached machine
                        for (device in distinctDeviceName) {

                            //Get the machine associated with the device
                            val machine = repository.getMachine(device)
                            Timber.d("Machine: $machine")

                            //Attach the device and machine to the [DevicesDataClass Object]
                            deviceData.add(DevicesDataClass(device, machine))

                            /**Attach the room name and the devices list to the
                             *[RoomDataClass Object]
                             **/
                            roomsList.add(RoomsDataClass(roomName, deviceData))

                            //Saving devices in a list for managing
                            devicesList.add(device)
                        }
                    }

                    /**Add the room list to the floor object and
                    add the floor to the floor list **/
                    floorsList.add(FloorsDataClass(floorName, roomsList))

                }
                //Sending the list as livedata to be further observed - from add details for device - manage devices fragment
                devicesLiveData.postValue(devicesList)
                /** Post the complete value of floorList in the floorListLiveData which will be
                 * observed from the [ControlPanelFragment]
                 */
                floorListLiveData.postValue(floorsList)
                Timber.d("$floorsList")
            }
        })
    }

现在,为了显示我刚刚观察到的这个floorsList的数据,然后将它传递给我的嵌套适配器,它会相应地显示数据。
我正试图以类似的方式从Firebase获取数据。我已经达到了一个地步,我甚至可以获取我的楼层和每个楼层的房间,但问题是在获取机器时出现的。
基本上我在我的项目中使用了两个ValueEventListener。我使用来自其中一个侦听器的值来填充我的数据。但是由于从Firebase阅读数据是异步的,我的数据字段显示为空,因为我试图在数据来自数据库之前使用该数据。这是主要问题。
火座结构

从Firebase阅读值的代码

private fun readRoomsAndFloorFromFirebase(): List<FloorsDataClass> {

        val roomsDataClass: MutableList<RoomsDataClass> = mutableListOf()
        val devicesDataClass: MutableList<DevicesDataClass> = mutableListOf()
        val floorsDataClass: MutableList<FloorsDataClass> = mutableListOf()

        val listener = object : ValueEventListener {
            override fun onDataChange(snapshot: DataSnapshot) {

                var floors: FloorsDataClass
//                Log.d(TAG, "Data: ${snapshot}")
                for (i in snapshot.children) {
                    Log.i(TAG, "Data: $i")
//                    floor = "${i.key}"
                    for (j in i.children) {
                        Log.i(TAG, "Value: ${j.key}")
//                        roomsList.add("${j.key}")

                        val listener = object : ValueEventListener {
                            override fun onDataChange(snapshot: DataSnapshot) {
//                                Log.w(TAG, "Listener: ${snapshot.child("Device ID").value}")
                                val device = snapshot.child("Device ID").value.toString()
                                val machine = snapshot.child("Machine").value.toString()

                                devicesDataClass.add(DevicesDataClass(device, machine))
                            }
                            override fun onCancelled(error: DatabaseError) {}
                        }

                        //Getting the list of devices and saving it with particular room
                        roomsDataClass.add(RoomsDataClass("${j.key}", devicesDataClass))

                        realtime.child("USERS").child(auth.uid!!).child(
                            "ADDED DEVICES"
                        ).child("${i.key}").child("${j.key}")
                            .addValueEventListener(listener)

                    }

                    //Storing the particular floor with room data class values
                    floors = FloorsDataClass("${i.key}", roomsDataClass)
                    floorsDataClass.add(floors)
                }
                Log.e(TAG, "List 1: $floorsDataClass")

            }

            override fun onCancelled(error: DatabaseError) {}
        }
        realtime.child("USERS").child(auth.uid!!).child("ADDED DEVICES")
            .addValueEventListener(listener)

        Log.e(TAG, "List: $floorsDataClass")

        return floorsDataClass
    }

数据类别:
第一个
另外,我想从firebase结构中读取数据,这样我就有了一个包含第一个元素(即第一层)的对象,在该对象中,它可以存储房间,然后进一步存储该房间的设备。一旦房间循环完成,我想继续并将其与楼层一起保存。
如果需要更多的代码或ss来理解问题,请评论。

xzlaal3s

xzlaal3s1#

我建议您使用KotlinFlow或Android LiveData来设置观察器,以便在数据准备就绪时执行一些工作。

val floors: MutableLiveData<FloorsDataClass> = MutableLiveData()

private fun startReadingRoomsAndFloorFromFirebase() {

    val listener = object : ValueEventListener {
        override fun onDataChange(snapshot: DataSnapshot) {
            floors.value = // your parsed data
            // or if you prefer:
            floors.postValue(/* your parsed data */)
        }

        override fun onCancelled(error: DatabaseError) {}
    }
}

然后在Activity/Fragment中观察LiveData并在回调中设置RecyclerView。我鼓励您回顾this tutorial on LiveData

相关问题