1

I'm trying to iterate the firebase-database and add its elements to my object. It works, but for some reason I fail to understand, it ads each element twice, the log output showing something like this:

TAGZ: DataSnapshot { key = recipeHeaderFirebase, value = Tahini-Oat Cookies } 
TAGZ: DataSnapshot { key = recipeHeaderFirebase, value = Tahini-Oat Cookies }
TAGZ: DataSnapshot { key = recipeHeaderFirebase, value = Chocolate & Cinnamon Cookies }
TAGZ: DataSnapshot { key = recipeHeaderFirebase, value = Chocolate & Cinnamon Cookies }
TAGZ: DataSnapshot { key = recipeHeaderFirebase, value = Sweet Potato Muffin }
TAGZ: DataSnapshot { key = recipeHeaderFirebase, value = Sweet Potato Muffin }
TAGZ: DataSnapshot { key = recipeHeaderFirebase, value = Almond Butter Cookies }
TAGZ: DataSnapshot { key = recipeHeaderFirebase, value = Almond Butter Cookies }

The Firebase-database structure looks like this (note that I have ignored the other elements for now):

FbDb

My Kotlin code looks like this:

fbdb = FirebaseDatabase.getInstance()
ref = fbdb!!.getReference("cookies")

ref!!.addChildEventListener(object: ChildEventListener {

    override fun onChildAdded(snapshot: DataSnapshot?, p1: String?) {
        val children = snapshot!!.children //DIRECT REFERENCE TO CHILD, USED FOR LOOP
        val header = snapshot!!.child("recipeHeaderFirebase")

        for(it in children) {
            var tempRecipe = RecipeTemplate()
            tempRecipe.recipeHeader = header.toString()
            Log.d("TAGZ", tempRecipe.recipeHeader)  //OUTPUT SHOWS DUPLICATE VALUES
            }
        }
    }) 

What am I missing? I successfully retrieve the elements in Xcode using similar code...

EDIT FULL CODE ON REQUEST:

package com.healthandchocolate.sjostedtafzelius.healthchocolateandroid

import android.support.v7.app.AppCompatActivity
import android.os.Bundle
import android.util.Log
import com.google.firebase.database.*

class RecipeGridView : AppCompatActivity() {

private var fbdb: FirebaseDatabase? = null
private var ref: DatabaseReference? = null
var recipeArray: MutableList<RecipeTemplate> = mutableListOf()

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    setContentView(R.layout.activity_recipe_grid_view)
    Log.d("TAG", "ON CREATE");

    fbdb = FirebaseDatabase.getInstance()
    ref = fbdb!!.getReference("cookies")

    ref!!.addChildEventListener(object: ChildEventListener {

        override fun onChildChanged(snapshot: DataSnapshot?, p1: String?) {

        }

        override fun onChildMoved(p0: DataSnapshot?, p1: String?) {
            Log.d("TAG", "ON CHILD MOVED");
        }

        override fun onChildRemoved(p0: DataSnapshot?) {
            Log.d("TAG", "ON CHILD REMOVED");

        }

        override fun onCancelled(error: DatabaseError) {
            print(error)
            Log.d("TAG", "ON ERROR");

        }

        override fun onChildAdded(snapshot: DataSnapshot?, p1: String?) {
            Log.d("TAGZ", "CALLED")  //keyName of each child

            val children = snapshot!!.children //DIRECT REFERENCE TO CHILD, USED FOR LOOP
            val header = snapshot!!.child("recipeHeaderFirebase")

            for(it in children) {
                var tempRecipe = RecipeTemplate()
                tempRecipe.recipeHeader = header.toString()
                //Log.d("TAGZ", tempRecipe.recipeHeader)  //keyName of each child

                recipeArray.add(tempRecipe)
            }
        }
        }) //END FB CODE
}
}
ReyAnthonyRenacia
  • 17,219
  • 5
  • 37
  • 56
johanjohansson
  • 491
  • 1
  • 9
  • 17

2 Answers2

1

The Activity is created each time the system route an intent to the instance of current activity. So the function onCreate can be called many times.

Each time your function gets called, the callback function you hold for the addChildEventListener event is added to the listener. Thereafter, when the event is triggered, the callback function is called as much as it has been linked to the listener before.

In order to prevent this behavior, you can define android:launchmode:"singleTop" in your App manifest file in the tag <activity>

This allow the system to route the intents to an other function onNewIntent instead of onCreate

For more information, take a tour on : https://developer.android.com/guide/topics/manifest/activity-element#lmode

I hope this will help.

I try my best to write correct english, so feel free to correct me.

Pritam
  • 339
  • 3
  • 23
csblo
  • 1,571
  • 13
  • 20
0

The correct way to do this is to move your code that adds the ChildEventListener from onCreate to a function fun attachValueEventListener() outside onCreate.

Then attach the listener in onStart method...

override fun onStart() {
    super.onStart()
    // add an event listener to watch changes in firebase database
    attachValueEventListener()
}

...and detach the listener in onStop

override fun onStop() {
    super.onStop()
    // detach the event listener that watches changes in firebase database
    detachValueEventListener()
}

private fun detachValueEventListener() {
    if (mValueEventListener != null) {
        myRef?.removeEventListener(mValueEventListener)
        mValueEventListener = null
    }
}

You will also need to declare

private var mValueEventListener: ValueEventListener? = null
CEO tech4lifeapps
  • 885
  • 1
  • 12
  • 31