2

I have created a geofire firebase location class. I am able to read subscriber keys from the location table. But, now I want to query subscriber table to get complete subscriber object.

Inside method onGeoQueryReady, I have all subscriber keys. But when I make a get call to subscriber table inside onGeoQueryReady method, it returns nothing.

 public void findNearBySubscribers(final LocationInterface locationInterface, double radius, double clatitude, double clongitude){
        DatabaseReference ref = FirebaseDatabase.getInstance().getReference("location/");
        GeoFire geoFire = new GeoFire(ref);
        final ArrayList<String> keys = new ArrayList<String>();
        GeoQuery geoQuery = geoFire.queryAtLocation(new GeoLocation(clatitude, clongitude), radius);
        geoQuery.addGeoQueryEventListener(new GeoQueryEventListener() {
            @Override
            public void onKeyEntered(String key, GeoLocation location) {
                keys.add(key);
            }

            @Override
            public void onKeyExited(String key) {
                System.out.println(String.format("Key %s is no longer in the search area", key));
            }

            @Override
            public void onKeyMoved(String key, GeoLocation location) {

            }

            @Override
            public void onGeoQueryReady() {

                locationInterface.getNearBySubscribersCallback(keys);
            }

            @Override
            public void onGeoQueryError(DatabaseError error) {
                System.err.println("There was an error with this query: " + error);
            }
        });
    }

Below code to find subscriber based on keys is not working. Subscriber list size is zero , but response string has value. can someone help me on this. looks like this is a 2 level callback...

 public void findNearBySubscribers(final LocationInterface locationInterface, double radius, double clatitude, double clongitude){
        DatabaseReference ref = FirebaseDatabase.getInstance().getReference("location/");
        GeoFire geoFire = new GeoFire(ref);
        final ArrayList<String> keys = new ArrayList<String>();
        GeoQuery geoQuery = geoFire.queryAtLocation(new GeoLocation(clatitude, clongitude), radius);
        geoQuery.addGeoQueryEventListener(new GeoQueryEventListener() {
            @Override
            public void onKeyEntered(String key, GeoLocation location) {
                keys.add(key);
            }

            @Override
            public void onKeyExited(String key) {
                System.out.println(String.format("Key %s is no longer in the search area", key));
            }

            @Override
            public void onKeyMoved(String key, GeoLocation location) {

            }

            @Override
            public void onGeoQueryReady() {
                final ArrayList<Subscriber> subscriberList = new ArrayList<Subscriber>();

                for(String key: keys) {
                    String url = "https://moe-90cc7.firebaseio.com/subscriber/";
                    String uri = url + key + ".json";
                    RestClient.get(uri, null, new TextHttpResponseHandler() {
                        @Override
                        public void onFailure(int statusCode, Header[] headers, String responseString, Throwable throwable) {
                        }

                        @Override
                        public void onSuccess(int statusCode, Header[] headers, String responseString) {
                            Gson gson = new Gson();
                            Subscriber subscriber = gson.fromJson(responseString, Subscriber.class);
                            System.out.println("Response Str" + responseString);
                             subscriberList.add(subscriber);
                        }
                    });
                }
                System.out.println("List size "+subscriberList.size());
                locationInterface.getNearBySubscribersCallback(keys);
            }

            @Override
            public void onGeoQueryError(DatabaseError error) {
                System.err.println("There was an error with this query: " + error);
            }
        });
    }

TRIED THE RED PILL WAY - BUT ITS NOT WORKING FOR ME. Basically I am trying to load a subscriber whenever a subscriber key is available.

package app.com.date.services;

import com.firebase.geofire.GeoFire;
import com.firebase.geofire.GeoLocation;
import com.firebase.geofire.GeoQuery;
import com.firebase.geofire.GeoQueryEventListener;
import com.google.firebase.database.DatabaseError;
import com.google.firebase.database.DatabaseReference;
import com.google.firebase.database.FirebaseDatabase;
import com.google.gson.Gson;
import com.loopj.android.http.TextHttpResponseHandler;

import java.util.ArrayList;
import java.util.concurrent.Semaphore;

import app.com.date.subscriber.Subscriber;
import cz.msebera.android.httpclient.Header;

public class LocationService {
    private ArrayList<Subscriber> subscriberList = new ArrayList<Subscriber>();

