0

Hey I was trying to code a simple quiz in Android(Kotlin) and the array seems to be empty when I try to use it. I'm getting the Data from Firebase Database and it gets logged in the LogCat but when I try to get the size of the array it says 0.

Any guidance on what should I do?

Here's my code:

import android.app.ProgressDialog
import android.support.v7.app.AppCompatActivity
import android.os.Bundle
import android.util.Log
import android.widget.Toast
import com.google.firebase.database.*
import kotlinx.android.synthetic.main.activity_quiz.*

class QuizActivity : AppCompatActivity() {

var quizArray: ArrayList<QuizItem> = ArrayList()

private var database: FirebaseDatabase? = null
private var databaseReference: DatabaseReference? = null

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    setContentView(R.layout.activity_quiz)

    database = FirebaseDatabase.getInstance()
    databaseReference = database!!.getReference("quiz")

    var dbRef = databaseReference!!.child("mod1")

    val progress = ProgressDialog(this)
    progress.setMessage("Loading")
    progress.isIndeterminate

    var valueEventListener: ValueEventListener = object : ValueEventListener {
        override fun onCancelled(p0: DatabaseError) {

        }

        override fun onDataChange(p0: DataSnapshot) {

            var contentSnapshot: DataSnapshot = p0
            var contentChildren: Iterable<DataSnapshot> = contentSnapshot.children

            for (content: DataSnapshot in contentChildren){

                var quizItem: QuizItem = content.getValue(QuizItem::class.java)!!
                Log.d("\n Received Data", "data: statement = "+quizItem.statement
                        +" id = "+quizItem.id
                        +" correctOption = "+quizItem.correctOption
                        +" optionA"+quizItem.optionA
                        +" optionB"+quizItem.optionB
                        +" optionC"+quizItem.optionC
                        +" optionD"+quizItem.optionD
                        +" \n ")

                quizArray.add(quizItem)
                Toast.makeText(applicationContext, "Item added for ID = "+quizItem.id, Toast.LENGTH_SHORT).show()

            }

            print(quizArray)
            progress.dismiss()
            Log.d("Data Received", "this@QuizActivity")

            Log.d("Progress Bar", "this@QuizActivity Progress Bar Dismissed")

        }

    }

    dbRef!!.addValueEventListener(valueEventListener)
    progress.show()
    Log.d("Progress Bar", "this@QuizActivity Progress Bar Sent")

    Toast.makeText(applicationContext, "Array Size = "+quizArray.size, Toast.LENGTH_SHORT).show()

    startQuiz()

}

private fun startQuiz(){

    var total: Int = quizArray.size
    Toast.makeText(applicationContext, total.toString(), Toast.LENGTH_SHORT).show()
    var correct: Int = 0
    var incorrect: Int = 0
    var done: Int = 0

    tv_quiz_total.text = total.toString()
    tv_quiz_correct.text = correct.toString()
    tv_quiz_incorrect.text = incorrect.toString()
    tv_quiz_done.text = done.toString()

    var i = 0

    while (i<total){

        tv_quiz_statement.text = quizArray[i].statement
        tv_quiz_opA.text = quizArray[i].optionA
        tv_quiz_opB.text = quizArray[i].optionB
        tv_quiz_opC.text = quizArray[i].optionC
        tv_quiz_opD.text = quizArray[i].optionD

        tv_quiz_opA.setOnClickListener {
            if (quizArray[i].correctOption == 1){
                correct++
                done++
                tv_quiz_correct.text = correct.toString()
                tv_quiz_done.text = done.toString()
                i++
            } else {
                incorrect++
                done++
                i++
                tv_quiz_incorrect.text = incorrect.toString()
            }
        }

        tv_quiz_opB.setOnClickListener {
            if (quizArray[i].correctOption == 2){
                correct++
                done++
                tv_quiz_correct.text = correct.toString()
                tv_quiz_done.text = done.toString()
                i++
            } else {
                incorrect++
                done++
                i++
                tv_quiz_incorrect.text = incorrect.toString()
            }
        }

        tv_quiz_opC.setOnClickListener {
            if (quizArray[i].correctOption == 3){
                correct++
                done++
                tv_quiz_correct.text = correct.toString()
                tv_quiz_done.text = done.toString()
                i++
            } else {
                incorrect++
                done++
                i++
                tv_quiz_incorrect.text = incorrect.toString()
            }
        }

        tv_quiz_opD.setOnClickListener {
            if (quizArray[i].correctOption == 4){
                correct++
                done++
                tv_quiz_correct.text = correct.toString()
                tv_quiz_done.text = done.toString()
                i++
            } else {
                incorrect++
                done++
                i++
                tv_quiz_incorrect.text = incorrect.toString()
            }
        }

    }

}

}

