2

I have a lot of questions about handeling asynchronous database in my Android app.

Since I know that database is asynchronous, I've tried several things to handle it. As you can see in my code, I've two functions who need to use an array in my database. My first function (setArrayfromDatabase) will apply changes on my array in my database and my second function (setAnotherArray)need to use this array with changes applied from my first function. Here's my code :

    FirebaseDatabase database = FirebaseDatabase.getInstance();
final DatabaseReference myReff =database.getReference("server").child("user");

    myReff.addListenerForSingleValueEvent(new ValueEventListener() {
    @Override
    public void onDataChange(DataSnapshot dataSnapshot) {

        //Take the array from my database
        final ArrayList<Integer> array_from_database;
        GenericTypeIndicator<ArrayList<Integer>> genericTypeIndicator = new GenericTypeIndicator<ArrayList<Integer>>() {};
        array_from_database = dataSnapshot.getValue(genericTypeIndicator) ;

        System.out.println("1");

        //use this array in this first function, and this function will modify it.
        setArray_for_database(array_from_database);

        myReff.addValueEventListener(new ValueEventListener() {
            @Override
            public void onDataChange(DataSnapshot dataSnapshot) {

                //this will be executed only if my array is changed (So only if my other function have been correctly executed
                setAnotherArray(array_from_database);
                System.out.println("3");
            }

            @Override
            public void onCancelled(DatabaseError databaseError) {}
        });
    }

    @Override
    public void onCancelled(DatabaseError databaseError) {

    }
});

Here's the code for setArray_for_database :

    public void setArray_for_database(ArrayList<Integer> array_from_database){

    FirebaseDatabase database = FirebaseDatabase.getInstance();
    final DatabaseReference myReff =database.getReference();

    //Take the array from my database (gived in parameter)
    final ArrayList<Integer> array = array_from_database;

    myReff.addListenerForSingleValueEvent(new ValueEventListener() {
        @Override
        public void onDataChange(DataSnapshot dataSnapshot) {

            //this will be executed, even if data hasn't been changed because of the method (addListenerForSingleValueEvent)

            System.out.println("2");

            array.set(0,3);

            Map<String, Object> chemainChild = new HashMap<>();
            chemainChild.put("server/user/",array);

            myReff.updateChildren(chemainChild);
        }

        @Override
        public void onCancelled(DatabaseError databaseError) {}
    });
}

Here's my trick. The purpose of having an myReff.addValueEventListener(new ValueEventListener()inside another myReff.addListenerForSingleValueEvent(new ValueEventListener() is to only execute onDataChange if my array from database has been changed. But the problem is that it's not the case. Here's what's print first : 1, 3, 2 instead of 1, 2, 3 like I'm expecting with my trick.

Can you help me ? Am I handling the problem in the wrong way ? How must I do to only execute my second function, in condition that my array has been changed ? How can I keep my code waiting for changes in my database before executing something else ?
Hope you have understood me and feel free to ask me question if you doesn't understand something in my problem.

Peter Haddad
  • 78,874
  • 25
  • 140
  • 134
Mxwan
  • 191
  • 13
  • The fact you're trying to "join" two lists of Firebase references means your data is modeled wrong and you're treating it like a relational database – OneCricketeer Feb 10 '18 at 13:47
  • @Diridou did the answer help you? – Peter Haddad Feb 11 '18 at 11:13
  • @PeterHaddad Currently I'm having trouble with my emulator, So I cannot test it but I will tell you thanks a lot Peter – Mxwan Feb 11 '18 at 12:54
  • Possible duplicate of [How to return dataSnapshot value as a result of a method?](https://stackoverflow.com/questions/47847694/how-to-return-datasnapshot-value-as-a-result-of-a-method) – Alex Mamo Feb 26 '18 at 20:43

1 Answers1

1

Change this:

   myReff.addValueEventListener(new ValueEventListener() {
        @Override
        public void onDataChange(DataSnapshot dataSnapshot) {

            //this will be executed only if my array is changed (So only if my other function have been correctly executed
            setAnotherArray(array_from_database);
            System.out.println("3");
        }

to this:

final FirebaseDatabase database = FirebaseDatabase.getInstance();
final DatabaseReference myReff = database.getReference();

  myReff.addValueEventListener(new ValueEventListener() {
        @Override
        public void onDataChange(DataSnapshot dataSnapshot) {

          DatabaseReference ref2 = database.getReference();

          //Take the array from my database (gived in parameter)
          final ArrayList<Integer> array = array_from_database;
           System.out.println("2");

        array.set(0,3);

        Map<String, Object> chemainChild = new HashMap<>();
        chemainChild.put("server/user/",array);

        myReff.updateChildren(chemainChild);

            System.out.println("3");
        }

Some code may be missing from the above, but you get the idea add the code that is in the method public void setArray_for_database(ArrayList<Integer> array_from_database){ for this to work.

Since onDataChange is asynchronous meaning the program will keep executing, even if data is still not retrieved then this is the only way.

The reason you get 1-3-2, is because of this:

  1. It entered the first onDataChange
  2. It printed "1"
  3. It entered the second onDataChange that is inside the first.
  4. It printed "3" since it is asynchronous, then when the data was retrieved
  5. It called the method and printed "2"

So the best thing is to add the data in the method inside the onDataChange

The above will fix the asynchronous problem that you had in the question. But you really should denormalize your data.

Read this to learn more: https://firebase.googleblog.com/2013/04/denormalizing-your-data-is-normal.html

Peter Haddad
  • 78,874
  • 25
  • 140
  • 134
  • It worked thanks a lot @PeterHaddad ! But I wan to know why ?! – Mxwan Feb 11 '18 at 21:02
  • It printed "1", then while it is retrieving values since it asynchronous it went to the second onDatachange and printed "3", then it went to the first and retrieved the data, then it went to the calling method since it has the data now and executed it @Diridou – Peter Haddad Feb 11 '18 at 21:16
  • I've already understood it thanks. But why when I put the code in it, it works and when I called the method it didn't work ? – Mxwan Feb 11 '18 at 22:04