I'm trying to get data from Firebase and return it as an array. So far this works, however the data remains to be the default data that I had set it.
Here is the code (I added a few comments to clarify where it works and where id doesn't):
fun getRandomQuestion() : ArrayList<ArrayList<String>>{
val random = Random(1234567)
val randQuestionId = random.nextInt(0, 27)
var dataRequestResult : String = "empty"
var requestSide1 : String = "empty"
var requestSide2 : String = "empty"
var requestCount1 : String = "empty"
var requestCount2 : String = "empty"
//Get a random question from the DB
Firebase.database.reference.child(Constants.wyrDBQuestionLocation)
.child(randQuestionId.toString())
.get()
.addOnFailureListener { task ->
Log.i("Get WYR Question Status", "Failed: " task.message.toString())
requestSide1 = "empty"
requestSide2 = "empty"
requestCount1 = "empty"
requestCount2 = "empty"
}.addOnSuccessListener {
Log.i("Get WYR Question Status", "Success")
//Logging this data returns the correct information from firebase, no issue here
Log.i("Request Data Result", it.value.toString())
dataRequestResult = it.value.toString()
}
requestSide1 = dataRequestResult.substringAfter("side1Question=").substringBefore("}")
requestSide2 = dataRequestResult.substringAfter("side2Question=").substringBefore(",")
requestCount1 = dataRequestResult.substringAfter("sideCount1=").substringBefore(",")
requestCount2 = dataRequestResult.substringAfter("sideCount2=").substringBefore(",")
//Return the value
return arrayListOf(arrayListOf(requestSide1, requestCount1), arrayListOf(requestSide2, requestCount2))
}
Activity where I try to get the returned value:
val myQuestion = databaseController.getRandomQuestion()
//This is where the issue lies
//When I get the returned data from the function above, it gives me all of the defaults I had set in the function (which is "empty")
//It logs:`[["empty", "empty"],["empty", "empty"]]`
Log.i("My Question", myQuestion.toString())
The data in the success listener is correct, but the return value is still default. Here is an image of the logs:
"Request Log Result", don't mind the result, I'm making a WYR app.
I have noticed that the request from the listener had come last, not first
I think this had to do with the data not even being completely gathered yet because in another app I made, all i had to do was give it a delay and it would be set, but in this case, I have no idea what to do.
Any help would be greatly appreciated! :)
CodePudding user response:
this is my solution
Method 1
fun getRandomQuestion() : ArrayList<ArrayList<String>>{
val random = Random(1234567)
val randQuestionId = random.nextInt(0, 27)
var dataRequestResult : String = "empty"
var requestSide1 : String = "empty"
var requestSide2 : String = "empty"
var requestCount1 : String = "empty"
var requestCount2 : String = "empty"
//Get a random question from the DB
Firebase.database.reference.child(Constants.wyrDBQuestionLocation)
.child(randQuestionId.toString())
.get()
.addOnFailureListener { task ->
Log.i("Get WYR Question Status", "Failed: " task.message.toString())
requestSide1 = "empty"
requestSide2 = "empty"
requestCount1 = "empty"
requestCount2 = "empty"
}.addOnSuccessListener {
Log.i("Get WYR Question Status", "Success")
//Logging this data returns the correct information from firebase, no issue here
Log.i("Request Data Result", it.value.toString())
dataRequestResult = it.value.toString()
requestSide1 = dataRequestResult.substringAfter("side1Question=").substringBefore("}")
requestSide2 = dataRequestResult.substringAfter("side2Question=").substringBefore(",")
requestCount1 = dataRequestResult.substringAfter("sideCount1=").substringBefore(",")
requestCount2 = dataRequestResult.substringAfter("sideCount2=").substringBefore(",")
}
//Return the value
return arrayListOf(arrayListOf(requestSide1, requestCount1), arrayListOf(requestSide2, requestCount2))
}
Method 2
fun getRandomQuestion() : ArrayList<ArrayList<String>>{
val random = Random(1234567)
val randQuestionId = random.nextInt(0, 27)
var dataRequestResult : String = "empty"
var requestSide1 : String = "empty"
var requestSide2 : String = "empty"
var requestCount1 : String = "empty"
var requestCount2 : String = "empty"
//Get a random question from the DB
Firebase.database.reference.child(Constants.wyrDBQuestionLocation)
.child(randQuestionId.toString())
.get()
.addOnFailureListener { task ->
Log.i("Get WYR Question Status", "Failed: " task.message.toString())
requestSide1 = "empty"
requestSide2 = "empty"
requestCount1 = "empty"
requestCount2 = "empty"
}.addOnSuccessListener {
Log.i("Get WYR Question Status", "Success")
//Logging this data returns the correct information from firebase, no issue here
Log.i("Request Data Result", it.value.toString())
dataRequestResult = it.value.toString()
requestSide1 = dataRequestResult.substringAfter("side1Question=").substringBefore("}")
requestSide2 = dataRequestResult.substringAfter("side2Question=").substringBefore(",")
requestCount1 = dataRequestResult.substringAfter("sideCount1=").substringBefore(",")
requestCount2 = dataRequestResult.substringAfter("sideCount2=").substringBefore(",")
//Return the value
return arrayListOf(arrayListOf(requestSide1, requestCount1), arrayListOf(requestSide2, requestCount2))
}
return null
}
CodePudding user response:
The listeners you use when requesting database data will fire later than the getRandomQuestion
function returns ArrayList
. You can use Kotlin coroutines to work with callbacks and suspend a coroutine until callback is fired, for example:
suspend fun getRandomQuestion() = suspendCoroutine<String> { continuation ->
Firebase.database.reference.child(Constants.wyrDBQuestionLocation)
.child(randQuestionId.toString())
.get()
.addOnFailureListener { task ->
continuation.resume("empty")
}.addOnSuccessListener {
continuation.resume(it.value.toString())
}
}
// somewhere in your code (e.g. in ViewModel, Activity or Fragment):
someCoroutineScope.launch {
val question: String = getRandomQuestion();
// ... use question to update UI
}
In Activity
or Fragment
you can use lifecycleScope
, in ViewModel
- viewModelScope
.
suspendCoroutine
builder function is used to wait for the callbacks. suspendCoroutine
suspends coroutine in which it executed until we decide to continue by calling appropriate methods - Continuation.resume...
.