0

I am trying to add data (The String name) to the list in the nested onDataChange. In the outer onDataChange, the data is being added to the timeIntervals arraylist. In the nested onDataChange, I do get the String name value, however, it is not being inserted into the the timeIntervals arraylist and thus does not show up in the List. I am not sure why this happens. Sorry about the bad code. the deadline is approaching.

  mUserDatabase = FirebaseDatabase.getInstance().getReference().child("Users").child(current_id).child("week").child(day);
    mUserDatabase.addValueEventListener(new ValueEventListener() {
        @Override
        public void onDataChange(DataSnapshot dataSnapshot) {
            daySchedule = (ArrayList<Long>)dataSnapshot.getValue();
            Log.d("daySchedule", "onDataChange: "+daySchedule.toString());
            int k = 0;
            startIndexes = new ArrayList<Integer>();
            endIndexes = new ArrayList<Integer>();
            timeIntervals = new ArrayList<String>();
            apptindex = new ArrayList<Integer>();
            apptnumber = new ArrayList<Long>();
            for (int i = 0; i < daySchedule.size(); i++) {
                //starthour = daySchedule.get(i);
                if (daySchedule.get(i) == 1L) {
                    //Log.d("checkone", "onDataChange: ");
                    startIndexes.add(i);
                    k = i;
                    while (k <= 15 && daySchedule.get(k) == 1L) {
                        k = k + 1;
                    }
                    endIndexes.add(k-1);
                    i = k;
                } else if (daySchedule.get(i) != 0) {
                    //Toast.makeText(DayAppointmentsActivity.this, "hi", Toast.LENGTH_SHORT).show();
                    apptindex.add(i);
                    apptnumber.add(daySchedule.get(i));
                }
            }
            Log.d("startindexes", "onDataChange: "+startIndexes.toString());
            Log.d("endindexes", "onDataChange: "+endIndexes.toString());
            int starthour = 0;
            int startmin = 0;
            int endhour = 0;
            int endmin = 0;
            for (int j = 0; j < startIndexes.size(); j++) {
                starthour = startIndexes.get(j);
                int check1 = starthour;
                if (starthour == 0) {
                    starthour = starthour + 9;
                } else if (starthour % 2 == 0) {
                    starthour = 9 + (starthour / 2);
                } else {
                    starthour = 9 + (starthour / 2);
                    startmin = 30;
                }
                endhour = endIndexes.get(j);
                int check2 = endhour;
                if (endhour == 0) {
                    endhour = endhour + 9;
                } else if (endhour % 2 == 0) {
                    endhour = 9 + (endhour / 2);
                } else {
                    endhour = 9 + (endhour / 2);
                    endmin = 30;
                }
                if (check1 == check2) {
                    if (check1 == 0) {
                        endmin = 30;
                    } else if (check2 % 2 == 0) {
                        endmin = 30;
                    } else {
                        endmin = 0;
                        endhour = endhour + 1;
                    }
                }
                Log.d("startingtime", "onDataChange: "+starthour + " " +startmin);
                Log.d("endingtime", "onDataChange: "+endhour + " " +endmin);
                String time = "Free: " + starthour + ":" + startmin + " to " + endhour + ":" + endmin;
                timeIntervals.add(time);
                //Toast.makeText(DayAppointmentsActivity.this, time, Toast.LENGTH_SHORT).show();
                startmin = 0;
                endmin = 0;
            }
            for (int j = 0; j < apptindex.size(); j++) {
                int index = apptindex.get(j);
                long apptnum = apptnumber.get(j);
                String appnumString = Long.toString(apptnum);
                if (daySchedule != null) {
                    mApptDatabase = FirebaseDatabase.getInstance().getReference().child("Appointments").child(appnumString);
                    mApptDatabase.addValueEventListener(new ValueEventListener() {
                        @Override
                        public void onDataChange(DataSnapshot dataSnapshot) {
                            String name = dataSnapshot.child("targetName").getValue().toString();
                            Toast.makeText(DayAppointmentsActivity.this, name, Toast.LENGTH_SHORT).show();
                            timeIntervals.add(name);
                        }

                        @Override
                        public void onCancelled(DatabaseError databaseError) {

                        }
                    });
                }

            }
            Log.d("timeintervals", "onDataChange: "+timeIntervals.toString());
            listAdapter = new ArrayAdapter<String>(DayAppointmentsActivity.this,android.R.layout.simple_list_item_1,timeIntervals);
            listView.setAdapter(listAdapter);
        }

1 Answers1

0

Welcome to asynchronous data loading, which always messages you up. Contrary to what you think, the data is getting added to timeIntervals. It's just being added after you print it.

The easiest way to see this is by adding some well placed log statements:

Log.d("timeintervals", "Before starting load additional data");
for (int j = 0; j < apptindex.size(); j++) {
    int index = apptindex.get(j);
    long apptnum = apptnumber.get(j);
    String appnumString = Long.toString(apptnum);
    if (daySchedule != null) {
        mApptDatabase = FirebaseDatabase.getInstance().getReference().child("Appointments").child(appnumString);
        mApptDatabase.addValueEventListener(new ValueEventListener() {
            @Override
            public void onDataChange(DataSnapshot dataSnapshot) {
                Log.d("timeintervals": "Additional data loaded");
                String name = dataSnapshot.child("targetName").getValue().toString();
                timeIntervals.add(name);
            }

            @Override
            public void onCancelled(DatabaseError databaseError) {
                throw databaseError.toException(); // don't ignore errors
            }
        });
    }

}
Log.d("timeintervals", "After starting load additional data");

When you run this code the output is:

Before starting load additional data

After starting load additional data

Additional data loaded

Additional data loaded

...

This is probably not the order you expected. But it explains perfectly why timeIntervals doesn't show the additional information when you print it: the data hasn't been loaded yet.

This is because Firebase loads data from the database asynchronously and doesn't block your app while it's loading the data.

The typical solution for dealing with asynchronous data is to move the code that needs the data into the onDataChange that fires when the data has loaded. E.g. if you move the logging of the timeIntervals into onDataChange it will log the intervals each time it loads one of the names.

To learn a lot more about this, I recommend reading these:

Community
  • 1
  • 1
Frank van Puffelen
  • 565,676
  • 79
  • 828
  • 807
  • Yes this is an unfortunate situation. The problem I have is I need to access 2 database references and get data from each to put in a single arraylist to be used in listadapter. – Avinash Prabhakar Mar 10 '18 at 17:41
  • So I am unable to put all the things I need to do in one onDataChange. – Avinash Prabhakar Mar 10 '18 at 17:42
  • Read some of the answers to questions from developers who were previously in this situation. The solution is almost always to reframe the problem from: "first I get all data into the array, then I do something with the array" to "I start loading the data. Whenever I get some data, I update the array and update the UI". – Frank van Puffelen Mar 10 '18 at 17:47
  • https://stackoverflow.com/questions/33203379/setting-singleton-property-value-in-firebase-listener. So I tried the blue pill solution and it seems to work. I do however have to go back to the previous activity and then go back to this activity to make it work. – Avinash Prabhakar Mar 10 '18 at 18:05
  • For your blue pill solution, do you know a way in which I do not have to go back and forth between the activities? Would it work if I simply refresh the List? – Avinash Prabhakar Mar 10 '18 at 18:08
  • To make it not hang, I used semaphore.acquire(); – Avinash Prabhakar Mar 10 '18 at 18:13
  • When I tried that last time on a physical Android device, it made the main/UI thread hang leading an an Application Not Responding. I'd highly recommend not trying to work around asynchronicity, but instead to learn how to work with it and use it to your advantage. – Frank van Puffelen Mar 10 '18 at 18:47