Reduce friction with the new Location APIs

Your ads will be inserted here by

Easy Ads.

Please go to the plugin admin page to set up your ad code.

Posted by Aaron Stacy, Software Engineer, Google Play services

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.

Read more below, or head straight to the updated
location samples on GitHub.

Why not use GoogleApiClient?

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.

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.

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:

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:

  • It would be hard to refactor into shared classes if, for instance, you wanted
    to access Location Services in multiple activities.

  • The app connects optimistically in onCreate even if Location
    Services are not needed until later (for example, after user input).

  • It does not handle the case where the app fails to connect to Google Play
    services.

  • There is a lot of boilerplate connection logic before getting started with
    location updates.

A better developer experience

Your ads will be inserted here by

Easy Ads.

Please go to the plugin admin page to set up your ad code.

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:

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 API calls automatically wait for the service connection to be
    established, which removes the need to wait for onConnected before
    making requests.

  • It uses the Task
    API which makes it easier to compose asynchronous operations.

  • The code is self-contained and could easily be moved into a shared utility
    class or similar.

  • You don’t need to understand the underlying connection process to start
    coding.

What happened to all of the callbacks?

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 it for yourself

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.


Android Developers Blog

Your ads will be inserted here by

Easy Ads.

Please go to the plugin admin page to set up your ad code.

Leave a Reply

Your email address will not be published. Required fields are marked *

*

* Copy This Password *

* Type Or Paste Password Here *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>