Home > Blockchain >  MutableList remaining empty (?) even though i'm populating it
MutableList remaining empty (?) even though i'm populating it

Time:11-25

I'm trying to populate a mutable list so that I can use it for a recycler view. Unfortunately, although (i think) I'm populating the list, it's still remaining empty and the recycler view is not working (and I imagine it's because of the list issue). Please see below for the code:

        private val newList: MutableList<NewListModel> = mutableListOf()
        private val oldList = retrieveOldList()

        private fun retrieveAndPopulate() {
                
                for (i in 0 until oldList.size){
        
                    val oldItem = oldList[i]
                    
                    val itemOne = oldItem.itemOne
                    val itemTwo = oldItem.itemTwo
                    val itemThree = oldItem.itemThree
                    val itemFour = oldItem.itemFour
    
                    val newItemData =
                        NewListModel(
                            itemOne, itemTwo, itemThree, itemFour
                        )
    
                newList.add(newItemData)
                Log.d(
                    "RetrieveData",
                    "${newItemData.itemOne} has been added to the list."
                )
            }
        }

The class below is for the "NewListModel"

@Keep
@IgnoreExtraProperties
data class NewListModel (
    var itemOne: String ?= null,
    var itemTwo: String ?= null,
    var itemThree: String ?= null,
    var itemFour: String ?= null,
)

Below is how i try to populate the "oldList"

fun retrieveData(): MutableList<OldListModel> {

        val list: MutableList<OldListModel> = mutableListOf()
        val ref = FirebaseDatabase.getInstance().getReference("/storage")

        ref.addValueEventListener(object : ValueEventListener {
            override fun onDataChange(snapshot: DataSnapshot) {
                if (snapshot.exists()) {
                    ref.get()
                        .addOnSuccessListener {

                            for (listItem in snapshot.children) {
                                val listItem = snapshot.getValue(OldListModel::class.java)

                                if (listItem != null) {
                                    list.add(listItem)
                                }
                            }
                        }
                } else {
                    Log.d(
                        "Data",
                        "Retrieving data was unsuccessful."
                    )
                }
            }

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

It's probably worth mentioning that I'm getting the data from one mutable list and adding it to another. Any help is much appreciated

(below is how i try to populate the recycler view)

val newList = retrieveAndPopulate()

val recyclerView = findViewById<View>(R.id.recyclerView) as RecyclerView
val layoutManager = LinearLayoutManager(this)
recyclerView.layoutManager = layoutManager
val adapterAdapter = AdapterAdapter(newList)
recyclerView.adapter = adapterAdapter

CodePudding user response:

Your problem is that you think you're running code sequentially when it's running asynchronously. See the numbered comments from your function to trace the order of execution:

fun retrieveData(): MutableList<OldListModel> {

    // 1. Here you create a list
    val list: MutableList<OldListModel> = mutableListOf()
    val ref = FirebaseDatabase.getInstance().getReference("/storage")

    // 2. Here a listener is added that will let you know LATER when the data is ready
    ref.addValueEventListener(object : ValueEventListener {
        // 4. LATER the data changed will get called
        override fun onDataChange(snapshot: DataSnapshot) {
            if (snapshot.exists()) {
                ref.get()
                    .addOnSuccessListener {
                        // 5. EVEN LATER this listener is called with data
                        for (listItem in snapshot.children) {
                            val listItem = snapshot.getValue(OldListModel::class.java)

                            // 6. FINALLY - you add to a list that has long since stopped being relevant
                            if (listItem != null) {
                                list.add(listItem)
                            }
                        }
                    }
            } else {
                Log.d(
                    "Data",
                    "Retrieving data was unsuccessful."
                )
            }
        }

        override fun onCancelled(error: DatabaseError) {
        }
    })
    return list // 3. Here you return the EMPTY list that was created
}

A solution - though likely not the best solution is to update your list once the callbacks complete:

fun retrieveData(): { // No longer returning anything

    // Remove this, no longer returning anything
    // val list: MutableList<OldListModel> = mutableListOf()

    val ref = FirebaseDatabase.getInstance().getReference("/storage")
    ref.addValueEventListener(object : ValueEventListener {
        override fun onDataChange(snapshot: DataSnapshot) {
            if (snapshot.exists()) {
                ref.get()
                    .addOnSuccessListener {
                        // Move list here
                        val list: MutableList<OldListModel> = mutableListOf()

                        for (listItem in snapshot.children) {
                            val listItem = snapshot.getValue(OldListModel::class.java)

                            if (listItem != null) {
                                list.add(listItem)
                            }
                        }
                        
                        // Since you're using Kotlin, you could use a map,
                        // but that's unrelated to this issue
                        // val list = snapshot.children.map { getValue(...) }.filterNotNull()
                        
                        // Now that we have a full list here, update:
                        updateAdapterWithNewData(list)
                    }
            } else {
                Log.d(
                    "Data",
                    "Retrieving data was unsuccessful."
                )
            }
        }

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

Where updateAdapterWithNewData is a function you write to do as it says.

Please read up on asynchronous programming and make sure you understand how the code is flowing when using callbacks / listeners in frameworks like Firebase.

  • Related