Ibrahim Baz Ibrahim Baz - 17 days ago 11
Android Question

Using Firebase to Determine User's Location And Fixed more Exception

I have used this method to get the user location depending on Firebase, but I get an Exception.

I tried to fix it, but I didn't know exactly how.

Here is my code:

public class MapsActivity extends FragmentActivity implements GeoQueryEventListener, GoogleMap.OnCameraChangeListener,OnMapReadyCallback {
private static final GeoLocation INITIAL_CENTER = new GeoLocation(37.7789, -122.4017);
private static final int INITIAL_ZOOM_LEVEL = 14;
private static final String GEO_FIRE_DB = "https://publicdata-transit.firebaseio.com";
private static final String GEO_FIRE_REF = GEO_FIRE_DB + "/_geofire";

private GoogleMap map;
private Circle searchCircle;
private GeoFire geoFire;
private GeoQuery geoQuery;

private Map<String,Marker> markers;

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_maps);

// setup map and camera position
SupportMapFragment mapFragment = (SupportMapFragment) getSupportFragmentManager()
.findFragmentById(R.id.map);
mapFragment.getMapAsync(this);
LatLng latLngCenter = new LatLng(INITIAL_CENTER.latitude, INITIAL_CENTER.longitude);
this.searchCircle = this.map.addCircle(new CircleOptions().center(latLngCenter).radius(1000));
this.searchCircle.setFillColor(Color.argb(66, 255, 0, 255));
this.searchCircle.setStrokeColor(Color.argb(66, 0, 0, 0));
this.map.moveCamera(CameraUpdateFactory.newLatLngZoom(latLngCenter, INITIAL_ZOOM_LEVEL));
this.map.setOnCameraChangeListener(this);

FirebaseOptions options = new FirebaseOptions.Builder().setApplicationId("geofire").setDatabaseUrl(GEO_FIRE_DB).build();
FirebaseApp app = FirebaseApp.initializeApp(this, options);

// setup GeoFire
this.geoFire = new GeoFire(FirebaseDatabase.getInstance(app).getReferenceFromUrl(GEO_FIRE_REF));
// radius in km
this.geoQuery = this.geoFire.queryAtLocation(INITIAL_CENTER, 1);

// setup markers
this.markers = new HashMap<String, Marker>();
}

@Override
protected void onStop() {
super.onStop();
// remove all event listeners to stop updating in the background
this.geoQuery.removeAllListeners();
for (Marker marker: this.markers.values()) {
marker.remove();
}
this.markers.clear();
}

@Override
protected void onStart() {
super.onStart();
// add an event listener to start updating locations again
this.geoQuery.addGeoQueryEventListener(this);
}

@Override
public void onKeyEntered(String key, GeoLocation location) {
// Add a new marker to the map
Marker marker = this.map.addMarker(new MarkerOptions().position(new LatLng(location.latitude, location.longitude)));
this.markers.put(key, marker);
}

@Override
public void onKeyExited(String key) {
// Remove any old marker
Marker marker = this.markers.get(key);
if (marker != null) {
marker.remove();
this.markers.remove(key);
}
}

@Override
public void onKeyMoved(String key, GeoLocation location) {
// Move the marker
Marker marker = this.markers.get(key);
if (marker != null) {
this.animateMarkerTo(marker, location.latitude, location.longitude);
}
}

@Override
public void onGeoQueryReady() {
}

@Override
public void onGeoQueryError(DatabaseError error) {
new AlertDialog.Builder(this)
.setTitle("Error")
.setMessage("There was an unexpected error querying GeoFire: " + error.getMessage())
.setPositiveButton(android.R.string.ok, null)
.setIcon(android.R.drawable.ic_dialog_alert)
.show();
}

// Animation handler for old APIs without animation support
private void animateMarkerTo(final Marker marker, final double lat, final double lng) {
final Handler handler = new Handler();
final long start = SystemClock.uptimeMillis();
final long DURATION_MS = 3000;
final Interpolator interpolator = new AccelerateDecelerateInterpolator();
final LatLng startPosition = marker.getPosition();
handler.post(new Runnable() {
@Override
public void run() {
float elapsed = SystemClock.uptimeMillis() - start;
float t = elapsed/DURATION_MS;
float v = interpolator.getInterpolation(t);

double currentLat = (lat - startPosition.latitude) * v + startPosition.latitude;
double currentLng = (lng - startPosition.longitude) * v + startPosition.longitude;
marker.setPosition(new LatLng(currentLat, currentLng));

// if animation is not finished yet, repeat
if (t < 1) {
handler.postDelayed(this, 16);
}
}
});
}