    public void addGeoLocation(final LocationInterface locationInterface, String subscriberId, double latitude, double longitude) {

        DatabaseReference ref = FirebaseDatabase.getInstance().getReference("location/");
        GeoFire geoFire = new GeoFire(ref);
        geoFire.setLocation(subscriberId, new GeoLocation(latitude, longitude), new GeoFire.CompletionListener() {
            @Override
            public void onComplete(String key, DatabaseError error) {
                if (key != null) {
                    locationInterface.addLocationCallback(true);
                } else {
                    locationInterface.addLocationCallback(false);
                }
            }
        });
    }

    public void findNearBySubscribers(final LocationInterface locationInterface, double radius, double clatitude, double clongitude) {
        DatabaseReference ref = FirebaseDatabase.getInstance().getReference("location/");
        GeoFire geoFire = new GeoFire(ref);
        final ArrayList<String> keys = new ArrayList<String>();
        final ArrayList<Subscriber> subscriberList = new ArrayList<Subscriber>();
        GeoQuery geoQuery = geoFire.queryAtLocation(new GeoLocation(clatitude, clongitude), radius);
        geoQuery.addGeoQueryEventListener(new GeoQueryEventListener() {
            @Override
            public void onKeyEntered(String key, GeoLocation location) {
                String url = "https://moe-90cc7.firebaseio.com/subscriber/";
                String uri = url + key + ".json";
                RestClient.get(uri, null, new TextHttpResponseHandler() {
                    @Override
                    public void onFailure(int statusCode, Header[] headers, String responseString, Throwable throwable) {
                    }
                    @Override
                    public void onSuccess(int statusCode, Header[] headers, String responseString) {
                        Gson gson = new Gson();
                        Subscriber subscriber = gson.fromJson(responseString, Subscriber.class);
                        System.out.println("Response Str" + responseString);
                        subscriberList.add(subscriber);
                    }
                });

                System.out.println("onKeyEntered list "+subscriberList.size());

            }

            @Override
            public void onKeyExited(String key) {
                System.out.println(String.format("Key %s is no longer in the search area", key));
            }

            @Override
            public void onKeyMoved(String key, GeoLocation location) {

            }

            @Override
            public void onGeoQueryReady() {
                System.out.println("onGeoQueryReady list "+subscriberList.size());
            }

            @Override
            public void onGeoQueryError(DatabaseError error) {
                System.err.println("There was an error with this query: " + error);
            }
        });
    }
}

OUTPUT

12-31 17:57:34.090 13956-14019/app.com.date V/FA: Processing queued up service tasks: 3
12-31 17:57:34.366 13956-13956/app.com.date I/System.out: onKeyEntered list 0
12-31 17:57:34.366 13956-13956/app.com.date I/System.out: onKeyEntered list 0
12-31 17:57:34.366 13956-13956/app.com.date I/System.out: onGeoQueryReady list 0
12-31 17:57:34.456 13956-13956/app.com.date V/AsyncHttpRH: Progress 332 from 332 (100%)
12-31 17:57:34.475 13956-13956/app.com.date I/System.out: Response Str{"age":29,"alcohol":false,"animalLover":false,"children":false,"id":"GYlSDXx0Kwh7AerOzFGf8MDmhOg1","isEmpty":false,"mainImage":"https://firebasestorage.googleapis.com/v0/b/moe-erOzFGf8MDmhOg1%2F1265290048?alt=media&token=80ab1410-bac9-4679-a3d4-152204319310","name":"singh","smoker":false}
12-31 17:57:34.574 13956-13956/app.com.date V/AsyncHttpRH: Progress 327 from 327 (100%)
12-31 17:57:34.584 13956-13956/app.com.date I/System.out: Response Str{"age":65,"alcohol":false,"animalLover":false,"children":false,"id":"xZWPMjWQCYY4xNimx3WHqVZcQxs1","isEmpty":false,"mainImage":"https://firebasestorage.googleapQCYY4xNimx3WHqVZcQxs1%2F1405868067?alt=media&token=b8d128a6-7156-42e8-9d0b-953feb128b4c","name":" Singh","smoker":false}
12-31 17:57:39.123 13956-14019/app.com.date V/FA: Inactivity, disconnecting from the service
user7327850
  • 229
  • 5
  • 15

