Skip to main content

Command Palette

Search for a command to run...

Android Location Guide: GPS, Wi-Fi, Cell Towers and the Fused Location Provider API

Updated
6 min read
Android Location Guide: GPS, Wi-Fi, Cell Towers and the Fused Location Provider API

Every Android app that uses location is making a decision it might not even realize it's making: which location provider to use. GPS, network-based location, Wi-Fi, passive — each has different accuracy characteristics, different power costs, and different availability depending on the environment. Most developers reach for FusedLocationProviderClient and move on, but understanding what's happening underneath makes your implementation choices more deliberate and your debugging faster.


How Android Determines Location

Android can derive a device's position from several sources simultaneously:

GPS (Global Positioning System) — communicates with satellites. High accuracy (within a few meters in open conditions), but slow to acquire a fix, doesn't work well indoors, and draws significant battery power.

Wi-Fi positioning — estimates location based on the signal strengths of nearby Wi-Fi networks, matched against a database of known networks and their physical locations. Fast, works indoors, moderate accuracy (tens of meters in cities), lower power consumption than GPS.

Cell tower triangulation — uses signal strengths from nearby cell towers. Fast acquisition, works everywhere with coverage, but accuracy is coarse — anywhere from a few hundred meters to several kilometers depending on tower density.

Sensors — accelerometers, gyroscopes, and barometers can assist location tracking by filling in gaps between GPS fixes (dead reckoning). Not a standalone source, but used by the system to smooth out position updates.

In practice, Android uses all of these together and blends the results to produce the best estimate it can.


The Traditional Approach: LocationManager

LocationManager is the lower-level API that's been in Android since API 1. It gives you direct access to each location provider independently.

GPS provider — most accurate, indoors unreliable:

LocationManager locationManager = (LocationManager) getSystemService(Context.LOCATION_SERVICE);

if (ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION)
    == PackageManager.PERMISSION_GRANTED) {
    locationManager.requestLocationUpdates(
        LocationManager.GPS_PROVIDER,
        5000,   // minimum time between updates (ms)
        10,     // minimum distance between updates (meters)
        locationListener
    );
}

Network provider — Wi-Fi and cell towers:

locationManager.requestLocationUpdates(
    LocationManager.NETWORK_PROVIDER,
    5000,
    10,
    locationListener
);

Passive provider — piggybacks on other apps' location requests:

locationManager.requestLocationUpdates(
    LocationManager.PASSIVE_PROVIDER,
    0,
    0,
    locationListener
);

The passive provider doesn't trigger any location requests on its own. It receives updates when other apps or the system request them. This is useful for background scenarios where you want location updates without contributing to battery drain yourself.

Listening for updates:

LocationListener locationListener = new LocationListener() {
    @Override
    public void onLocationChanged(Location location) {
        double latitude = location.getLatitude();
        double longitude = location.getLongitude();
        float accuracy = location.getAccuracy(); // meters
    }

    @Override
    public void onProviderEnabled(String provider) {}

    @Override
    public void onProviderDisabled(String provider) {}
};

LocationManager gives you granular control, but managing multiple providers simultaneously, handling their different characteristics, and balancing accuracy against battery life is work you have to do yourself.


The Modern Approach: Fused Location Provider

FusedLocationProviderClient is Google's abstraction over the raw providers. It fuses GPS, Wi-Fi, and cellular data internally and gives you a single, battery-efficient stream of location updates. You describe what you need — accuracy level, update frequency — and the system figures out which providers to use and when.

Setup:

FusedLocationProviderClient fusedLocationClient =
    LocationServices.getFusedLocationProviderClient(this);

Getting the last known location:

if (ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION)
    == PackageManager.PERMISSION_GRANTED) {

    fusedLocationClient.getLastLocation()
        .addOnSuccessListener(this, location -> {
            if (location != null) {
                double lat = location.getLatitude();
                double lon = location.getLongitude();
            }
        });
}

getLastLocation() returns immediately with a cached value — it's fast but can be null if the device hasn't obtained a location recently. For most "show current location" use cases, this is the right starting point.

Requesting continuous updates:

