2

I have a method which should initialize my field variable currentTask. I don't understand why I can't read my object from firebase. Here is the method:

private void getCurrentTask() {
    final DatabaseReference dRef1 = database.getReference().child("Users").child(uid).child("CurrentTask");
    dRef1.addListenerForSingleValueEvent(new ValueEventListener() {
        @Override
        public void onDataChange(DataSnapshot dataSnapshot) {
            if (dataSnapshot.exists()) {
                currentTask = dataSnapshot.getValue(CurrentTask.class);
            } else {
                    Toast toast = Toast.makeText(TasksListActivity.this, "no magic", Toast.LENGTH_LONG);
                    toast.show();
            }
        }

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

    if (currentTask == null) {
        Toast toast = Toast.makeText(this, "magic)", Toast.LENGTH_LONG);
        toast.show();
    }
}

Toast no magic never appears, so the object exists. But, in the end of the method toast magic appears, which means currentTask == null. Even after initialization!

And here is my database:

enter image description here

Reaz Murshed
  • 23,691
  • 13
  • 78
  • 98
Maxgmer
  • 456
  • 8
  • 19
  • Where you get uid? Also, instead of toasting everywhere, try to learn how to debug your app - you can set breakpoint where program will stop and inspect what's the result from firebase. – JoKr Jan 06 '17 at 11:25
  • @Gudin i get uid in OnCreate() of my activity. Only after that i call this method. – Maxgmer Jan 06 '17 at 11:30
  • 2
    @Maxgmer, `currentTask` will always be `null` because `onDataChange` is asynchronous. – JP Ventura Jan 06 '17 at 11:32
  • @JPVentura oh, that means that i should include all my code that uses `currentTask` into `onDataChange`? Or are there any other ways to cope with this issue? – Maxgmer Jan 06 '17 at 11:36
  • Have you set correctly the authentication step, to access your database? – David Kasabji Jan 06 '17 at 11:54
  • @Maxgmer, correct. You should include all code that uses `currentTask` into `onDataChange`. – JP Ventura Jan 06 '17 at 13:55

3 Answers3

4

@Roasario and @ReazMurshed 's answer is both right. But let me make it more simple by describing how your code doesn't work as you expected:

private void getCurrentTask() {
    ...
    dRef1.addListenerForSingleValueEvent(new ValueEventListener() {
        @Override
        public void onDataChange(DataSnapshot dataSnapshot) {
            ... (point 1)
        }

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

    ... (point 2)
}

Firebase Database load your data asyncronously. It means (to make it simple) that the process of getting data does not interfere with your main process. With that in mind, the code in (point 2) does not always get executed after code in (point 1), and vice versa. Though usually code in (point 1) get executed after (point 2), it is not always like that. So you should consider that the code you write in (point 1) can be executed at anytime.

Then you should write your code with that concept. Meaning that if you want to do anything to a variable inside (point 1) (like filling currentTask with dataSnapshot value then check if it is null), you should place it all inside (point 1)

Hope this help.

Community
  • 1
  • 1
koceeng
  • 2,169
  • 3
  • 16
  • 37
  • 1
    _Point 2_ is **always** executed after because is defined immediately after calling `addListenerForSingleValueEvent`. The callback `onDataChange` is asynchronous and will take at least 4 milliseconds to run. – JP Ventura Jan 06 '17 at 13:59
  • I think so too, but I'm not really sure. Thanks a lot for the info :) – koceeng Jan 06 '17 at 19:01
2

Note that onDataChange is asynchronous, so your if statement will always return false because you're checking if it is null while the data hasn't been read before. You should check if it is null inside of onDataChange (to assure the data has been read) like this:

private void getCurrentTask() {
    final DatabaseReference dRef1 = database.getReference().child("Users").child(uid).child("CurrentTask");
    dRef1.addListenerForSingleValueEvent(new ValueEventListener() {
        @Override
        public void onDataChange(DataSnapshot dataSnapshot) {
            if (dataSnapshot.exists()) {
                currentTask = dataSnapshot.getValue(CurrentTask.class);

    if (currentTask == null) {
        Toast toast = Toast.makeText(this, "magic)", Toast.LENGTH_LONG);
        toast.show();
    }
            } else {
                    Toast toast = Toast.makeText(TasksListActivity.this, "no magic", Toast.LENGTH_LONG);
                    toast.show();
            }
        }

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

Modify your reference url a bit like this.

final DatabaseReference dRef1 = database.getReference().child("Users").child(uid);

Now create a class to represent each of your nodes.

public class User {
    public UserCharacter Character;
    public UserCurrentTask CurrentTask;    
    public String Email;
    public UserTasks Tasks;
}

Now inside your onDataChange you need to do this.

@Override
public void onDataChange(DataSnapshot dataSnapshot) {
    if (dataSnapshot.exists()) {
        User mUser = dataSnapshot.getValue(User.class);
        currentTask = mUser.CurrentTask;
    } else {
        // Show toast or something.
    }
}

And as @Roasario stated, the onDataChanged function is Async. So you can't get the actual value while you check for null value.

Reaz Murshed
  • 23,691
  • 13
  • 78
  • 98
  • I don't think this is a good approach because he would be downloading the whole User node when he only needs the currentTask. – Rosário Pereira Fernandes Jan 06 '17 at 12:18
  • you mean i should load everything and work with the data i got inside `onDataChange`? That toasts are for you just to ease the explanation of the issue. – Maxgmer Jan 06 '17 at 12:29
  • It actually loads the data of a single node. So it won't do any harm I guess unless the `CurrentTask`, `Tasks`, `Character` are huge objects. If they are large enough to be worried about then you need to rethink about your data structure I guess. – Reaz Murshed Jan 06 '17 at 12:55