The 11.0.0 release of the Google Play services SDK includes a new way to access LocationServices. The new APIs do not require your app to manually manage a connection to Google Play services through a GoogleApiClient. This reduces boilerplate and common pitfalls in your app.
GoogleApiClient
Read more below, or head straight to the updated location samples on GitHub.
The LocationServices APIs allow you to access device location, set up geofences, prompt the user to enable location on the device and more. In order to access these services, the app must connect to Google Play services, which can involve error-prone connection logic. For example, can you spot the crash in the app below?
Note: we'll assume our app has the ACCESS_FINE_LOCATION permission, which is required to get the user's exact location using the LocationServices APIs.
ACCESS_FINE_LOCATION
public class MainActivity extends AppCompatActivity implements GoogleApiClient.OnConnectionFailedListener { @Override public void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); GoogleApiClient client = new GoogleApiClient.Builder(this) .enableAutoManage(this, this) .addApi(LocationServices.API) .build(); client.connect(); PendingResult result = LocationServices.FusedLocationApi.requestLocationUpdates( client, LocationRequest.create(), pendingIntent); result.setResultCallback(new ResultCallback() { @Override public void onResult(@NonNull Status status) { Log.d(TAG, "Result: " + status.getStatusMessage()); } }); } // ... }
If you pointed to the requestLocationUpdates() call, you're right! That call throws an IllegalStateException, since the GoogleApiClient is has not yet connected. The call to connect() is asynchronous.
requestLocationUpdates()
IllegalStateException
connect()
While the code above looks like it should work, it's missing a ConnectionCallbacks argument to the GoogleApiClient builder. The call to request location updates should only be made after the onConnected callback has fired:
onConnected
public class MainActivity extends AppCompatActivity implements GoogleApiClient.OnConnectionFailedListener, GoogleApiClient.ConnectionCallbacks { private GoogleApiClient client; @Override protected void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); client = new GoogleApiClient.Builder(this) .enableAutoManage(this, this) .addApi(LocationServices.API) .addConnectionCallbacks(this) .build(); client.connect(); } @Override public void onConnected(@Nullable Bundle bundle) { PendingResult result = LocationServices.FusedLocationApi.requestLocationUpdates( client, LocationRequest.create(), pendingIntent); result.setResultCallback(new ResultCallback() { @Override public void onResult(@NonNull Status status) { Log.d(TAG, "Result: " + status.getStatusMessage()); } }); } // ... }
Now the code works, but it's not ideal for a few reasons:
onCreate
The new LocationServices APIs are much simpler and will make your code less error prone. The connection logic is handled automatically, and you only need to attach a single completion listener:
LocationServices
public class MainActivity extends AppCompatActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); FusedLocationProviderClient client = LocationServices.getFusedLocationProviderClient(this); client.requestLocationUpdates(LocationRequest.create(), pendingIntent) .addOnCompleteListener(new OnCompleteListener() { @Override public void onComplete(@NonNull Task task) { Log.d("MainActivity", "Result: " + task.getResult()); } }); } }
The new API immediately improves the code in a few ways:
The new API will automatically resolve certain connection failures for you, so you don't need to write code that for things like prompting the user to update Google Play services. Rather than exposing connection failures globally in the onConnectionFailed method, connection problems will fail the Task with an ApiException:
client.requestLocationUpdates(LocationRequest.create(), pendingIntent) .addOnFailureListener(new OnFailureListener() { @Override public void onFailure(@NonNull Exception e) { if (e instanceof ApiException) { Log.w(TAG, ((ApiException) e).getStatusMessage()); } else { Log.w(TAG, e.getMessage()); } } });
Try the new LocationServices APIs out for yourself in your own app or head over to the android-play-location samples on GitHub and see more examples of how the new clients reduce boilerplate and simplify logic.