Sunday, December 14, 2014

Migrating from LocationClient to FusedLocationProviderApi

Update: The documentation on developer.android.com has been updated.

The Google Play Services library on Android includes the Location APIs including the Fused Location Provider, Activity recognition and Geofencing APIs.

The goal of the Fused Location Provider from the documentation:
  • Simple APIs: Lets you specify high-level needs like "high accuracy" or "low power", instead of having to worry about location providers.
  • Immediately available: Gives your apps immediate access to the best, most recent location.
  • Power-efficiency: Minimizes your app's use of power. Based on all incoming location requests and available sensors, fused location provider chooses the most efficient way to meet those needs.
  • Versatility: Meets a wide range of needs, from foreground uses that need highly accurate location to background uses that need periodic location updates with negligible power impact.
The use of the location APIs is documented in a training tutorial called Making Your App Location Aware. Unfortunately, with recent updates of the Google Play Services library (version 6.5.+ at the moment) the tutorial is now outdated and doesn't work. The tutorials use the LocationClient class. The class was first deprecated then removed in version 6.5. When the class was deprecated, in the documentation, Google recommended to use the FusedLocationProviderApi instead.

LocationClient removed in version 6.5

Receiving Location Updates

The high level approach for receiving location updates is: 
  1. Request Location Permission
  2. Check for Google Play Services
  3. Define Location Services Callbacks
  4. Specify Update Parameters
  5. Start Location Updates
  6. Stop Location Updates
There are no differences between the LocationClient and FusedLocationProviderApi in steps 1, 2 and 4. The difference is in steps 3, 5 and 6.

Using the LocationClient

A striped down version of the code using the LocationClient looks like this:
public class MainActivity extends Activity implements GooglePlayServicesClient.ConnectionCallbacks, GooglePlayServicesClient.OnConnectionFailedListener, LocationListener {
private LocationRequest mLocationRequest;
private LocationClient mLocationClient;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mLocationRequest = LocationRequest.create();
mLocationClient = new LocationClient(this, this, this);
}
@Override
protected void onStart() {
super.onStart();
mLocationClient.connect();
}
@Override
protected void onStop() {
super.onStop();
if (mLocationClient.isConnected()) {
mLocationClient.removeLocationUpdates(this);
}
mLocationClient.disconnect();
}
@Override
public void onConnected(Bundle bundle) {
mLocationClient.requestLocationUpdates(mLocationRequest, this);
}
@Override
public void onDisconnected() {
}
@Override
public void onLocationChanged(Location location) {
//Here we have our location update
}
@Override
public void onConnectionFailed(ConnectionResult connectionResult) {
}
}
view raw LocationClient hosted with ❤ by GitHub

Using the FusedLocationProviderApi

A striped down version of the code using the FusedLocationProviderApi looks like this:
public class MainActivity extends Activity implements LocationListener, GoogleApiClient.ConnectionCallbacks, GoogleApiClient.OnConnectionFailedListener {
private LocationRequest mLocationRequest;
private GoogleApiClient mGoogleApiClient;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mLocationRequest = LocationRequest.create();
mGoogleApiClient = new GoogleApiClient.Builder(this).addApi(LocationServices.API).addConnectionCallbacks(this).addOnConnectionFailedListener(this).build();
}
@Override
protected void onStart() {
super.onStart();
mGoogleApiClient.connect();
}
@Override
protected void onStop() {
super.onStop();
if(mGoogleApiClient.isConnected()) {
mGoogleApiClient.disconnect();
}
}
@Override
public void onConnected(Bundle bundle) {
LocationServices.FusedLocationApi.requestLocationUpdates(mGoogleApiClient, mLocationRequest, this);
}
@Override
public void onConnectionSuspended(int i) {
}
@Override
public void onLocationChanged(Location location) {
//u
}
@Override
public void onConnectionFailed(ConnectionResult connectionResult) {
}
}

Key differences

  1. ConnectionCallback interface from the GooglePlayServicesClient replaced with the ConnectionCallback interface from GoogleApiClient
  2. OnConnectionFailedListener interface from the GooglePlayServicesClient replaced with the OnConnectionFailedListener interface from GoogleApiClient
  3. Create a GoogleApiClient instead of LocationClient in onCreate
  4. In onStart and onStop use the GoogleApiClient instead of the LocationClient
  5. In onConnected use LocationServices.FusedLocationApi to request location updates
The switch from LocationServices to FusedLocationProviderApi shouldn't take more than a few minutes if you know what are you doing. Since Google are stubbornly refusing to document it I decided to write this blog post. I hope you find it useful.

Update: You can star this issue on the android bug tracker and maybe Google will fix the documentation.