Fused Location Provider (Java)
RecipeBox's next feature is a "Nearby Grocery Stores" screen — given the user's current position, show stores close by that likely carry the ingredients for a recipe. Getting that position reliably is the job of the Fused Location Provider, part of Google Play Services. Rather than reading raw GPS, WiFi, or cell-tower signals yourself, the Fused Location Provider combines all of them intelligently and hands back the best estimate it can produce for the current conditions — which is why Google recommends it over the older platform LocationManager APIs for almost every use case.
Adding the Dependency
// app/build.gradle
dependencies {
implementation 'com.google.android.gms:play-services-location:21.3.0'
}
Requesting the Location Permission First
Location is a runtime-dangerous permission, so it must be requested the way the Runtime Permissions lesson covers, before any call to the Fused Location Provider is made. Android offers two location permissions:
| Permission | Accuracy | RecipeBox needs it for |
|---|---|---|
| ACCESS_COARSE_LOCATION | Approximate location (city-block level), derived from WiFi/cell. | Enough to find "nearby grocery stores" — RecipeBox doesn't need pinpoint precision for this. |
| ACCESS_FINE_LOCATION | Precise GPS-level location. | Not required for RecipeBox's grocery-store feature; only worth requesting if a future feature needs turn-by-turn accuracy. |
Getting a One-Time Current Location
Since RecipeBox just needs a single snapshot of "where is the user right now," a one-shot read is the right tool — not continuous tracking.
// In NearbyStoresActivity.java (permission already granted)
private FusedLocationProviderClient fusedLocationClient;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_nearby_stores);
fusedLocationClient = LocationServices.getFusedLocationProviderClient(this);
fetchCurrentLocation();
}
private void fetchCurrentLocation() {
if (ContextCompat.checkSelfPermission(this,
Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED) {
return;
}
fusedLocationClient.getCurrentLocation(Priority.PRIORITY_BALANCED_POWER_ACCURACY, null)
.addOnSuccessListener(location -> {
if (location != null) {
double lat = location.getLatitude();
double lng = location.getLongitude();
loadNearbyGroceryStores(lat, lng);
}
});
}
- getCurrentLocation() actively requests a fresh fix, unlike the older getLastLocation() which only returns a cached value that might be stale or null.
- The result arrives asynchronously in an OnSuccessListener — never block the UI thread waiting for it.
Requesting Continuous Location Updates
RecipeBox's grocery-store screen doesn't need continuous tracking — a one-shot location is enough. But it's worth knowing the pattern briefly, for a feature like "recalculate nearby stores as I walk." Continuous updates use a LocationRequest plus a LocationCallback:
private LocationCallback locationCallback;
private LocationRequest locationRequest;
private void startLocationUpdates() {
locationRequest = new LocationRequest.Builder(
Priority.PRIORITY_BALANCED_POWER_ACCURACY, 10000) // every 10 seconds
.build();
locationCallback = new LocationCallback() {
@Override
public void onLocationResult(LocationResult result) {
for (Location location : result.getLocations()) {
loadNearbyGroceryStores(location.getLatitude(), location.getLongitude());
}
}
};
fusedLocationClient.requestLocationUpdates(
locationRequest, locationCallback, Looper.getMainLooper());
}
Removing Updates When No Longer Needed
@Override
protected void onStop() {
super.onStop();
if (locationCallback != null) {
fusedLocationClient.removeLocationUpdates(locationCallback);
}
}
Battery vs Accuracy Tradeoffs
| Priority | Tradeoff |
|---|---|
| PRIORITY_HIGH_ACCURACY | Uses GPS aggressively for the best precision; costs the most battery. |
| PRIORITY_BALANCED_POWER_ACCURACY | City-block accuracy using WiFi/cell; much lighter on battery — the right default for RecipeBox's grocery-store lookup. |