Thank you

2 Answers 2

1

Firebase APIs are asynchronous, meaning that onDataChange() function returns immediately after it's invoked and the callback from the Task it returns, will be called some time later. There are no guarantees about how long it will take. So it may take from a few hundred milliseconds to a few seconds before that data is available. Because that method returns immediately, your quizArray ArrayList you're trying to use outside this method, will always be empty because since the data hasn't finished loading yet.

Basically, you're trying to use a value synchronously from an API that's asynchronous. That's not a good idea. You should handle the APIs asynchronously as intended.

A quick solve for this problem would be to use the value of the ArrayList only inside the callback. If you want to use it outside, I recommend you see the last part of my anwser from this post in which I have explained how it can be done using a custom callback.

Sign up to request clarification or add additional context in comments.

5 Comments

Here's how I modified my code pastebin.com/MnRs9mZV but this causes the app to just crash altogether. Am I doing it right?
If the app crashes, there is a stack trace. Please look that up on logcat, and add it to your question or as a commnet and indicate the exact line at which it occurs.
I rechecked. The while loop inside the 'startQuiz()' function seems to execute an infinite number of times. Any idea why that could be occurring?
Alex, I solved the issue for the loop executing an infinite number of times but now the app crashes as soon as I reach the end of the array. I've added a condition for the last attempt done but that doesn't seem to work. Could you please point me in the right direction? Here's the code pastebin.com/bDkqLaXY
The fact that you get another error, it means that the initial error disappeared and this are good news :) but this sounds as a new issue which basically should be considered another question that cannot be answered in a comment or using only the data above. As a personal guess, I think that might be a NPE but in order to follow the rules of this comunity, please post another fresh question using a MCVE, so me and other users can help you.
0

As pointed out by Alex Mamo, you are trying to access the array synchronously while the function that populates the array is an asynchronous one.

Try calling the startQuiz() function inside the onDataChange() method. Somewhat like this:

var valueEventListener: ValueEventListener = object : ValueEventListener {
    override fun onCancelled(p0: DatabaseError) {

    }

    override fun onDataChange(p0: DataSnapshot) {

        var contentSnapshot: DataSnapshot = p0
        var contentChildren: Iterable<DataSnapshot> = contentSnapshot.children

        for (content: DataSnapshot in contentChildren){

            var quizItem: QuizItem = content.getValue(QuizItem::class.java)!!
            Log.d("\n Received Data", "data: statement = "+quizItem.statement
                    +" id = "+quizItem.id
                    +" correctOption = "+quizItem.correctOption
                    +" optionA"+quizItem.optionA
                    +" optionB"+quizItem.optionB
                    +" optionC"+quizItem.optionC
                    +" optionD"+quizItem.optionD
                    +" \n ")

            quizArray.add(quizItem)
            Toast.makeText(applicationContext, "Item added for ID = "+quizItem.id, Toast.LENGTH_SHORT).show()

        }

        print(quizArray)
        progress.dismiss()

        // call the startQuiz function here
        startQuiz()

        Log.d("Data Received", "this@QuizActivity")

        Log.d("Progress Bar", "this@QuizActivity Progress Bar Dismissed")

    }

}

1 Comment

I tried your answer but doing so results in the app becoming non-responsive and getting a popup that says 'App isn't responding. Do you want to close it?'

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.