LocationRequest locationRequest = new LocationRequest.Builder(
    Priority.PRIORITY_HIGH_ACCURACY,
    10000  // interval in milliseconds
)
.setMinUpdateIntervalMillis(5000)
.build();

LocationCallback locationCallback = new LocationCallback() {
    @Override
    public void onLocationResult(LocationResult locationResult) {
        if (locationResult == null) return;
        for (Location location : locationResult.getLocations()) {
            // Process each update
        }
    }
};

fusedLocationClient.requestLocationUpdates(
    locationRequest,
    locationCallback,
    Looper.getMainLooper()
);

Priority levels:

  • PRIORITY_HIGH_ACCURACY — uses GPS when available, highest accuracy, highest power cost
  • PRIORITY_BALANCED_POWER_ACCURACY — uses Wi-Fi and cell, roughly city-block accuracy, lower power
  • PRIORITY_LOW_POWER — city-level accuracy, minimal power
  • PRIORITY_PASSIVE — no active requests, receives updates from other apps

Always stop updates when you don't need them:

@Override
protected void onStop() {
    super.onStop();
    fusedLocationClient.removeLocationUpdates(locationCallback);
}

Failing to remove updates is one of the most common causes of unexpected battery drain in location-aware apps.


Permissions

Location requires explicit runtime permissions. There are two relevant permission levels:

  • ACCESS_COARSE_LOCATION — city-level accuracy, uses Wi-Fi and cell
  • ACCESS_FINE_LOCATION — precise accuracy, uses GPS

Declare them in AndroidManifest.xml:

<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />

And request them at runtime before making any location calls. Using the Activity Result API (recommended):

val locationPermissionRequest = registerForActivityResult(
    ActivityResultContracts.RequestMultiplePermissions()
) { permissions ->
    when {
        permissions[Manifest.permission.ACCESS_FINE_LOCATION] == true -> {
            // Fine location granted
        }
        permissions[Manifest.permission.ACCESS_COARSE_LOCATION] == true -> {
            // Coarse location only
        }
        else -> {
            // No location permission
        }
    }
}

locationPermissionRequest.launch(
    arrayOf(
        Manifest.permission.ACCESS_FINE_LOCATION,
        Manifest.permission.ACCESS_COARSE_LOCATION
    )
)

For background location (tracking while the app is not in the foreground), you also need ACCESS_BACKGROUND_LOCATION, which requires additional justification in the Play Store review process.


Choosing Between LocationManager and FusedLocationProviderClient

For most apps, FusedLocationProviderClient is the right choice. It handles provider selection automatically, optimizes battery usage, and produces more consistent results than managing raw providers yourself.

LocationManager makes sense when:

  • Your app runs on devices without Google Play Services (some older or specialized Android devices)
  • You need direct, unfiltered GPS output for precision-critical applications
  • You're building a tool that needs to reason about individual providers separately

Reading the Location Object

Regardless of which API you use, location updates arrive as Location objects. The most commonly used properties:

location.getLatitude()      // degrees, -90 to +90
location.getLongitude()     // degrees, -180 to +180
location.getAccuracy()      // estimated horizontal accuracy in meters
location.getAltitude()      // meters above WGS 84 ellipsoid (if available)
location.getSpeed()         // meters per second (if available)
location.getBearing()       // direction of travel in degrees (if available)
location.getTime()          // UTC timestamp in milliseconds
location.getProvider()      // which provider produced this update

getAccuracy() is particularly useful — it tells you the radius of the circle (in meters) within which the device's actual location falls with 68% confidence. A location with 5-meter accuracy is a confident GPS fix. A location with 1500-meter accuracy is a rough cell-tower estimate. Use the accuracy value to decide whether a given update is precise enough for your use case.


Summary

Android location works by blending multiple data sources — GPS, Wi-Fi, cell towers, and sensors. LocationManager gives you direct access to individual providers. FusedLocationProviderClient abstracts all of that into a single battery-efficient API where you describe the accuracy level you need. For most applications, start with FusedLocationProviderClient using PRIORITY_BALANCED_POWER_ACCURACY, switch to PRIORITY_HIGH_ACCURACY only when precision matters, and always remove update callbacks when the app is not actively displaying or using location data.

More from this blog

A

Anmol's blog

17 posts