3

I'm currently developing an android app where I'm using firebase as database but when in got the variable in the onDataChange method and I assign them to a global variables I got null variables but when I call those variables in the onDataChange method they are not null.

public class PositionateMarkerTask extends AsyncTask {
    public ArrayList<Location> arrayList= new ArrayList<>();
    public void connect() {
        //setting connexion parameter
        final Firebase ref = new Firebase("https://test.firebaseio.com/test");
        Query query = ref.orderByChild("longitude");

        //get the data from the DB
        query.addListenerForSingleValueEvent(new ValueEventListener() {

            @Override
            public void onDataChange(DataSnapshot dataSnapshot) {
                //checking if the user exist
                if(dataSnapshot.exists()){
                    for (DataSnapshot userSnapshot : dataSnapshot.getChildren()) {
                        //get each user which has the target username
                        Location location =userSnapshot.getValue(Location.class);
                            arrayList.add(location);
                        //if the password is true , the data will be storaged in the sharedPreferences file and a Home activity will be launched
                    }
                }
                else{
                    System.out.println("not found");
                }
            }

            @Override
            public void onCancelled(FirebaseError firebaseError) {
                  System.out.println("problem ");

            }
        });
    }

    @Override
    protected Object doInBackground(Object[] params) {
        connect();
        return null;
    }

    @Override
    protected void onPostExecute(Object o) {
        super.onPostExecute(o);

        System.out.println("the firs long is"+arrayList.get(0).getLongitude());

    }
}
Devid Farinelli
  • 7,514
  • 9
  • 42
  • 73
fbm fatma
  • 430
  • 6
  • 22
  • Where is your global variable – Murat Karagöz Jul 19 '16 at 11:03
  • arrayList is my global variable – fbm fatma Jul 19 '16 at 11:06
  • Yeah... `onPostExecute` does not wait until you `arrayList` has items. That's why its empty. – Murat Karagöz Jul 19 '16 at 11:07
  • normally onpostexecute is executed after the onbackground method is executed so it must wait for it – fbm fatma Jul 19 '16 at 11:09
  • no, from your code, it doesn't wait for the ValueEventListener method. the onPostExecute is called directly after it finished attaching the ValueEventListener. the firebase database also works asynchronously. – Wilik Jul 19 '16 at 11:27
  • so how can i use the data of firebase out of ondatachangemethod – fbm fatma Jul 19 '16 at 11:31
  • you don't need the `asynctask`. just do the `connect` in your activity/fragment/service and then do the operation inside `onDataChange` or call a method. – Wilik Jul 19 '16 at 11:38
  • but it is not recommanded to make the db connection in the activity class – fbm fatma Jul 19 '16 at 11:44
  • I think that only applies to sqlite db access, you should not access it from UI thread because that might block the UI process, so you need to create a separate thread (one way is using `asynctask`). But you don't need that when retrieving data in firebase, the `ValueEventListener` already works like `asynctask`. – Wilik Jul 19 '16 at 12:01
  • mmmm ok thx for the help but i'd rather lake an asynchtask – fbm fatma Jul 19 '16 at 12:08
  • ok no problem. then you have to find a way to wait for the `onDataChange` before calling the return statement in the `doInBackground` – Wilik Jul 19 '16 at 12:38
  • ok i will look at that – fbm fatma Jul 19 '16 at 13:16

1 Answers1

9

Welcome to asynchronous programming, which messes up everything you always thought was true. :-)

Firebase retrieves/synchronizes with the database automatically in the background. The work happens on a separate thread, so you don't need and AsyncTask. But unfortunately this also means you can't wait for the data.

I typically recommend that you reframe your code from "first do A, then to B" to "whenever we get A, we do B with it".

In your case, you want to get the data and then print the longitude of the first item. Reframed that is: whenever you receive data, print the longitude of the first item.

Query query = ref.orderByChild("longitude");

query.addListenerForSingleValueEvent(new ValueEventListener() {
    @Override
    public void onDataChange(DataSnapshot dataSnapshot) {
        if(dataSnapshot.exists()){
            for (DataSnapshot userSnapshot : dataSnapshot.getChildren()) {
                Location location =userSnapshot.getValue(Location.class);
                arrayList.add(location);
            }
            System.out.println("the first long is"+arrayList.get(0).getLongitude());                }
        else{
            System.out.println("not found");
        }
    }

    @Override
    public void onCancelled(FirebaseError firebaseError) {
          System.out.println("problem ");
    }
});

A few things to note here:

  1. if you're only interested in the first item, you can limit the query to one item: query = ref.orderByChild("longitude").limitToFirst(1). This will retrieve less data.

  2. I recommend using addValueEventListener() instead of addListenerForSingleValueEvent(). The former will keep synchronizing the data. This means that if you insert/change the longitude of an item in the list, your code will automatically get retriggered and print the (potentially) new first item.

Frank van Puffelen
  • 565,676
  • 79
  • 828
  • 807
  • my problem is that i can't call data out of ondatachange method if i do this.arraylist.add.. i got an error it like the ondatachange dosen't know the asynctask it ' s attribute – fbm fatma Jul 19 '16 at 14:50
  • 1
    Use `PositionateMarkerTask.this.arraylist`. Better yet: get rid of the async task and it'll be something like `MyActivity.this.arraylist` – Frank van Puffelen Jul 19 '16 at 15:39