2

I have a global variable named "bg". It gets initialised inside the ValueEventListener of the firebase database reference "myRef"

But if I try to use the value of the variable outside the valueeventlistener block, the variable is empty. It's as if it's being reset.

I am very new to android. Please let me know what's wrong.

The code is as follows:

package com.example.tejasvi.donorfinder;

import android.support.v4.app.FragmentActivity;
import android.os.Bundle;
import android.widget.Toast;

import com.google.android.gms.maps.CameraUpdateFactory;
import com.google.android.gms.maps.GoogleMap;

import com.google.android.gms.maps.OnMapReadyCallback;
import com.google.android.gms.maps.SupportMapFragment;
import com.google.android.gms.maps.model.LatLng;
import com.google.android.gms.maps.model.MarkerOptions;
import com.google.firebase.auth.FirebaseAuth;
import com.google.firebase.database.DataSnapshot;
import com.google.firebase.database.DatabaseError;
import com.google.firebase.database.DatabaseReference;
import com.google.firebase.database.FirebaseDatabase;
import com.google.firebase.database.ValueEventListener;

public class MapsActivity extends FragmentActivity implements OnMapReadyCallback {

    private GoogleMap mMap;
    private FirebaseAuth mAuth;

    public String latlng[],bg;

    @Override
    protected void onCreate(Bundle savedInstanceState) {

         super.onCreate(savedInstanceState);
         setContentView(R.layout.activity_maps);

         mAuth = FirebaseAuth.getInstance();

         Bundle bundle = getIntent().getExtras();

         String s= bundle.getString("Phone");  

         String path="Active Requests/"+mAuth.getCurrentUser().getUid()+"/"+s+"/Blood Group";

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


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

                bg=dataSnapshot.getValue().toString();
                }

            @Override
            public void onCancelled(DatabaseError databaseError) {
                Toast.makeText(MapsActivity.this, "Error", Toast.LENGTH_LONG).show();

            }
        });

        Toast.makeText(MapsActivity.this, bg, Toast.LENGTH_SHORT).show();  //bg is empty here

        SupportMapFragment mapFragment = (SupportMapFragment)    getSupportFragmentManager()
                .findFragmentById(R.id.map);
        mapFragment.getMapAsync(this);
    }

    @Override
    public void onMapReady(GoogleMap googleMap) {
        mMap = googleMap;

        }
}

The value of dataSnapshot.getValue() is "A+ve"

The JSON file is as follows:

{
  "Active Requests": {
    "3R8XGIM5uSRv3sxQZ2sqqUnIjQ62": {
      "9849949799": {
        "Blood Group": "B+ve",
        "Date of Requirement": "3/1/17",
        "Direct or Replacement?": "Direct donation",
        "Donation Type": "Whole Blood",
        "For Self?": "Yes",
        "Hospital Name": "Shekhar Hospital",
        "Location": "12.945793000000002,77.56759339999999",
        "Name": "Poorna Chandra Tejasvi",
        "Units": "2"
      }
    },
    "YL5oVxmua4gktNM2TwwPavL1Tj32": {
      "123456789": {
        "Blood Group": "A+ve",
        "Date of Requirement": "1/1/17",
        "Direct or Replacement?": "Direct donation",
        "Donation Type": "Whole Blood",
        "For Self?": "No",
        "Hospital Name": "Columbia Asia Hospital - Hebbal",
        "Location": "13.0509869,77.5937892",
        "Recipient Name": "jssnxn",
        "Recipient Relation": "Married",
        "Units": "3"
      }
    }
  }
}
Frank van Puffelen
  • 565,676
  • 79
  • 828
  • 807
pct960
  • 41
  • 7
  • looks like the value under `Active Requests///Blood Group` is null, please export your database as JSON and post the content here. – Wilik Dec 31 '16 at 09:52
  • It isn't null. I've tried toasting the value inside the block and it works – pct960 Dec 31 '16 at 09:54
  • can you log the value of `dataSnapshot.toString()` ? – Wilik Dec 31 '16 at 09:55
  • Yes I can. There seems to be a deeper problem here. The variable magically gets reset after the control comes out of the block. – pct960 Dec 31 '16 at 09:58
  • the value of bg is defaulted to null until the the data is actually received. what happens is that you issued a request and tried to access a response that is not yet available. – msdev16 Dec 31 '16 at 10:01
  • 12-31 15:31:22.690 28403-28403/com.example.tejasvi.donorfinder D/A+ve: Value – pct960 Dec 31 '16 at 10:02
  • bg has a value of "A+ve" inside the block – pct960 Dec 31 '16 at 10:04
  • Hmm. But how do I rectify it? Thanks in advance – pct960 Dec 31 '16 at 10:05
  • please put the value of dataSnapshot.toString() and also your database structure in JSON format so it's easier for us to know the actual problem – Wilik Dec 31 '16 at 10:08
  • just do whatever you want with bg value inside the listener .. you're welcom – msdev16 Dec 31 '16 at 10:08

1 Answers1

4

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();
    }
});
Community
  • 1
  • 1
Frank van Puffelen
  • 565,676
  • 79
  • 828
  • 807