Value of a global variable is reset after it is initialised in ValueEventListener

Solution 1:

There is no magic going on here. You're just seeing the effects of asynchronous loading, on which most of the modern web is built.

It's easiest to understand if you run the code in a debugger or add some logging statements, like this:

FirebaseDatabase database = FirebaseDatabase.getInstance();
DatabaseReference myRef = database.getReference(path);   

System.out.println("Before adding listener");
myRef.addListenerForSingleValueEvent(new ValueEventListener() {
    @Override
    public void onDataChange(DataSnapshot dataSnapshot) {
        System.out.println("In onDataChange");
    }

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

System.out.println("After adding listener");

When you run the code like this, it will print:

Before adding listener

After adding listener

In OnDataChange

This is probably not what you expected, but it is the expected behavior when dealing with Firebase and as said most modern web APIs. Since loading the data may take an unknown amount of time (it has to be gotten from somewhere on the internet after all), the code doesn't wait for the data to be return, but instead simply continues with the line after you attached the listener. Then when the data becomes available, it calls your onDataChange() and you can use the data.

This behavior is incredibly confusing at first, so don't worry if it's not immediately clear. The important thing to realize is that the behavior is normal and part of modern web programming. So don't try to work around it (i.e. making the code wait for the data). Instead: try to reframe your problem into a model that works better with this asynchronous nature.

I found it easiest to reframe my problems from a "first load the data, then show it in a toast" to "start loading the data, then when it's loaded, show it in a toast". This translates to the following code:

FirebaseDatabase database = FirebaseDatabase.getInstance();
DatabaseReference myRef = database.getReference(path);   

myRef.addListenerForSingleValueEvent(new ValueEventListener() {
    @Override
    public void onDataChange(DataSnapshot dataSnapshot) {
        bg=dataSnapshot.getValue().toString();
        Toast.makeText(MapsActivity.this, bg, Toast.LENGTH_SHORT).show();  
    }

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