Merge "docs: Updates the request-location-updates lesson in the location APIs training course." into lmp-docs

This commit is contained in:
Sarah Maddox
2014-12-18 19:42:34 +00:00
committed by Android (Google) Code Review

View File

@@ -1,612 +1,417 @@
page.title=Receiving Location Updates page.title=Receiving Location Updates
trainingnavtop=true trainingnavtop=true
@jd:body @jd:body
<div id="tb-wrapper"> <div id="tb-wrapper">
<div id="tb"> <div id="tb">
<h2>This lesson teaches you to</h2> <h2>This lesson teaches you how to</h2>
<ol> <ol>
<li><a href="#Permissions">Request Location Permission</a></li> <li><a href="#connect">Connect to Location Services</a></li>
<li><a href="#PlayServices">Check for Google Play Services</a></li> <li><a href="#location-request">Set Up a Location Request</a></li>
<li><a href="#DefineCallbacks">Define Location Services Callbacks</a></li> <li><a href="#updates">Request Location Updates</a></li>
<li><a href="#UpdateParameters">Specify Update Parameters</a></li> <li><a href="#callback">Define the Location Update Callback</a></li>
<li><a href="#StartUpdates">Start Location Updates</a></li> <li><a href="#stop-updates">Stop Location Updates</a></li>
<li><a href="#StopUpdates">Stop Location Updates</a></li> <li><a href="#save-state">Save the State of the Activity</a></li>
</ol> </ol>
<h2>You should also read</h2> <h2>You should also read</h2>
<ul> <ul>
<li> <li>
<a href="{@docRoot}google/play-services/setup.html">Setup Google Play Services SDK</a> <a href="{@docRoot}google/play-services/setup.html">Setting up Google Play
Services</a>
</li> </li>
<li> <li>
<a href="retrieve-current.html">Retrieving the Current Location</a> <a href="retrieve-current.html">Getting the Last Known Location</a>
</li> </li>
</ul> </ul>
<h2>Try it out</h2> <h2>Try it out</h2>
<div class="download-box"> <ul>
<a href="http://developer.android.com/shareables/training/LocationUpdates.zip" class="button">Download the sample</a> <li>
<p class="filename">LocationUpdates.zip</p> <a href="https://github.com/googlesamples/android-play-location/tree/master/LocationUpdates" class="external-link">LocationUpdates</a>
</div> </li>
</ul>
</div> </div>
</div> </div>
<p> <p>If your app can continuously track location, it can deliver more relevant
If your app does navigation or tracking, you probably want to get the user's information to the user. For example, if your app helps the user find their
location at regular intervals. While you can do this with way while walking or driving, or if your app tracks the location of assets, it
<code><a href="{@docRoot}reference/com/google/android/gms/location/LocationClient.html#getLastLocation()">LocationClient.getLastLocation()</a></code>, needs to get the location of the device at regular intervals. As well as the
a more direct approach is to request periodic updates from Location Services. In geographical location (latitude and longitude), you may want to give the user
response, Location Services automatically updates your app with the best available location, further information such as the bearing (horizontal direction of travel),
based on the currently-available location providers such as WiFi and GPS. altitude, or velocity of the device. This information, and more, is available
</p> in the {@link android.location.Location} object that your app can retrieve
<p> from the
To get periodic location updates from Location Services, you send a request using a location <a href="{@docRoot}reference/com/google/android/gms/location/FusedLocationProviderApi.html">fused
client. Depending on the form of the request, Location Services either invokes a callback location provider</a>.</p>
method and passes in a {@link android.location.Location} object, or issues an
{@link android.content.Intent} that contains the location in its extended data. The accuracy and <p>While you can get a device's location with
frequency of the updates are affected by the location permissions you've requested and the <a href="{@docRoot}reference/com/google/android/gms/location/FusedLocationProviderApi.html#getLastLocation(com.google.android.gms.common.api.GoogleApiClient)">{@code getLastLocation()}</a>,
parameters you pass to Location Services with the request. as illustrated in the lesson on
</p> <a href="retrieve-current.html">Getting the Last Known Location</a>,
<!-- Request permission --> a more direct approach is to request periodic updates from the fused location
<h2 id="Permissions">Specify App Permissions</h2> provider. In response, the API updates your app periodically with the best
<p> available location, based on the currently-available location providers such
Apps that use Location Services must request location permissions. Android has two location as WiFi and GPS (Global Positioning System). The accuracy of the location is
permissions, {@link android.Manifest.permission#ACCESS_COARSE_LOCATION ACCESS_COARSE_LOCATION} determined by the providers, the location permissions you've requested, and
and {@link android.Manifest.permission#ACCESS_FINE_LOCATION ACCESS_FINE_LOCATION}. The the options you set in the location request.</p>
permission you choose affects the accuracy of the location updates you receive.
For example, If you request only coarse location permission, Location Services obfuscates the <p>This lesson shows you how to request regular updates about a device's
updated location to an accuracy that's roughly equivalent to a city block. location using the
</p> <a href="{@docRoot}reference/com/google/android/gms/location/FusedLocationProviderApi.html#requestLocationUpdates(com.google.android.gms.common.api.GoogleApiClient, com.google.android.gms.location.LocationRequest, com.google.android.gms.location.LocationListener)">{@code requestLocationUpdates()}</a>
<p> method in the fused location provider.
Requesting {@link android.Manifest.permission#ACCESS_FINE_LOCATION ACCESS_FINE_LOCATION} implies
a request for {@link android.Manifest.permission#ACCESS_COARSE_LOCATION ACCESS_COARSE_LOCATION}. <h2 id="connect">Connect to Location Services</h2>
</p>
<p> <p>Location services for apps are provided through Google Play services and the
For example, to add the coarse location permission to your manifest, insert the following as a fused location provider. In order to use these services, you connect your app
child element of using the Google API Client and then request location updates. For details on
the connecting with the
<code><a href="{@docRoot}guide/topics/manifest/manifest-element.html">&lt;manifest&gt;</a></code> <a href="{@docRoot}reference/com/google/android/gms/common/api/GoogleApiClient.html">{@code GoogleApiClient}</a>,
element: follow the instructions in
</p> <a href="retrieve-current.html">Getting the Last Known Location</a>, including
<pre> requesting the current location.</p>
&lt;uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/&gt;
</pre> <p>The last known location of the device provides a handy base from which to
<!-- Check for Google Play services --> start, ensuring that the app has a known location before starting the
<h2 id="PlayServices">Check for Google Play Services</h2> periodic location updates. The lesson on
<p> <a href="retrieve-current.html">Getting the Last Known Location</a> shows you
Location Services is part of the Google Play services APK. Since it's hard to anticipate the how to get the last known location by calling
state of the user's device, you should always check that the APK is installed before you attempt <a href="{@docRoot}reference/com/google/android/gms/location/FusedLocationProviderApi.html#getLastLocation(com.google.android.gms.common.api.GoogleApiClient)">{@code getLastLocation()}</a>.
to connect to Location Services. To check that the APK is installed, call The snippets in the following sections assume that your app has already
<code><a href="{@docRoot}reference/com/google/android/gms/common/GooglePlayServicesUtil.html#isGooglePlayServicesAvailable(android.content.Context)">GooglePlayServicesUtil.isGooglePlayServicesAvailable()</a></code>, retrieved the last known location and stored it as a
which returns one of the {@link android.location.Location} object in the global variable
integer result codes listed in the API reference documentation. If you encounter an error, {@code mCurrentLocation}.</p>
call
<code><a href="{@docRoot}reference/com/google/android/gms/common/GooglePlayServicesUtil.html#getErrorDialog(int, android.app.Activity, int)">GooglePlayServicesUtil.getErrorDialog()</a></code> <p>Apps that use location services must request location permissions. In this
to retrieve localized dialog that prompts users to take the correct action, then display lesson you require fine location detection, so that your app can get as
the dialog in a {@link android.support.v4.app.DialogFragment}. The dialog may allow the precise a location as possible from the available location providers. Request
user to correct the problem, in which case Google Play services may send a result back to your this permission with the
activity. To handle this result, override the method {@code uses-permission} element in your app manifest, as shown in the
{@link android.support.v4.app.FragmentActivity#onActivityResult onActivityResult()} following example:</p>
</p>
<p class="note">
<strong>Note:</strong> To make your app compatible with
platform version 1.6 and later, the activity that displays the
{@link android.support.v4.app.DialogFragment} must subclass
{@link android.support.v4.app.FragmentActivity} instead of {@link android.app.Activity}. Using
{@link android.support.v4.app.FragmentActivity} also allows you to call
{@link android.support.v4.app.FragmentActivity#getSupportFragmentManager
getSupportFragmentManager()} to display the {@link android.support.v4.app.DialogFragment}.
</p>
<p>
Since you usually need to check for Google Play services in more than one place in your code,
define a method that encapsulates the check, then call the method before each connection
attempt. The following snippet contains all of the code required to check for Google
Play services:
</p>
<pre> <pre>
public class MainActivity extends FragmentActivity { &lt;manifest xmlns:android="http://schemas.android.com/apk/res/android"
... package="com.google.android.gms.location.sample.locationupdates" &gt;
// Global constants
/* &lt;uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/&gt;
* Define a request code to send to Google Play services &lt;/manifest&gt;
* This code is returned in Activity.onActivityResult
*/
private final static int
CONNECTION_FAILURE_RESOLUTION_REQUEST = 9000;
...
// Define a DialogFragment that displays the error dialog
public static class ErrorDialogFragment extends DialogFragment {
// Global field to contain the error dialog
private Dialog mDialog;
// Default constructor. Sets the dialog field to null
public ErrorDialogFragment() {
super();
mDialog = null;
}
// Set the dialog to display
public void setDialog(Dialog dialog) {
mDialog = dialog;
}
// Return a Dialog to the DialogFragment.
&#64;Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
return mDialog;
}
}
...
/*
* Handle results returned to the FragmentActivity
* by Google Play services
*/
&#64;Override
protected void onActivityResult(
int requestCode, int resultCode, Intent data) {
// Decide what to do based on the original request code
switch (requestCode) {
...
case CONNECTION_FAILURE_RESOLUTION_REQUEST :
/*
* If the result code is Activity.RESULT_OK, try
* to connect again
*/
switch (resultCode) {
case Activity.RESULT_OK :
/*
* Try the request again
*/
...
break;
}
...
}
...
}
...
private boolean servicesConnected() {
// Check that Google Play services is available
int resultCode =
GooglePlayServicesUtil.
isGooglePlayServicesAvailable(this);
// If Google Play services is available
if (ConnectionResult.SUCCESS == resultCode) {
// In debug mode, log the status
Log.d("Location Updates",
"Google Play services is available.");
// Continue
return true;
// Google Play services was not available for some reason
} else {
// Get the error code
int errorCode = connectionResult.getErrorCode();
// Get the error dialog from Google Play services
Dialog errorDialog = GooglePlayServicesUtil.getErrorDialog(
errorCode,
this,
CONNECTION_FAILURE_RESOLUTION_REQUEST);
// If Google Play services can provide an error dialog
if (errorDialog != null) {
// Create a new DialogFragment for the error dialog
ErrorDialogFragment errorFragment =
new ErrorDialogFragment();
// Set the dialog in the DialogFragment
errorFragment.setDialog(errorDialog);
// Show the error dialog in the DialogFragment
errorFragment.show(
getSupportFragmentManager(),
"Location Updates");
}
}
}
...
}
</pre> </pre>
<p>
Snippets in the following sections call this method to verify that Google Play services is <h2 id="location-request">Set Up a Location Request</h2>
available.
</p> <p>To store parameters for requests to the fused location provider, create a
<!-- <a href="{@docRoot}reference/com/google/android/gms/location/LocationRequest.html">{@code LocationRequest}</a>.
Define Location Services Callbacks The parameters determine the levels of accuracy requested. For details of all
--> the options available in the location request, see the
<h2 id="DefineCallbacks">Define Location Services Callbacks</h2> <a href="{@docRoot}reference/com/google/android/gms/location/LocationRequest.html">{@code LocationRequest}</a>
<p> class reference. This lesson sets the update interval, fastest update
Before you request location updates, you must first implement the interfaces that Location interval, and priority, as described below:</p>
Services uses to communicate connection status to your app:
</p>
<dl>
<dt>
<code><a href="{@docRoot}reference/com/google/android/gms/common/GooglePlayServicesClient.ConnectionCallbacks.html">ConnectionCallbacks</a></code>
</dt>
<dd>
Specifies methods that Location Services calls when a location client is connected or
disconnected.
</dd>
<dt>
<code><a href="{@docRoot}reference/com/google/android/gms/common/GooglePlayServicesClient.OnConnectionFailedListener.html">OnConnectionFailedListener</a></code>
</dt>
<dd>
Specifies a method that Location Services calls if an error occurs while attempting to
connect the location client. This method uses the previously-defined {@code showErrorDialog}
method to display an error dialog that attempts to fix the problem using Google Play
services.
</dd>
</dl>
<p>
The following snippet shows how to specify the interfaces and define the methods:
</p>
<pre>
public class MainActivity extends FragmentActivity implements
GooglePlayServicesClient.ConnectionCallbacks,
GooglePlayServicesClient.OnConnectionFailedListener {
...
/*
* Called by Location Services when the request to connect the
* client finishes successfully. At this point, you can
* request the current location or start periodic updates
*/
&#64;Override
public void onConnected(Bundle dataBundle) {
// Display the connection status
Toast.makeText(this, "Connected", Toast.LENGTH_SHORT).show();
}
...
/*
* Called by Location Services if the connection to the
* location client drops because of an error.
*/
&#64;Override
public void onDisconnected() {
// Display the connection status
Toast.makeText(this, "Disconnected. Please re-connect.",
Toast.LENGTH_SHORT).show();
}
...
/*
* Called by Location Services if the attempt to
* Location Services fails.
*/
&#64;Override
public void onConnectionFailed(ConnectionResult connectionResult) {
/*
* Google Play services can resolve some errors it detects.
* If the error has a resolution, try sending an Intent to
* start a Google Play services activity that can resolve
* error.
*/
if (connectionResult.hasResolution()) {
try {
// Start an Activity that tries to resolve the error
connectionResult.startResolutionForResult(
this,
CONNECTION_FAILURE_RESOLUTION_REQUEST);
/*
* Thrown if Google Play services canceled the original
* PendingIntent
*/
} catch (IntentSender.SendIntentException e) {
// Log the error
e.printStackTrace();
}
} else {
/*
* If no resolution is available, display a dialog to the
* user with the error.
*/
showErrorDialog(connectionResult.getErrorCode());
}
}
...
}
</pre>
<h3>Define the location update callback</h3>
<p>
Location Services sends location updates to your app either as an {@link android.content.Intent}
or as an argument passed to a callback method you define. This lesson shows you how to get the
update using a callback method, because that pattern works best for most use cases. If you want
to receive updates in the form of an {@link android.content.Intent}, read the lesson
<a href="activity-recognition.html">Recognizing the User's Current Activity</a>, which
presents a similar pattern.
</p>
<p>
The callback method that Location Services invokes to send a location update to your app is
specified in the
<code><a href="{@docRoot}reference/com/google/android/gms/location/LocationListener.html">LocationListener</a></code>
interface, in the method
<code><a href="{@docRoot}reference/com/google/android/gms/location/LocationListener.html#onLocationChanged(android.location.Location)">onLocationChanged()</a></code>.
The incoming argument is a {@link android.location.Location} object containing the location's
latitude and longitude. The following snippet shows how to specify the interface and define
the method:
</p>
<pre>
public class MainActivity extends FragmentActivity implements
GooglePlayServicesClient.ConnectionCallbacks,
GooglePlayServicesClient.OnConnectionFailedListener,
LocationListener {
...
// Define the callback method that receives location updates
&#64;Override
public void onLocationChanged(Location location) {
// Report to the UI that the location was updated
String msg = "Updated Location: " +
Double.toString(location.getLatitude()) + "," +
Double.toString(location.getLongitude());
Toast.makeText(this, msg, Toast.LENGTH_SHORT).show();
}
...
}
</pre>
<p>
Now that you have the callbacks prepared, you can set up the request for location updates.
The first step is to specify the parameters that control the updates.
</p>
<!-- Specify update parameters -->
<h2 id="UpdateParameters">Specify Update Parameters</h2>
<p>
Location Services allows you to control the interval between updates and the location accuracy
you want, by setting the values in a
<code><a href="{@docRoot}reference/com/google/android/gms/location/LocationRequest.html">LocationRequest</a></code>
object and then sending this object as part of your request to start updates.
</p>
<p>
First, set the following interval parameters:
</p>
<dl> <dl>
<dt> <dt>
Update interval Update interval
</dt> </dt>
<dd> <dd>
Set by <a href="{@docRoot}reference/com/google/android/gms/location/LocationRequest.html#setInterval(long)">{@code setInterval()}</a>
<code><a href="{@docRoot}reference/com/google/android/gms/location/LocationRequest.html#setInterval(long)">LocationRequest.setInterval()</a></code>. - This method sets the rate in milliseconds at which your app prefers to
This method sets the rate in milliseconds at which your app prefers to receive location receive location updates. Note that the location updates may be faster than
updates. If no other apps are receiving updates from Location Services, your app will this rate if another app is receiving updates at a faster rate, or slower
receive updates at this rate. than this rate, or there may be no updates at all (if the device has no
connectivity, for example).
</dd> </dd>
<dt> <dt>
Fastest update interval Fastest update interval
</dt> </dt>
<dd> <dd>
Set by <a href="{@docRoot}reference/com/google/android/gms/location/LocationRequest.html#setFastestInterval(long)">{@code setFastestInterval()}</a>
<code><a href="{@docRoot}reference/com/google/android/gms/location/LocationRequest.html#setFastestInterval(long)">LocationRequest.setFastestInterval()</a></code>. - This method sets the <strong>fastest</strong> rate in milliseconds at which
This method sets the <b>fastest</b> rate in milliseconds at which your app can handle your app can handle location updates. You need to set this rate because
location updates. You need to set this rate because other apps also affect the rate other apps also affect the rate at which updates are sent. The Google Play
at which updates are sent. Location Services sends out updates at the fastest rate that any services location APIs send out updates at the fastest rate that any app
app requested by calling has requested with
<code><a href="{@docRoot}reference/com/google/android/gms/location/LocationRequest.html#setInterval(long)">LocationRequest.setInterval()</a></code>. <a href="{@docRoot}reference/com/google/android/gms/location/LocationRequest.html#setInterval(long)">{@code setInterval()}</a>.
If this rate is faster than your app can handle, you may encounter problems with UI flicker If this rate is faster
or data overflow. To prevent this, call than your app can handle, you may encounter problems with UI flicker or data
<code><a href="{@docRoot}reference/com/google/android/gms/location/LocationRequest.html#setFastestInterval(long)">LocationRequest.setFastestInterval()</a></code> overflow. To prevent this, call
<a href="{@docRoot}reference/com/google/android/gms/location/LocationRequest.html#setFastestInterval(long)">{@code setFastestInterval()}</a>
to set an upper limit to the update rate. to set an upper limit to the update rate.
</dd>
<dt>Priority</dt>
<dd>
<p> <p>
Calling <a href="{@docRoot}reference/com/google/android/gms/location/LocationRequest.html#setPriority(int)">{@code setPriority()}</a>
<code><a href="{@docRoot}reference/com/google/android/gms/location/LocationRequest.html#setFastestInterval(long)">LocationRequest.setFastestInterval()</a></code> - This method sets the priority of the request, which gives the Google Play
also helps to save power. When you request a preferred update rate by calling services location services a strong hint about which location sources to use.
<code><a href="{@docRoot}reference/com/google/android/gms/location/LocationRequest.html#setInterval(long)">LocationRequest.setInterval()</a></code>, The following values are supported:</p>
and a maximum rate by calling <ul>
<code><a href="{@docRoot}reference/com/google/android/gms/location/LocationRequest.html#setFastestInterval(long)">LocationRequest.setFastestInterval()</a></code>, <li>
then your app gets the same update rate as the fastest rate in the system. If other <a href="{@docRoot}reference/com/google/android/gms/location/LocationRequest.html#PRIORITY_BALANCED_POWER_ACCURACY">{@code PRIORITY_BALANCED_POWER_ACCURACY}</a>
apps have requested a faster rate, you get the benefit of a faster rate. If no other - Use this setting to request location precision to within a city
apps have a faster rate request outstanding, your app receives updates at the rate you specified block, which is an accuracy of approximately 100 meters. This is
with considered a coarse level of accuracy, and is likely to consume less
<code><a href="{@docRoot}reference/com/google/android/gms/location/LocationRequest.html#setInterval(long)">LocationRequest.setInterval()</a></code>. power. With this setting, the location services are likely to use WiFi
</p> and cell tower positioning. Note, however, that the choice of location
provider depends on many other factors, such as which sources are
available.</li>
<li>
<a href="{@docRoot}reference/com/google/android/gms/location/LocationRequest.html#PRIORITY_HIGH_ACCURACY">{@code PRIORITY_HIGH_ACCURACY}</a>
- Use this setting to request the most precise location possible. With
this setting, the location services are more likely to use GPS
(Global Positioning System) to determine the location.</li>
<li><a href="{@docRoot}reference/com/google/android/gms/location/LocationRequest.html#PRIORITY_LOW_POWER">{@code PRIORITY_LOW_POWER}</a>
- Use this setting to request city-level precision, which is
an accuracy of approximately 10 kilometers. This is considered a
coarse level of accuracy, and is likely to consume less power.</li>
<li><a href="{@docRoot}reference/com/google/android/gms/location/LocationRequest.html#PRIORITY_NO_POWER">{@code PRIORITY_NO_POWER}</a>
- Use this setting if you need negligible impact on power consumption,
but want to receive location updates when available. With this
setting, your app does not trigger any location updates, but
receives locations triggered by other apps.</li>
</ul>
</dd> </dd>
</dl> </dl>
<p>
Next, set the accuracy parameter. In a foreground app, you need constant location updates with <p>Create the location request and set the parameters as shown in this
high accuracy, so use the setting code sample:</p>
<code><a href="{@docRoot}reference/com/google/android/gms/location/LocationRequest.html#PRIORITY_HIGH_ACCURACY">LocationRequest.PRIORITY_HIGH_ACCURACY</a></code>.
</p>
<p>
The following snippet shows how to set the update interval and accuracy in
{@link android.support.v4.app.FragmentActivity#onCreate onCreate()}:
</p>
<pre> <pre>
public class MainActivity extends FragmentActivity implements protected void createLocationRequest() {
GooglePlayServicesClient.ConnectionCallbacks, LocationRequest mLocationRequest = new LocationRequest();
GooglePlayServicesClient.OnConnectionFailedListener, mLocationRequest.setInterval(10000);
LocationListener { mLocationRequest.setFastestInterval(5000);
... mLocationRequest.setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY);
// Global constants
...
// Milliseconds per second
private static final int MILLISECONDS_PER_SECOND = 1000;
// Update frequency in seconds
public static final int UPDATE_INTERVAL_IN_SECONDS = 5;
// Update frequency in milliseconds
private static final long UPDATE_INTERVAL =
MILLISECONDS_PER_SECOND * UPDATE_INTERVAL_IN_SECONDS;
// The fastest update frequency, in seconds
private static final int FASTEST_INTERVAL_IN_SECONDS = 1;
// A fast frequency ceiling in milliseconds
private static final long FASTEST_INTERVAL =
MILLISECONDS_PER_SECOND * FASTEST_INTERVAL_IN_SECONDS;
...
// Define an object that holds accuracy and frequency parameters
LocationRequest mLocationRequest;
...
&#64;Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// Create the LocationRequest object
mLocationRequest = LocationRequest.create();
// Use high accuracy
mLocationRequest.setPriority(
LocationRequest.PRIORITY_HIGH_ACCURACY);
// Set the update interval to 5 seconds
mLocationRequest.setInterval(UPDATE_INTERVAL);
// Set the fastest update interval to 1 second
mLocationRequest.setFastestInterval(FASTEST_INTERVAL);
...
}
...
} }
</pre> </pre>
<p class="note">
<strong>Note:</strong> If your app accesses the network or does other long-running work after <p>The priority of
receiving a location update, adjust the fastest interval to a slower value. This prevents your <a href="{@docRoot}reference/com/google/android/gms/location/LocationRequest.html#PRIORITY_HIGH_ACCURACY">{@code PRIORITY_HIGH_ACCURACY}</a>,
app from receiving updates it can't use. Once the long-running work is done, set the fastest combined with the
interval back to a fast value. {@link android.Manifest.permission#ACCESS_FINE_LOCATION ACCESS_FINE_LOCATION}
</p> permission setting that you've defined in the app manifest, and a fast update
<!-- Start Location Updates --> interval of 5000 milliseconds (5 seconds), causes the fused location
<h2 id="StartUpdates">Start Location Updates</h2> provider to return location updates that are accurate to within a few feet.
<p> This approach is appropriate for mapping apps that display the location in
To send the request for location updates, create a location client in real time.</p>
{@link android.support.v4.app.FragmentActivity#onCreate onCreate()}, then connect it and make
the request by calling <p class="note"><strong>Performance hint:</strong> If your app accesses the
<code><a href="{@docRoot}reference/com/google/android/gms/location/LocationClient.html#requestLocationUpdates(com.google.android.gms.location.LocationRequest, com.google.android.gms.location.LocationListener)">requestLocationUpdates()</a></code>. network or does other long-running work after receiving a location update,
Since your client must be connected for your app to receive updates, you should adjust the fastest interval to a slower value. This adjustment prevents your
connect the client in app from receiving updates it can't use. Once the long-running work is done,
{@link android.support.v4.app.FragmentActivity#onStart onStart()}. This ensures that you always set the fastest interval back to a fast value.</p>
have a valid, connected client while your app is visible. Since you need a connection before you
can request updates, make the update request in <h2 id="updates">Request Location Updates</h2>
<code><a href="{@docRoot}reference/com/google/android/gms/common/GooglePlayServicesClient.ConnectionCallbacks.html#onConnected(android.os.Bundle)">ConnectionCallbacks.onConnected()</a></code>
</p> <p>Now that you've set up a location request containing your app's requirements
<p> for the location updates, you can start the regular updates by calling
Remember that the user may want to turn off location updates for various reasons. You should <a href="{@docRoot}reference/com/google/android/gms/location/FusedLocationProviderApi.html#requestLocationUpdates(com.google.android.gms.common.api.GoogleApiClient, com.google.android.gms.location.LocationRequest, com.google.android.gms.location.LocationListener)">{@code requestLocationUpdates()}</a>.
provide a way for the user to do this, and you should ensure that you don't start updates in Do this in the
{@link android.support.v4.app.FragmentActivity#onStart onStart()} if updates were previously <a href="{@docRoot}reference/com/google/android/gms/common/api/GoogleApiClient.ConnectionCallbacks.html#onConnected(android.os.Bundle)">{@code onConnected()}</a>
turned off. To track the user's preference, store it in your app's callback provided by Google API Client, which is called when the client is
{@link android.content.SharedPreferences} in ready.</p>
{@link android.support.v4.app.FragmentActivity#onPause onPause()} and retrieve it in
{@link android.support.v4.app.FragmentActivity#onResume onResume()}. <p>Depending on the form of the request, the fused location provider either
</p> invokes the
<p> <a href="{@docRoot}reference/com/google/android/gms/location/LocationListener.html">{@code LocationListener.onLocationChanged()}</a>
The following snippet shows how to set up the client in callback method and passes it a {@link android.location.Location} object, or
{@link android.support.v4.app.FragmentActivity#onCreate onCreate()}, and how to connect it issues a
and request updates in {@link android.support.v4.app.FragmentActivity#onStart onStart()}: <a href="{@docRoot}reference/android/app/PendingIntent.html">{@code PendingIntent}</a>
</p> that contains the location in its extended data. The accuracy and frequency of
the updates are affected by the location permissions you've requested and the
options you set in the location request object.</p>
<p>This lesson shows you how to get the update using the
<a href="{@docRoot}reference/com/google/android/gms/location/LocationListener.html">{@code LocationListener}</a>
callback approach. Call
<a href="{@docRoot}reference/com/google/android/gms/location/FusedLocationProviderApi.html#requestLocationUpdates(com.google.android.gms.common.api.GoogleApiClient, com.google.android.gms.location.LocationRequest, com.google.android.gms.location.LocationListener)">{@code requestLocationUpdates()}</a>,
passing it your instance of the
<a href="{@docRoot}reference/com/google/android/gms/common/api/GoogleApiClient.html">{@code GoogleApiClient}</a>,
the
<a href="{@docRoot}reference/com/google/android/gms/location/LocationRequest.html">{@code LocationRequest}</a>
object,
and a <a href="{@docRoot}reference/com/google/android/gms/location/LocationListener.html">{@code LocationListener}</a>.
Define a {@code startLocationUpdates()} method, called from the
<a href="{@docRoot}reference/com/google/android/gms/common/api/GoogleApiClient.ConnectionCallbacks.html#onConnected(android.os.Bundle)">{@code onConnected()}</a>
callback, as shown in the following code sample:</p>
<pre> <pre>
public class MainActivity extends FragmentActivity implements &#64;Override
GooglePlayServicesClient.ConnectionCallbacks, public void onConnected(Bundle connectionHint) {
GooglePlayServicesClient.OnConnectionFailedListener,
LocationListener {
... ...
// Global variables if (mRequestingLocationUpdates) {
... startLocationUpdates();
LocationClient mLocationClient; }
boolean mUpdatesRequested; }
protected void startLocationUpdates() {
LocationServices.FusedLocationApi.requestLocationUpdates(
mGoogleApiClient, mLocationRequest, this);
}
</pre>
<p>Notice that the above code snippet refers to a boolean flag,
{@code mRequestingLocationUpdates}, used to track whether the user has
turned location updates on or off. For more about retaining the value of this
flag across instances of the activity, see
<a href="#save-state">Save the State of the Activity</a>.
<h2 id="callback">Define the Location Update Callback</h2>
<p>The fused location provider invokes the
<a href="{@docRoot}reference/com/google/android/gms/location/LocationListener.html#onLocationChanged(android.location.Location)">{@code LocationListener.onLocationChanged()}</a>
callback method. The incoming argument is a {@link android.location.Location}
object containing the location's latitude and longitude. The following snippet
shows how to implement the
<a href="{@docRoot}reference/com/google/android/gms/location/LocationListener.html">{@code LocationListener}</a>
interface and define the method, then get the timestamp of the location update
and display the latitude, longitude and timestamp on your app's user
interface:</p>
<pre>
public class MainActivity extends ActionBarActivity implements
ConnectionCallbacks, OnConnectionFailedListener, LocationListener {
... ...
&#64;Override &#64;Override
protected void onCreate(Bundle savedInstanceState) { public void onLocationChanged(Location location) {
... mCurrentLocation = location;
// Open the shared preferences mLastUpdateTime = DateFormat.getTimeInstance().format(new Date());
mPrefs = getSharedPreferences("SharedPreferences", updateUI();
Context.MODE_PRIVATE);
// Get a SharedPreferences editor
mEditor = mPrefs.edit();
/*
* Create a new location client, using the enclosing class to
* handle callbacks.
*/
mLocationClient = new LocationClient(this, this, this);
// Start with updates turned off
mUpdatesRequested = false;
...
} }
...
private void updateUI() {
mLatitudeTextView.setText(String.valueOf(mCurrentLocation.getLatitude()));
mLongitudeTextView.setText(String.valueOf(mCurrentLocation.getLongitude()));
mLastUpdateTimeTextView.setText(mLastUpdateTime);
}
}
</pre>
<h2 id="stop-updates">Stop Location Updates</h2>
<p>Consider whether you want to stop the location updates when the activity is
no longer in focus, such as when the user switches to another app or to a
different activity in the same app. This can be handy to reduce power
consumption, provided the app doesn't need to collect information even when
it's running in the background. This section shows how you can stop the
updates in the activity's
{@link android.app.Activity#onPause onPause()} method.</p>
<p>To stop location updates, call
<a href="{@docRoot}reference/com/google/android/gms/location/FusedLocationProviderApi.html#removeLocationUpdates(com.google.android.gms.common.api.GoogleApiClient, com.google.android.gms.location.LocationListener)">{@code removeLocationUpdates()}</a>,
passing it your instance of the
<a href="{@docRoot}reference/com/google/android/gms/common/api/GoogleApiClient.html">{@code GoogleApiClient}</a>
object and a
<a href="{@docRoot}reference/com/google/android/gms/location/LocationListener.html">{@code LocationListener}</a>,
as shown in the following code sample:</p>
<pre>
&#64;Override &#64;Override
protected void onPause() { protected void onPause() {
// Save the current setting for updates
mEditor.putBoolean("KEY_UPDATES_ON", mUpdatesRequested);
mEditor.commit();
super.onPause(); super.onPause();
stopLocationUpdates();
} }
...
&#64;Override
protected void onStart() {
...
mLocationClient.connect();
}
...
&#64;Override
protected void onResume() {
/*
* Get any previous setting for location updates
* Gets "false" if an error occurs
*/
if (mPrefs.contains("KEY_UPDATES_ON")) {
mUpdatesRequested =
mPrefs.getBoolean("KEY_UPDATES_ON", false);
// Otherwise, turn off location updates protected void stopLocationUpdates() {
} else { LocationServices.FusedLocationApi.removeLocationUpdates(
mEditor.putBoolean("KEY_UPDATES_ON", false); mGoogleApiClient, this);
mEditor.commit();
}
}
...
/*
* Called by Location Services when the request to connect the
* client finishes successfully. At this point, you can
* request the current location or start periodic updates
*/
&#64;Override
public void onConnected(Bundle dataBundle) {
// Display the connection status
Toast.makeText(this, "Connected", Toast.LENGTH_SHORT).show();
// If already requested, start periodic updates
if (mUpdatesRequested) {
mLocationClient.requestLocationUpdates(mLocationRequest, this);
}
}
...
} }
</pre> </pre>
<p>
For more information about saving preferences, read <p>Use a boolean, {@code mRequestingLocationUpdates}, to track
<a href="{@docRoot}training/basics/data-storage/shared-preferences.html">Saving Key-Value Sets</a>. whether location updates are currently turned on. In the activity's
</p> {@link android.app.Activity#onResume onResume()} method, check
<!-- whether location updates are currently active, and activate them if not:</p>
Stop Location Updates
-->
<h2 id="StopUpdates">Stop Location Updates</h2>
<p>
To stop location updates, save the state of the update flag in
{@link android.support.v4.app.FragmentActivity#onPause onPause()}, and stop updates in
{@link android.support.v4.app.FragmentActivity#onStop onStop()} by calling
<code><a href="{@docRoot}reference/com/google/android/gms/location/LocationClient.html#removeLocationUpdates(com.google.android.gms.location.LocationListener)">removeLocationUpdates(LocationListener)</a></code>.
For example:
</p>
<pre> <pre>
public class MainActivity extends FragmentActivity implements
GooglePlayServicesClient.ConnectionCallbacks,
GooglePlayServicesClient.OnConnectionFailedListener,
LocationListener {
...
/*
* Called when the Activity is no longer visible at all.
* Stop updates and disconnect.
*/
&#64;Override &#64;Override
protected void onStop() { public void onResume() {
// If the client is connected super.onResume();
if (mLocationClient.isConnected()) { if (mGoogleApiClient.isConnected() && !mRequestingLocationUpdates) {
/* startLocationUpdates();
* Remove location updates for a listener.
* The current Activity is the listener, so
* the argument is "this".
*/
removeLocationUpdates(this);
} }
/*
* After disconnect() is called, the client is
* considered "dead".
*/
mLocationClient.disconnect();
super.onStop();
}
...
} }
</pre> </pre>
<p>
You now have the basic structure of an app that requests and receives periodic location updates. <h2 id="save-state">Save the State of the Activity</h2>
You can combine the features described in this lesson with the geofencing, activity recognition,
or reverse geocoding features described in other lessons in this class. <p>A change to the device's configuration, such as a change in screen
</p> orientation or language, can cause the current activity to be destroyed. Your
<p> app must therefore store any information it needs to recreate the activity.
The next lesson, <a href="display-address.html">Displaying a Location Address</a>, shows you how One way to do this is via an instance state stored in a
to use the current location to display the current street address. {@link android.os.Bundle} object.</p>
</p>
<p>The following code sample shows how to use the activity's
<a href="{@docRoot}reference/android/app/Activity.html#onSaveInstanceState(android.os.Bundle)">{@code onSaveInstanceState()}</a>
callback to save the instance state:</p>
<pre>
public void onSaveInstanceState(Bundle savedInstanceState) {
savedInstanceState.putBoolean(REQUESTING_LOCATION_UPDATES_KEY,
mRequestingLocationUpdates);
savedInstanceState.putParcelable(LOCATION_KEY, mCurrentLocation);
savedInstanceState.putString(LAST_UPDATED_TIME_STRING_KEY, mLastUpdateTime);
super.onSaveInstanceState(savedInstanceState);
}
</pre>
<p>Define an {@code updateValuesFromBundle()} method to restore
the saved values from the previous instance of the activity, if they're
available. Call the method from the activity's
{@link android.app.Activity#onCreate onCreate()} method, as shown in the
following code sample:</p>
<pre>
&#64;Override
public void onCreate(Bundle savedInstanceState) {
...
updateValuesFromBundle(savedInstanceState);
}
private void updateValuesFromBundle(Bundle savedInstanceState) {
if (savedInstanceState != null) {
// Update the value of mRequestingLocationUpdates from the Bundle, and
// make sure that the Start Updates and Stop Updates buttons are
// correctly enabled or disabled.
if (savedInstanceState.keySet().contains(REQUESTING_LOCATION_UPDATES_KEY)) {
mRequestingLocationUpdates = savedInstanceState.getBoolean(
REQUESTING_LOCATION_UPDATES_KEY);
setButtonsEnabledState();
}
// Update the value of mCurrentLocation from the Bundle and update the
// UI to show the correct latitude and longitude.
if (savedInstanceState.keySet().contains(LOCATION_KEY)) {
// Since LOCATION_KEY was found in the Bundle, we can be sure that
// mCurrentLocationis not null.
mCurrentLocation = savedInstanceState.getParcelable(LOCATION_KEY);
}
// Update the value of mLastUpdateTime from the Bundle and update the UI.
if (savedInstanceState.keySet().contains(LAST_UPDATED_TIME_STRING_KEY)) {
mLastUpdateTime = savedInstanceState.getString(
LAST_UPDATED_TIME_STRING_KEY);
}
updateUI();
}
}
</pre>
<p>For more about saving instance state, see the
<a href="{@docRoot}reference/android/app/Activity.html#ConfigurationChanges">Android
Activity</a> class reference.</p>
<p class="note"><strong>Note:</strong> For a more persistent storage, you can
store the user's preferences in your app's
{@link android.content.SharedPreferences}. Set the shared preference in
your activity's {@link android.app.Activity#onPause onPause()} method, and
retrieve the preference in {@link android.app.Activity#onResume onResume()}.
For more information about saving preferences, read
<a href="{@docRoot}training/basics/data-storage/shared-preferences.html">Saving
Key-Value Sets</a>.</p>
<p>The next lesson,
<a href="display-address.html">Displaying a Location Address</a>, shows
you how to display the street address for a given location.</p>