diff options
| -rw-r--r-- | docs/html/training/location/receive-location-updates.jd | 943 |
1 files changed, 374 insertions, 569 deletions
diff --git a/docs/html/training/location/receive-location-updates.jd b/docs/html/training/location/receive-location-updates.jd index e6e8c51a5078..208dc1799faf 100644 --- a/docs/html/training/location/receive-location-updates.jd +++ b/docs/html/training/location/receive-location-updates.jd @@ -1,612 +1,417 @@ page.title=Receiving Location Updates trainingnavtop=true @jd:body + <div id="tb-wrapper"> -<div id="tb"> - -<h2>This lesson teaches you to</h2> -<ol> - <li><a href="#Permissions">Request Location Permission</a></li> - <li><a href="#PlayServices">Check for Google Play Services</a></li> - <li><a href="#DefineCallbacks">Define Location Services Callbacks</a></li> - <li><a href="#UpdateParameters">Specify Update Parameters</a></li> - <li><a href="#StartUpdates">Start Location Updates</a></li> - <li><a href="#StopUpdates">Stop Location Updates</a></li> -</ol> - -<h2>You should also read</h2> -<ul> + <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">Setup Google Play Services SDK</a> + <a href="{@docRoot}google/play-services/setup.html">Setting up Google Play + Services</a> </li> <li> - <a href="retrieve-current.html">Retrieving the Current Location</a> + <a href="retrieve-current.html">Getting the Last Known Location</a> </li> - </ul> + </ul> -<h2>Try it out</h2> + <h2>Try it out</h2> -<div class="download-box"> - <a href="http://developer.android.com/shareables/training/LocationUpdates.zip" class="button">Download the sample</a> - <p class="filename">LocationUpdates.zip</p> + <ul> + <li> + <a href="https://github.com/googlesamples/android-play-location/tree/master/LocationUpdates" class="external-link">LocationUpdates</a> + </li> + </ul> + </div> </div> -</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> -<p> - If your app does navigation or tracking, you probably want to get the user's - location at regular intervals. While you can do this with -<code><a href="{@docRoot}reference/com/google/android/gms/location/LocationClient.html#getLastLocation()">LocationClient.getLastLocation()</a></code>, - a more direct approach is to request periodic updates from Location Services. In - response, Location Services automatically updates your app with the best available location, - based on the currently-available location providers such as WiFi and GPS. -</p> -<p> - To get periodic location updates from Location Services, you send a request using a location - client. Depending on the form of the request, Location Services either invokes a callback - 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 - frequency of the updates are affected by the location permissions you've requested and the - parameters you pass to Location Services with the request. -</p> -<!-- Request permission --> -<h2 id="Permissions">Specify App Permissions</h2> -<p> - Apps that use Location Services must request location permissions. Android has two location - permissions, {@link android.Manifest.permission#ACCESS_COARSE_LOCATION ACCESS_COARSE_LOCATION} - and {@link android.Manifest.permission#ACCESS_FINE_LOCATION ACCESS_FINE_LOCATION}. The - 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 - updated location to an accuracy that's roughly equivalent to a city block. -</p> -<p> - 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}. -</p> -<p> - For example, to add the coarse location permission to your manifest, insert the following as a - child element of - the -<code><a href="{@docRoot}guide/topics/manifest/manifest-element.html"><manifest></a></code> - element: -</p> <pre> -<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/> +<manifest xmlns:android="http://schemas.android.com/apk/res/android" + package="com.google.android.gms.location.sample.locationupdates" > + + <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/> +</manifest> </pre> -<!-- Check for Google Play services --> -<h2 id="PlayServices">Check for Google Play Services</h2> -<p> - Location Services is part of the Google Play services APK. Since it's hard to anticipate the - state of the user's device, you should always check that the APK is installed before you attempt - to connect to Location Services. To check that the APK is installed, call -<code><a href="{@docRoot}reference/com/google/android/gms/common/GooglePlayServicesUtil.html#isGooglePlayServicesAvailable(android.content.Context)">GooglePlayServicesUtil.isGooglePlayServicesAvailable()</a></code>, - which returns one of the - integer result codes listed in the API reference documentation. If you encounter an error, - call -<code><a href="{@docRoot}reference/com/google/android/gms/common/GooglePlayServicesUtil.html#getErrorDialog(int, android.app.Activity, int)">GooglePlayServicesUtil.getErrorDialog()</a></code> - to retrieve localized dialog that prompts users to take the correct action, then display - the dialog in a {@link android.support.v4.app.DialogFragment}. The dialog may allow the - user to correct the problem, in which case Google Play services may send a result back to your - activity. To handle this result, override the method - {@link android.support.v4.app.FragmentActivity#onActivityResult onActivityResult()} - -</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> + +<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> -public class MainActivity extends FragmentActivity { - ... - // Global constants - /* - * Define a request code to send to Google Play services - * 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. - @Override - public Dialog onCreateDialog(Bundle savedInstanceState) { - return mDialog; - } - } - ... - /* - * Handle results returned to the FragmentActivity - * by Google Play services - */ - @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"); - } - } - } - ... +protected void createLocationRequest() { + LocationRequest mLocationRequest = new LocationRequest(); + mLocationRequest.setInterval(10000); + mLocationRequest.setFastestInterval(5000); + mLocationRequest.setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY); } </pre> -<p> - Snippets in the following sections call this method to verify that Google Play services is - available. -</p> -<!-- - Define Location Services Callbacks - --> -<h2 id="DefineCallbacks">Define Location Services Callbacks</h2> -<p> - Before you request location updates, you must first implement the interfaces that Location - 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> + +<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> -public class MainActivity extends FragmentActivity implements - GooglePlayServicesClient.ConnectionCallbacks, - GooglePlayServicesClient.OnConnectionFailedListener { +@Override +public void onConnected(Bundle connectionHint) { ... - /* - * 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 - */ - @Override - public void onConnected(Bundle dataBundle) { - // Display the connection status - Toast.makeText(this, "Connected", Toast.LENGTH_SHORT).show(); + if (mRequestingLocationUpdates) { + startLocationUpdates(); } - ... - /* - * Called by Location Services if the connection to the - * location client drops because of an error. - */ - @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. - */ - @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()); - } - } - ... +} + +protected void startLocationUpdates() { + LocationServices.FusedLocationApi.requestLocationUpdates( + mGoogleApiClient, mLocationRequest, this); } </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> + +<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 FragmentActivity implements - GooglePlayServicesClient.ConnectionCallbacks, - GooglePlayServicesClient.OnConnectionFailedListener, - LocationListener { +public class MainActivity extends ActionBarActivity implements + ConnectionCallbacks, OnConnectionFailedListener, LocationListener { ... - // Define the callback method that receives location updates @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(); + 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> -<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> - <dt> - Update interval - </dt> - <dd> - Set by -<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 receive location - updates. If no other apps are receiving updates from Location Services, your app will - receive updates at this rate. - </dd> - <dt> - Fastest update interval - </dt> - <dd> - Set by -<code><a href="{@docRoot}reference/com/google/android/gms/location/LocationRequest.html#setFastestInterval(long)">LocationRequest.setFastestInterval()</a></code>. - This method sets the <b>fastest</b> 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. Location Services sends out updates at the fastest rate that any - app requested by calling -<code><a href="{@docRoot}reference/com/google/android/gms/location/LocationRequest.html#setInterval(long)">LocationRequest.setInterval()</a></code>. - If this rate is faster than your app can handle, you may encounter problems with UI flicker - or data overflow. To prevent this, call -<code><a href="{@docRoot}reference/com/google/android/gms/location/LocationRequest.html#setFastestInterval(long)">LocationRequest.setFastestInterval()</a></code> - to set an upper limit to the update rate. - <p> - Calling -<code><a href="{@docRoot}reference/com/google/android/gms/location/LocationRequest.html#setFastestInterval(long)">LocationRequest.setFastestInterval()</a></code> - also helps to save power. When you request a preferred update rate by calling -<code><a href="{@docRoot}reference/com/google/android/gms/location/LocationRequest.html#setInterval(long)">LocationRequest.setInterval()</a></code>, - and a maximum rate by calling -<code><a href="{@docRoot}reference/com/google/android/gms/location/LocationRequest.html#setFastestInterval(long)">LocationRequest.setFastestInterval()</a></code>, - then your app gets the same update rate as the fastest rate in the system. If other - apps have requested a faster rate, you get the benefit of a faster rate. If no other - apps have a faster rate request outstanding, your app receives updates at the rate you specified - with -<code><a href="{@docRoot}reference/com/google/android/gms/location/LocationRequest.html#setInterval(long)">LocationRequest.setInterval()</a></code>. - </p> - </dd> -</dl> -<p> - Next, set the accuracy parameter. In a foreground app, you need constant location updates with - high accuracy, so use the setting -<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> + +<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> -public class MainActivity extends FragmentActivity implements - GooglePlayServicesClient.ConnectionCallbacks, - GooglePlayServicesClient.OnConnectionFailedListener, - LocationListener { - ... - // 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; - ... - @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); - ... - } - ... +@Override +protected void onPause() { + super.onPause(); + stopLocationUpdates(); +} + +protected void stopLocationUpdates() { + LocationServices.FusedLocationApi.removeLocationUpdates( + mGoogleApiClient, this); } </pre> -<p class="note"> - <strong>Note:</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 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> -<!-- Start Location Updates --> -<h2 id="StartUpdates">Start Location Updates</h2> -<p> - To send the request for location updates, create a location client in - {@link android.support.v4.app.FragmentActivity#onCreate onCreate()}, then connect it and make - the request by calling -<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>. - Since your client must be connected for your app to receive updates, you should - connect the client in - {@link android.support.v4.app.FragmentActivity#onStart onStart()}. This ensures that you always - 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 -<code><a href="{@docRoot}reference/com/google/android/gms/common/GooglePlayServicesClient.ConnectionCallbacks.html#onConnected(android.os.Bundle)">ConnectionCallbacks.onConnected()</a></code> -</p> -<p> - Remember that the user may want to turn off location updates for various reasons. You should - provide a way for the user to do this, and you should ensure that you don't start updates in - {@link android.support.v4.app.FragmentActivity#onStart onStart()} if updates were previously - turned off. To track the user's preference, store it in your app's - {@link android.content.SharedPreferences} in - {@link android.support.v4.app.FragmentActivity#onPause onPause()} and retrieve it in - {@link android.support.v4.app.FragmentActivity#onResume onResume()}. -</p> -<p> - The following snippet shows how to set up the client in - {@link android.support.v4.app.FragmentActivity#onCreate onCreate()}, and how to connect it - and request updates in {@link android.support.v4.app.FragmentActivity#onStart onStart()}: -</p> + +<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> -public class MainActivity extends FragmentActivity implements - GooglePlayServicesClient.ConnectionCallbacks, - GooglePlayServicesClient.OnConnectionFailedListener, - LocationListener { - ... - // Global variables - ... - LocationClient mLocationClient; - boolean mUpdatesRequested; - ... - @Override - protected void onCreate(Bundle savedInstanceState) { - ... - // Open the shared preferences - mPrefs = getSharedPreferences("SharedPreferences", - 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; - ... - } - ... - @Override - protected void onPause() { - // Save the current setting for updates - mEditor.putBoolean("KEY_UPDATES_ON", mUpdatesRequested); - mEditor.commit(); - super.onPause(); - } - ... - @Override - protected void onStart() { - ... - mLocationClient.connect(); +@Override +public void onResume() { + super.onResume(); + if (mGoogleApiClient.isConnected() && !mRequestingLocationUpdates) { + startLocationUpdates(); } - ... - @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 - } else { - mEditor.putBoolean("KEY_UPDATES_ON", false); - 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 - */ - @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> -<p> - For more information about saving preferences, read -<a href="{@docRoot}training/basics/data-storage/shared-preferences.html">Saving Key-Value Sets</a>. -</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> + +<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 class MainActivity extends FragmentActivity implements - GooglePlayServicesClient.ConnectionCallbacks, - GooglePlayServicesClient.OnConnectionFailedListener, - LocationListener { +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> +@Override +public void onCreate(Bundle savedInstanceState) { ... - /* - * Called when the Activity is no longer visible at all. - * Stop updates and disconnect. - */ - @Override - protected void onStop() { - // If the client is connected - if (mLocationClient.isConnected()) { - /* - * Remove location updates for a listener. - * The current Activity is the listener, so - * the argument is "this". - */ - removeLocationUpdates(this); + 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); } - /* - * After disconnect() is called, the client is - * considered "dead". - */ - mLocationClient.disconnect(); - super.onStop(); + updateUI(); } - ... } </pre> -<p> - You now have the basic structure of an app that requests and receives periodic location updates. - 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> -<p> - The next lesson, <a href="display-address.html">Displaying a Location Address</a>, shows you how - to use the current location to display the current street address. -</p> + +<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> |