blob: 208dc1799faf3e5af3cad2ced827d9910e104c8d [file] [log] [blame]
page.title=Receiving Location Updates
trainingnavtop=true
@jd:body
<div id="tb-wrapper">
<div id="tb">
<h2>This lesson teaches you how to</h2>
<ol>
<li><a href="#connect">Connect to Location Services</a></li>
<li><a href="#location-request">Set Up a Location Request</a></li>
<li><a href="#updates">Request Location Updates</a></li>
<li><a href="#callback">Define the Location Update Callback</a></li>
<li><a href="#stop-updates">Stop Location Updates</a></li>
<li><a href="#save-state">Save the State of the Activity</a></li>
</ol>
<h2>You should also read</h2>
<ul>
<li>
<a href="{@docRoot}google/play-services/setup.html">Setting up Google Play
Services</a>
</li>
<li>
<a href="retrieve-current.html">Getting the Last Known Location</a>
</li>
</ul>
<h2>Try it out</h2>
<ul>
<li>
<a href="https://github.com/googlesamples/android-play-location/tree/master/LocationUpdates" class="external-link">LocationUpdates</a>
</li>
</ul>
</div>
</div>
<p>If your app can continuously track location, it can deliver more relevant
information to the user. For example, if your app helps the user find their
way while walking or driving, or if your app tracks the location of assets, it
needs to get the location of the device at regular intervals. As well as the
geographical location (latitude and longitude), you may want to give the user
further information such as the bearing (horizontal direction of travel),
altitude, or velocity of the device. This information, and more, is available
in the {@link android.location.Location} object that your app can retrieve
from the
<a href="{@docRoot}reference/com/google/android/gms/location/FusedLocationProviderApi.html">fused
location provider</a>.</p>
<p>While you can get a device's location with
<a href="{@docRoot}reference/com/google/android/gms/location/FusedLocationProviderApi.html#getLastLocation(com.google.android.gms.common.api.GoogleApiClient)">{@code getLastLocation()}</a>,
as illustrated in the lesson on
<a href="retrieve-current.html">Getting the Last Known Location</a>,
a more direct approach is to request periodic updates from the fused location
provider. In response, the API updates your app periodically with the best
available location, based on the currently-available location providers such
as WiFi and GPS (Global Positioning System). The accuracy of the location is
determined by the providers, the location permissions you've requested, and
the options you set in the location request.</p>
<p>This lesson shows you how to request regular updates about a device's
location using the
<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>
method in the fused location provider.
<h2 id="connect">Connect to Location Services</h2>
<p>Location services for apps are provided through Google Play services and the
fused location provider. In order to use these services, you connect your app
using the Google API Client and then request location updates. For details on
connecting with the
<a href="{@docRoot}reference/com/google/android/gms/common/api/GoogleApiClient.html">{@code GoogleApiClient}</a>,
follow the instructions in
<a href="retrieve-current.html">Getting the Last Known Location</a>, including
requesting the current location.</p>
<p>The last known location of the device provides a handy base from which to
start, ensuring that the app has a known location before starting the
periodic location updates. The lesson on
<a href="retrieve-current.html">Getting the Last Known Location</a> shows you
how to get the last known location by calling
<a href="{@docRoot}reference/com/google/android/gms/location/FusedLocationProviderApi.html#getLastLocation(com.google.android.gms.common.api.GoogleApiClient)">{@code getLastLocation()}</a>.
The snippets in the following sections assume that your app has already
retrieved the last known location and stored it as a
{@link android.location.Location} object in the global variable
{@code mCurrentLocation}.</p>
<p>Apps that use location services must request location permissions. In this
lesson you require fine location detection, so that your app can get as
precise a location as possible from the available location providers. Request
this permission with the
{@code uses-permission} element in your app manifest, as shown in the
following example:</p>
<pre>
&lt;manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.google.android.gms.location.sample.locationupdates" &gt;
&lt;uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/&gt;
&lt;/manifest&gt;
</pre>
<h2 id="location-request">Set Up a Location Request</h2>
<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>.
The parameters determine the levels of accuracy requested. For details of all
the options available in the location request, see the
<a href="{@docRoot}reference/com/google/android/gms/location/LocationRequest.html">{@code LocationRequest}</a>
class reference. This lesson sets the update interval, fastest update
interval, and priority, as described below:</p>
<dl>
<dt>
Update interval
</dt>
<dd>
<a href="{@docRoot}reference/com/google/android/gms/location/LocationRequest.html#setInterval(long)">{@code setInterval()}</a>
- This method sets the rate in milliseconds at which your app prefers to
receive location updates. Note that the location updates may be faster than
this rate if another app is receiving updates at a faster rate, or slower
than this rate, or there may be no updates at all (if the device has no
connectivity, for example).
</dd>
<dt>
Fastest update interval
</dt>
<dd>
<a href="{@docRoot}reference/com/google/android/gms/location/LocationRequest.html#setFastestInterval(long)">{@code setFastestInterval()}</a>
- This method sets the <strong>fastest</strong> rate in milliseconds at which
your app can handle location updates. You need to set this rate because
other apps also affect the rate at which updates are sent. The Google Play
services location APIs send out updates at the fastest rate that any app
has requested with
<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 or data
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.
</dd>
<dt>Priority</dt>
<dd>
<p>
<a href="{@docRoot}reference/com/google/android/gms/location/LocationRequest.html#setPriority(int)">{@code setPriority()}</a>
- This method sets the priority of the request, which gives the Google Play
services location services a strong hint about which location sources to use.
The following values are supported:</p>
<ul>
<li>
<a href="{@docRoot}reference/com/google/android/gms/location/LocationRequest.html#PRIORITY_BALANCED_POWER_ACCURACY">{@code PRIORITY_BALANCED_POWER_ACCURACY}</a>
- Use this setting to request location precision to within a city
block, which is an accuracy of approximately 100 meters. This is
considered a coarse level of accuracy, and is likely to consume less
power. With this setting, the location services are likely to use WiFi
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>
</dl>
<p>Create the location request and set the parameters as shown in this
code sample:</p>
<pre>
protected void createLocationRequest() {
LocationRequest mLocationRequest = new LocationRequest();
mLocationRequest.setInterval(10000);
mLocationRequest.setFastestInterval(5000);
mLocationRequest.setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY);
}
</pre>
<p>The priority of
<a href="{@docRoot}reference/com/google/android/gms/location/LocationRequest.html#PRIORITY_HIGH_ACCURACY">{@code PRIORITY_HIGH_ACCURACY}</a>,
combined with the
{@link android.Manifest.permission#ACCESS_FINE_LOCATION ACCESS_FINE_LOCATION}
permission setting that you've defined in the app manifest, and a fast update
interval of 5000 milliseconds (5 seconds), causes the fused location
provider to return location updates that are accurate to within a few feet.
This approach is appropriate for mapping apps that display the location in
real time.</p>
<p class="note"><strong>Performance hint:</strong> If your app accesses the
network or does other long-running work after receiving a location update,
adjust the fastest interval to a slower value. This adjustment prevents your
app from receiving updates it can't use. Once the long-running work is done,
set the fastest interval back to a fast value.</p>
<h2 id="updates">Request Location Updates</h2>
<p>Now that you've set up a location request containing your app's requirements
for the location updates, you can start the regular updates by calling
<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>.
Do this in the
<a href="{@docRoot}reference/com/google/android/gms/common/api/GoogleApiClient.ConnectionCallbacks.html#onConnected(android.os.Bundle)">{@code onConnected()}</a>
callback provided by Google API Client, which is called when the client is
ready.</p>
<p>Depending on the form of the request, the fused location provider either
invokes the
<a href="{@docRoot}reference/com/google/android/gms/location/LocationListener.html">{@code LocationListener.onLocationChanged()}</a>
callback method and passes it a {@link android.location.Location} object, or
issues a
<a href="{@docRoot}reference/android/app/PendingIntent.html">{@code PendingIntent}</a>
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>
&#64;Override
public void onConnected(Bundle connectionHint) {
...
if (mRequestingLocationUpdates) {
startLocationUpdates();
}
}
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
public void onLocationChanged(Location location) {
mCurrentLocation = location;
mLastUpdateTime = DateFormat.getTimeInstance().format(new Date());
updateUI();
}
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
protected void onPause() {
super.onPause();
stopLocationUpdates();
}
protected void stopLocationUpdates() {
LocationServices.FusedLocationApi.removeLocationUpdates(
mGoogleApiClient, this);
}
</pre>
<p>Use a boolean, {@code mRequestingLocationUpdates}, to track
whether location updates are currently turned on. In the activity's
{@link android.app.Activity#onResume onResume()} method, check
whether location updates are currently active, and activate them if not:</p>
<pre>
&#64;Override
public void onResume() {
super.onResume();
if (mGoogleApiClient.isConnected() && !mRequestingLocationUpdates) {
startLocationUpdates();
}
}
</pre>
<h2 id="save-state">Save the State of the Activity</h2>
<p>A change to the device's configuration, such as a change in screen
orientation or language, can cause the current activity to be destroyed. Your
app must therefore store any information it needs to recreate the activity.
One way to do this is via an instance state stored in a
{@link android.os.Bundle} object.</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>