private double zoomLevelToRadius(double zoomLevel) {
// Approximation to fit circle into view
return 16384000/Math.pow(2, zoomLevel);
}

@Override
public void onCameraChange(CameraPosition cameraPosition) {
// Update the search criteria for this geoQuery and the circle on the map
LatLng center = cameraPosition.target;
double radius = zoomLevelToRadius(cameraPosition.zoom);
this.searchCircle.setCenter(center);
this.searchCircle.setRadius(radius);
this.geoQuery.setCenter(new GeoLocation(center.latitude, center.longitude));
// radius in km
this.geoQuery.setRadius(radius/1000);
}

@Override
public void onMapReady(GoogleMap googleMap) {

}
}


And the logcat looks like this:


java.lang.RuntimeException: Unable to start activity ComponentInfo{l.t.g.i.iserve/l.t.g.i.iserve.Activity.MapsActivity}: java.lang.NullPointerException: Attempt to invoke virtual method 'com.google.android.gms.maps.model.Circle com.google.android.gms.maps.GoogleMap.addCircle(com.google.android.gms.maps.model.CircleOptions)' on a null object reference


After Changes..

onCreat Look Like :

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_maps);

// setup map and camera position
SupportMapFragment mapFragment = (SupportMapFragment) getSupportFragmentManager()
.findFragmentById(R.id.map);
mapFragment.getMapAsync(this);


onMapReady(map);
}


and onMapReady Look Like :

@Override
public void onMapReady(GoogleMap googleMap) {

this.map = googleMap;
LatLng latLngCenter = new LatLng(INITIAL_CENTER.latitude, INITIAL_CENTER.longitude);
this.searchCircle = this.map.addCircle(new CircleOptions().center(latLngCenter).radius(1000));
this.searchCircle.setFillColor(Color.argb(66, 255, 0, 255));
this.searchCircle.setStrokeColor(Color.argb(66, 0, 0, 0));
this.map.moveCamera(CameraUpdateFactory.newLatLngZoom(latLngCenter, INITIAL_ZOOM_LEVEL));
this.map.setOnCameraChangeListener(this);

FirebaseOptions options = new FirebaseOptions.Builder().setApplicationId("geofire").setDatabaseUrl(GEO_FIRE_DB).build();
FirebaseApp app = FirebaseApp.initializeApp(this, options);

// setup GeoFire
this.geoFire = new GeoFire(FirebaseDatabase.getInstance(app).getReferenceFromUrl(GEO_FIRE_REF));
// radius in km
this.geoQuery = this.geoFire.queryAtLocation(INITIAL_CENTER, 1);

// setup markers
this.markers = new HashMap<String, Marker>();
}


And Log Cat Look Like :


Caused by: java.lang.NullPointerException: Attempt to invoke virtual
method
'com.google.android.gms.maps.model.Circle
com.google.android.gms.maps.GoogleMap.addCircle(com.google.android.gms.maps.model.CircleOptions)'
on a null object reference
at l.t.g.i.iserve.Activity.MapsActivity.onMapReady(MapsActivity.java:165)
at l.t.g.i.iserve.Activity.MapsActivity.onCreate(MapsActivity.java:54)

Answer

this.map.addCircle is the error. The Map is null.

The method of onMapReady is empty, and that method (and after) is where you need to move all code that relies on the instance of the Map object

You need to first assign this.map = googleMap in that method.

EDIT

Do not call onMapReady(map) yourself! mapFragment.getMapAsync(this); will do that for you.

And this section can stay in onCreate since it does not need the map.

FirebaseOptions options = new FirebaseOptions.Builder().setApplicationId("geofire").setDatabaseUrl(GEO_FIRE_DB).build();
FirebaseApp app = FirebaseApp.initializeApp(this, options);

// setup GeoFire
this.geoFire = new GeoFire(FirebaseDatabase.getInstance(app).getReferenceFromUrl(GEO_FIRE_REF));
// radius in km
this.geoQuery = this.geoFire.queryAtLocation(INITIAL_CENTER, 1);

// setup markers
this.markers = new HashMap<String, Marker>();
Comments