【问题标题】:How to handle the asynchronous call in Firebase while working with Android使用 Android 时如何处理 Firebase 中的异步调用
【发布时间】:2022-12-03 13:44:39
【问题描述】:

我很难解决一个问题。所以基本上我正在尝试将我的数据库从 Android 中的 Room 迁移到 Firebase。我已经能够按照我试图保存在房间数据库中的类似结构将我的值存储在 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结构

从 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
    }

数据类:

data class FloorsDataClass(val floor: String, val rooms: List<RoomsDataClass>)

data class RoomsDataClass(val room:String, val devices: List<DevicesDataClass>)

data class DevicesDataClass(val device: String, val machine: String?) 

P.S - 我想从那个 firebase 结构中读取数据,这样我就有一个对象,它包含第一个元素作为第一层,然后在它里面,它可以存储房间,然后它可以进一步存储那个房间的设备。房间循环完成后,我想继续将其与地板一起保存。

如果需要更多代码或 ss 来理解问题,请发表评论。

【问题讨论】:

    标签: android firebase kotlin firebase-realtime-database android-room


    【解决方案1】:

    我建议您使用 Kotlin Flow 或 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

    【讨论】:

      猜你喜欢
      • 2018-03-15
      • 2019-11-15
      • 1970-01-01
      • 2012-07-23
      • 2018-07-21
      • 1970-01-01
      • 2011-09-18
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多