I have this little Android app that I am working on to sign up and add a password and username to a Firebase Real-time Database. Everything in the database has a number key, somewhat like the number 1 would be set to a username and a corresponding password, 2 would have values of username and passwords too, and so on. So, I had created this for-loop that starts at the key of 1 and checks every number key if it has already been assigned to. If the current value has not been assigned to, a new username and password would be stored there and the loop would break when the sign up button is clicked.
As an example of how this loop works: Lets say that "1" has a username of "11111" and a password of "11111", "2" has a username of "22222" and a password of "22222", and 3 is empty. When the for loop reaches the key of 3, it would have a username of "33333" and a password of "333333" written to it while none of the other key's values are changed. HOWEVER, the issue is that I had tested this with 10 values and after the value has been set, it just keeps setting values beyond what I asked it to set. I believe this is because of the conditional at the end of my for-loop below. I want to store if the data had been changed within a variable and use it later but this is not working correctly. I had tested this by plugging in "true" into the if statement, and it breaks, but when using the variable, it does not.
Here is my loop:
var dataHasBeenSet : Boolean = false
for(num in 1..10){
database.child(num.toString()).get().addOnSuccessListener {
if(it.value == "" || it.value == null || it.value == "null") {
database.child(num.toString()).setValue(AccountAdder(password.text.toString(), username.text.toString()))
dataHasBeenSet = true
} else {
dataHasBeenSet = false
//Runs again
}
}.addOnFailureListener {
Toast.makeText(this, "Database cannot be reached at this time. Please try again later", Toast.LENGTH_LONG).show()
}
//Conditional works, however the condition statement does not. Why?
if(dataHasBeenSet){
break
} else {
continue
}
}
Yes, the data is actually being set, but the loop acts like the dataHasBeenSet variable has not been set to true even though I had reassigned it to be. Remember, the condition at the bottom works because I had plugged in true by default, but it seems like the dataHasBeenSet variable's value never gets changed even if the data is added to the database.
What I have tried:
How can I put an end to this nightmare?
You need to move the conditional within the callback of the addOnSuccessListener
, get it out won't be triggered whenever the OnSuccessListener
callback get triggered whenever the firebase query background thread is over.
There is another issue that the for loop runs in the main thread, and the firebase methods runs in worker threads, so here you want to break/continue the iterations from different threads.
I get the error: 'break' or 'continue' breaks across a function or class boundary
This is because you try to return from within a lambda of the firebase callbacks and the compiler not sure where it should return.
I encourage you to change the approach of using a loop to use a listener interface whose callback get called whenever you want to reiterate the loop (i.e. whenever dataHasBeenSet).. but actually in this approach you don't need to the boolean dataHasBeenSet
, you can just invoke the listener callback with the new value of iteration.
Create this interface:
interface NextCallListener {
fun onNext(num: Int)
}
Implement it in the surrounding class, I'll assume its name as MainActivity
:
class MainActivity: AppCompatActivity() , NextCallListener {
}
Then move your firebase code into a new method that accepts the iteration number, and an instance of the listener:
fun nextCall(num: Int, listener: NextCallListener) {
database.child(num.toString()).get().addOnSuccessListener {
if (it.value == "" || it.value == null || it.value == "null") {
database.child(num.toString())
.setValue(AccountAdder(password.text.toString(), username.text.toString()))
if (num <= 10) // loop max value
listener.onNext(num + 1) // trigger the interface callback for the next iteration
}
}.addOnFailureListener {
Toast.makeText(
this,
"Database cannot be reached at this time. Please try again later",
Toast.LENGTH_LONG
).show()
}
}
Then call the method with the first iteration whenever you want to do the username/password checks:
nextCall(1, this)
And implement the interface callback:
class MainActivity: AppCompatActivity() , NextCallListener {
override fun onNext(num: Int) {
nextCall(num, this)
}
}