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
  • 73,993
  • 25
  • 133
  • 124
Mxwan
  • 171
  • 13

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
  • 73,993
  • 25
  • 133
  • 124
  • 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