1 Answers1

1

The problem is in this code:

final ArrayList<Subscriber> subscriberList = new ArrayList<Subscriber>();

for(String key: keys) {
    String url = "https://moe-90cc7.firebaseio.com/subscriber/";
    String uri = url + key + ".json";
    RestClient.get(uri, null, new TextHttpResponseHandler() {
        @Override
        public void onFailure(int statusCode, Header[] headers, String responseString, Throwable throwable) {
        }

        @Override
        public void onSuccess(int statusCode, Header[] headers, String responseString) {
            Gson gson = new Gson();
            Subscriber subscriber = gson.fromJson(responseString, Subscriber.class);
            System.out.println("Response Str" + responseString);
             subscriberList.add(subscriber);
        }
    });
}
System.out.println("List size "+subscriberList.size());
locationInterface.getNearBySubscribersCallback(keys);

The data is loaded asynchronously from Firebase. This means that by the time you print the subscriberList.size(), you haven't loaded any subscribers yet.

This is easiest to see if you change the code to output some simple logging statements:

final ArrayList<Subscriber> subscriberList = new ArrayList<Subscriber>();

System.out.println("Before starting to load");
for(String key: keys) {
    String url = "https://moe-90cc7.firebaseio.com/subscriber/";
    String uri = url + key + ".json";
    RestClient.get(uri, null, new TextHttpResponseHandler() {
        @Override
        public void onFailure(int statusCode, Header[] headers, String responseString, Throwable throwable) {
        }

        @Override
        public void onSuccess(int statusCode, Header[] headers, String responseString) {
            Gson gson = new Gson();
            Subscriber subscriber = gson.fromJson(responseString, Subscriber.class);
            System.out.println("Response Str" + responseString);
            subscriberList.add(subscriber);
            System.out.println("In onSuccess, list size "+subscriberList.size());
        }
    });
}
System.out.println("After starting to load, list size "+subscriberList.size());
locationInterface.getNearBySubscribersCallback(keys);

The output of this will be:

Before starting to load

After starting to load, list size 0

In onSuccess, list size 1

In onSuccess, list size 2

...

So you can see the the code in onSuccess() runs after the code lower in the file. Remember: this is because the data is loaded asynchronously and is completely normal behavior when dealing with modern web/cloud APIs.

The solution is to reframe your problem. Instead of saying "first load the subscribers, then do xyz with them", try reframing your problem to "whenever the subscribers have changed, do xyz with them".

The implementation of this is that you move the code that does something to the subscribers into the onSuccess handler, like I've done with printing the length of the subscriber list.

Community
  • 1
  • 1
Frank van Puffelen
  • 565,676
  • 79
  • 828
  • 807
  • Thanks for reply, the problem is I am calling this method from someother activity and I need a subscriber list available in that activity to populate the list view. – user7327850 Dec 31 '16 at 18:44
  • So I can't do anything inside the onsucces method, I want the callback to return list of subscribers. – user7327850 Dec 31 '16 at 18:46
  • You cannot return something that hasn't loaded yet. And you cannot make the method wait on Android (nor should you want to). See http://stackoverflow.com/questions/33203379/setting-singleton-property-value-in-firebase-listener. But if you reframe the problem, you can "start loading the subscriber list, then populate the list view once subscribers are loaded". – Frank van Puffelen Dec 31 '16 at 19:29
  • I have checked the link, but honestly it's hard for me to understand. You can "start loading the subscriber list, then populate the list view once subscribers are loaded. Can you please explain how can I achieve that by calling findNearBySubscribers from other activity. – user7327850 Dec 31 '16 at 22:36
  • I have tried the red pill way and have updated my original post with the code, can you please take a look. Its still not working. I might have done some mistake. kindly review. – user7327850 Dec 31 '16 at 23:01
  • I did it but, I am unhappy because the design is ugly. Now all the firebase calls are inside my activity..Basically in onDataChange method I am notifying the list view adapter with updated list. Do you have any suggestions on how to keep firebase calls outside the activity.? – user7327850 Dec 31 '16 at 23:57
  • Yes, you'd do that by implementing a callback: a single-method interface that you pass into the data-loading method. Very similar to `onDataChange()` itself. – Frank van Puffelen Dec 31 '16 at 23:58