• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1page.title=Receiving Location Updates
2trainingnavtop=true
3@jd:body
4<div id="tb-wrapper">
5<div id="tb">
6
7<h2>This lesson teaches you to</h2>
8<ol>
9    <li><a href="#Permissions">Request Location Permission</a></li>
10    <li><a href="#PlayServices">Check for Google Play Services</a></li>
11    <li><a href="#DefineCallbacks">Define Location Services Callbacks</a></li>
12    <li><a href="#UpdateParameters">Specify Update Parameters</a></li>
13    <li><a href="#StartUpdates">Start Location Updates</a></li>
14    <li><a href="#StopUpdates">Stop Location Updates</a></li>
15</ol>
16
17<h2>You should also read</h2>
18<ul>
19    <li>
20        <a href="{@docRoot}google/play-services/setup.html">Setup Google Play Services SDK</a>
21    </li>
22    <li>
23        <a href="retrieve-current.html">Retrieving the Current Location</a>
24    </li>
25 </ul>
26
27<h2>Try it out</h2>
28
29<div class="download-box">
30  <a href="http://developer.android.com/shareables/training/LocationUpdates.zip" class="button">Download the sample</a>
31  <p class="filename">LocationUpdates.zip</p>
32</div>
33
34</div>
35</div>
36
37<p>
38    If your app does navigation or tracking, you probably want to get the user's
39    location at regular intervals. While you can do this with
40<code><a href="{@docRoot}reference/com/google/android/gms/location/LocationClient.html#getLastLocation()">LocationClient.getLastLocation()</a></code>,
41    a more direct approach is to request periodic updates from Location Services. In
42    response, Location Services automatically updates your app with the best available location,
43    based on the currently-available location providers such as WiFi and GPS.
44</p>
45<p>
46    To get periodic location updates from Location Services, you send a request using a location
47    client. Depending on the form of the request, Location Services either invokes a callback
48    method and passes in a {@link android.location.Location} object, or issues an
49    {@link android.content.Intent} that contains the location in its extended data. The accuracy and
50    frequency of the updates are affected by the location permissions you've requested and the
51    parameters you pass to Location Services with the request.
52</p>
53<!-- Request permission -->
54<h2 id="Permissions">Specify App Permissions</h2>
55<p>
56    Apps that use Location Services must request location permissions. Android has two location
57    permissions, {@link android.Manifest.permission#ACCESS_COARSE_LOCATION ACCESS_COARSE_LOCATION}
58    and {@link android.Manifest.permission#ACCESS_FINE_LOCATION ACCESS_FINE_LOCATION}. The
59    permission you choose affects the accuracy of the location updates you receive.
60    For example, If you request only coarse location permission, Location Services obfuscates the
61    updated location to an accuracy that's roughly equivalent to a city block.
62</p>
63<p>
64    Requesting {@link android.Manifest.permission#ACCESS_FINE_LOCATION ACCESS_FINE_LOCATION} implies
65    a request for {@link android.Manifest.permission#ACCESS_COARSE_LOCATION ACCESS_COARSE_LOCATION}.
66</p>
67<p>
68    For example, to add the coarse location permission to your manifest, insert the following as a
69    child element of
70    the
71<code><a href="{@docRoot}guide/topics/manifest/manifest-element.html">&lt;manifest&gt;</a></code>
72    element:
73</p>
74<pre>
75&lt;uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/&gt;
76</pre>
77<!-- Check for Google Play services -->
78<h2 id="PlayServices">Check for Google Play Services</h2>
79<p>
80    Location Services is part of the Google Play services APK. Since it's hard to anticipate the
81    state of the user's device, you should always check that the APK is installed before you attempt
82    to connect to Location Services. To check that the APK is installed, call
83<code><a href="{@docRoot}reference/com/google/android/gms/common/GooglePlayServicesUtil.html#isGooglePlayServicesAvailable(android.content.Context)">GooglePlayServicesUtil.isGooglePlayServicesAvailable()</a></code>,
84    which returns one of the
85    integer result codes listed in the API reference documentation. If you encounter an error,
86    call
87<code><a href="{@docRoot}reference/com/google/android/gms/common/GooglePlayServicesUtil.html#getErrorDialog(int, android.app.Activity, int)">GooglePlayServicesUtil.getErrorDialog()</a></code>
88    to retrieve localized dialog that prompts users to take the correct action, then display
89    the dialog in a {@link android.support.v4.app.DialogFragment}. The dialog may allow the
90    user to correct the problem, in which case Google Play services may send a result back to your
91    activity. To handle this result, override the method
92    {@link android.support.v4.app.FragmentActivity#onActivityResult onActivityResult()}
93
94</p>
95<p class="note">
96    <strong>Note:</strong> To make your app compatible with
97    platform version 1.6 and later, the activity that displays the
98    {@link android.support.v4.app.DialogFragment} must subclass
99    {@link android.support.v4.app.FragmentActivity} instead of {@link android.app.Activity}. Using
100    {@link android.support.v4.app.FragmentActivity} also allows you to call
101    {@link android.support.v4.app.FragmentActivity#getSupportFragmentManager
102    getSupportFragmentManager()} to display the {@link android.support.v4.app.DialogFragment}.
103</p>
104<p>
105    Since you usually need to check for Google Play services in more than one place in your code,
106    define a method that encapsulates the check, then call the method before each connection
107    attempt. The following snippet contains all of the code required to check for Google
108    Play services:
109</p>
110<pre>
111public class MainActivity extends FragmentActivity {
112    ...
113    // Global constants
114    /*
115     * Define a request code to send to Google Play services
116     * This code is returned in Activity.onActivityResult
117     */
118    private final static int
119            CONNECTION_FAILURE_RESOLUTION_REQUEST = 9000;
120    ...
121    // Define a DialogFragment that displays the error dialog
122    public static class ErrorDialogFragment extends DialogFragment {
123        // Global field to contain the error dialog
124        private Dialog mDialog;
125        // Default constructor. Sets the dialog field to null
126        public ErrorDialogFragment() {
127            super();
128            mDialog = null;
129        }
130        // Set the dialog to display
131        public void setDialog(Dialog dialog) {
132            mDialog = dialog;
133        }
134        // Return a Dialog to the DialogFragment.
135        &#64;Override
136        public Dialog onCreateDialog(Bundle savedInstanceState) {
137            return mDialog;
138        }
139    }
140    ...
141    /*
142     * Handle results returned to the FragmentActivity
143     * by Google Play services
144     */
145    &#64;Override
146    protected void onActivityResult(
147            int requestCode, int resultCode, Intent data) {
148        // Decide what to do based on the original request code
149        switch (requestCode) {
150            ...
151            case CONNECTION_FAILURE_RESOLUTION_REQUEST :
152            /*
153             * If the result code is Activity.RESULT_OK, try
154             * to connect again
155             */
156                switch (resultCode) {
157                    case Activity.RESULT_OK :
158                    /*
159                     * Try the request again
160                     */
161                    ...
162                    break;
163                }
164            ...
165        }
166        ...
167    }
168    ...
169    private boolean servicesConnected() {
170        // Check that Google Play services is available
171        int resultCode =
172                GooglePlayServicesUtil.
173                        isGooglePlayServicesAvailable(this);
174        // If Google Play services is available
175        if (ConnectionResult.SUCCESS == resultCode) {
176            // In debug mode, log the status
177            Log.d("Location Updates",
178                    "Google Play services is available.");
179            // Continue
180            return true;
181        // Google Play services was not available for some reason
182        } else {
183            // Get the error code
184            int errorCode = connectionResult.getErrorCode();
185            // Get the error dialog from Google Play services
186            Dialog errorDialog = GooglePlayServicesUtil.getErrorDialog(
187                    errorCode,
188                    this,
189                    CONNECTION_FAILURE_RESOLUTION_REQUEST);
190            // If Google Play services can provide an error dialog
191            if (errorDialog != null) {
192                // Create a new DialogFragment for the error dialog
193                ErrorDialogFragment errorFragment =
194                        new ErrorDialogFragment();
195                // Set the dialog in the DialogFragment
196                errorFragment.setDialog(errorDialog);
197                // Show the error dialog in the DialogFragment
198                errorFragment.show(
199                        getSupportFragmentManager(),
200                        "Location Updates");
201            }
202        }
203    }
204    ...
205}
206</pre>
207<p>
208    Snippets in the following sections call this method to verify that Google Play services is
209    available.
210</p>
211<!--
212    Define Location Services Callbacks
213 -->
214<h2 id="DefineCallbacks">Define Location Services Callbacks</h2>
215<p>
216    Before you request location updates, you must first implement the interfaces that Location
217    Services uses to communicate connection status to your app:
218</p>
219<dl>
220    <dt>
221<code><a href="{@docRoot}reference/com/google/android/gms/common/GooglePlayServicesClient.ConnectionCallbacks.html">ConnectionCallbacks</a></code>
222    </dt>
223    <dd>
224        Specifies methods that Location Services calls when a location client is connected or
225        disconnected.
226    </dd>
227    <dt>
228<code><a href="{@docRoot}reference/com/google/android/gms/common/GooglePlayServicesClient.OnConnectionFailedListener.html">OnConnectionFailedListener</a></code>
229    </dt>
230    <dd>
231        Specifies a method that Location Services calls if an error occurs while attempting to
232        connect the location client. This method uses the previously-defined {@code showErrorDialog}
233        method to display an error dialog that attempts to fix the problem using Google Play
234        services.
235    </dd>
236</dl>
237<p>
238    The following snippet shows how to specify the interfaces and define the methods:
239</p>
240<pre>
241public class MainActivity extends FragmentActivity implements
242        GooglePlayServicesClient.ConnectionCallbacks,
243        GooglePlayServicesClient.OnConnectionFailedListener {
244    ...
245    /*
246     * Called by Location Services when the request to connect the
247     * client finishes successfully. At this point, you can
248     * request the current location or start periodic updates
249     */
250    &#64;Override
251    public void onConnected(Bundle dataBundle) {
252        // Display the connection status
253        Toast.makeText(this, "Connected", Toast.LENGTH_SHORT).show();
254    }
255    ...
256    /*
257     * Called by Location Services if the connection to the
258     * location client drops because of an error.
259     */
260    &#64;Override
261    public void onDisconnected() {
262        // Display the connection status
263        Toast.makeText(this, "Disconnected. Please re-connect.",
264                Toast.LENGTH_SHORT).show();
265    }
266    ...
267    /*
268     * Called by Location Services if the attempt to
269     * Location Services fails.
270     */
271    &#64;Override
272    public void onConnectionFailed(ConnectionResult connectionResult) {
273        /*
274         * Google Play services can resolve some errors it detects.
275         * If the error has a resolution, try sending an Intent to
276         * start a Google Play services activity that can resolve
277         * error.
278         */
279        if (connectionResult.hasResolution()) {
280            try {
281                // Start an Activity that tries to resolve the error
282                connectionResult.startResolutionForResult(
283                        this,
284                        CONNECTION_FAILURE_RESOLUTION_REQUEST);
285                /*
286                * Thrown if Google Play services canceled the original
287                * PendingIntent
288                */
289            } catch (IntentSender.SendIntentException e) {
290                // Log the error
291                e.printStackTrace();
292            }
293        } else {
294            /*
295             * If no resolution is available, display a dialog to the
296             * user with the error.
297             */
298            showErrorDialog(connectionResult.getErrorCode());
299        }
300    }
301    ...
302}
303</pre>
304<h3>Define the location update callback</h3>
305<p>
306    Location Services sends location updates to your app either as an {@link android.content.Intent}
307    or as an argument passed to a callback method you define. This lesson shows you how to get the
308    update using a callback method, because that pattern works best for most use cases. If you want
309    to receive updates in the form of an {@link android.content.Intent}, read the lesson
310    <a href="activity-recognition.html">Recognizing the User's Current Activity</a>, which
311    presents a similar pattern.
312</p>
313<p>
314    The callback method that Location Services invokes to send a location update to your app is
315    specified in the
316<code><a href="{@docRoot}reference/com/google/android/gms/location/LocationListener.html">LocationListener</a></code>
317    interface, in the method
318<code><a href="{@docRoot}reference/com/google/android/gms/location/LocationListener.html#onLocationChanged(android.location.Location)">onLocationChanged()</a></code>.
319    The incoming argument is a {@link android.location.Location} object containing the location's
320    latitude and longitude. The following snippet shows how to specify the interface and define
321    the method:
322</p>
323<pre>
324public class MainActivity extends FragmentActivity implements
325        GooglePlayServicesClient.ConnectionCallbacks,
326        GooglePlayServicesClient.OnConnectionFailedListener,
327        LocationListener {
328    ...
329    // Define the callback method that receives location updates
330    &#64;Override
331    public void onLocationChanged(Location location) {
332        // Report to the UI that the location was updated
333        String msg = "Updated Location: " +
334                Double.toString(location.getLatitude()) + "," +
335                Double.toString(location.getLongitude());
336        Toast.makeText(this, msg, Toast.LENGTH_SHORT).show();
337    }
338    ...
339}
340</pre>
341<p>
342    Now that you have the callbacks prepared, you can set up the request for location updates.
343    The first step is to specify the parameters that control the updates.
344</p>
345<!-- Specify update parameters -->
346<h2 id="UpdateParameters">Specify Update Parameters</h2>
347<p>
348    Location Services allows you to control the interval between updates and the location accuracy
349    you want, by setting the values in a
350<code><a href="{@docRoot}reference/com/google/android/gms/location/LocationRequest.html">LocationRequest</a></code>
351    object and then sending this object as part of your request to start updates.
352</p>
353<p>
354    First, set the following interval parameters:
355</p>
356<dl>
357    <dt>
358        Update interval
359    </dt>
360    <dd>
361        Set by
362<code><a href="{@docRoot}reference/com/google/android/gms/location/LocationRequest.html#setInterval(long)">LocationRequest.setInterval()</a></code>.
363        This method sets the rate in milliseconds at which your app prefers to receive location
364        updates. If no other apps are receiving updates from Location Services, your app will
365        receive updates at this rate.
366    </dd>
367    <dt>
368        Fastest update interval
369    </dt>
370    <dd>
371        Set by
372<code><a href="{@docRoot}reference/com/google/android/gms/location/LocationRequest.html#setFastestInterval(long)">LocationRequest.setFastestInterval()</a></code>.
373        This method sets the <b>fastest</b> rate in milliseconds at which your app can handle
374        location updates. You need to set this rate because other apps also affect the rate
375        at which updates are sent. Location Services sends out updates at the fastest rate that any
376        app requested by calling
377<code><a href="{@docRoot}reference/com/google/android/gms/location/LocationRequest.html#setInterval(long)">LocationRequest.setInterval()</a></code>.
378        If this rate is faster than your app can handle, you may encounter problems with UI flicker
379        or data overflow. To prevent this, call
380<code><a href="{@docRoot}reference/com/google/android/gms/location/LocationRequest.html#setFastestInterval(long)">LocationRequest.setFastestInterval()</a></code>
381        to set an upper limit to the update rate.
382        <p>
383            Calling
384<code><a href="{@docRoot}reference/com/google/android/gms/location/LocationRequest.html#setFastestInterval(long)">LocationRequest.setFastestInterval()</a></code>
385            also helps to save power. When you request a preferred update rate by calling
386<code><a href="{@docRoot}reference/com/google/android/gms/location/LocationRequest.html#setInterval(long)">LocationRequest.setInterval()</a></code>,
387            and a maximum rate by calling
388<code><a href="{@docRoot}reference/com/google/android/gms/location/LocationRequest.html#setFastestInterval(long)">LocationRequest.setFastestInterval()</a></code>,
389            then your app gets the same update rate as the fastest rate in the system. If other
390            apps have requested a faster rate, you get the benefit of a faster rate. If no other
391            apps have a faster rate request outstanding, your app receives updates at the rate you specified
392        with
393<code><a href="{@docRoot}reference/com/google/android/gms/location/LocationRequest.html#setInterval(long)">LocationRequest.setInterval()</a></code>.
394        </p>
395    </dd>
396</dl>
397<p>
398    Next, set the accuracy parameter. In a foreground app, you need constant location updates with
399    high accuracy, so use the setting
400<code><a href="{@docRoot}reference/com/google/android/gms/location/LocationRequest.html#PRIORITY_HIGH_ACCURACY">LocationRequest.PRIORITY_HIGH_ACCURACY</a></code>.
401</p>
402<p>
403    The following snippet shows how to set the update interval and accuracy in
404    {@link android.support.v4.app.FragmentActivity#onCreate onCreate()}:
405</p>
406<pre>
407public class MainActivity extends FragmentActivity implements
408        GooglePlayServicesClient.ConnectionCallbacks,
409        GooglePlayServicesClient.OnConnectionFailedListener,
410        LocationListener {
411    ...
412    // Global constants
413    ...
414    // Milliseconds per second
415    private static final int MILLISECONDS_PER_SECOND = 1000;
416    // Update frequency in seconds
417    public static final int UPDATE_INTERVAL_IN_SECONDS = 5;
418    // Update frequency in milliseconds
419    private static final long UPDATE_INTERVAL =
420            MILLISECONDS_PER_SECOND * UPDATE_INTERVAL_IN_SECONDS;
421    // The fastest update frequency, in seconds
422    private static final int FASTEST_INTERVAL_IN_SECONDS = 1;
423    // A fast frequency ceiling in milliseconds
424    private static final long FASTEST_INTERVAL =
425            MILLISECONDS_PER_SECOND * FASTEST_INTERVAL_IN_SECONDS;
426    ...
427    // Define an object that holds accuracy and frequency parameters
428    LocationRequest mLocationRequest;
429    ...
430    &#64;Override
431    protected void onCreate(Bundle savedInstanceState) {
432        super.onCreate(savedInstanceState);
433        // Create the LocationRequest object
434        mLocationRequest = LocationRequest.create();
435        // Use high accuracy
436        mLocationRequest.setPriority(
437                LocationRequest.PRIORITY_HIGH_ACCURACY);
438        // Set the update interval to 5 seconds
439        mLocationRequest.setInterval(UPDATE_INTERVAL);
440        // Set the fastest update interval to 1 second
441        mLocationRequest.setFastestInterval(FASTEST_INTERVAL);
442        ...
443    }
444    ...
445}
446</pre>
447<p class="note">
448   <strong>Note:</strong> If your app accesses the network or does other long-running work after
449   receiving a location update, adjust the fastest interval to a slower value. This prevents your
450   app from receiving updates it can't use. Once the long-running work is done, set the fastest
451   interval back to a fast value.
452</p>
453<!-- Start Location Updates -->
454<h2 id="StartUpdates">Start Location Updates</h2>
455<p>
456    To send the request for location updates, create a location client in
457    {@link android.support.v4.app.FragmentActivity#onCreate onCreate()}, then connect it and make
458    the request by calling
459<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>.
460    Since your client must be connected for your app to receive updates, you should
461    connect the client in
462    {@link android.support.v4.app.FragmentActivity#onStart onStart()}. This ensures that you always
463    have a valid, connected client while your app is visible. Since you need a connection before you
464    can request updates, make the update request in
465<code><a href="{@docRoot}reference/com/google/android/gms/common/GooglePlayServicesClient.ConnectionCallbacks.html#onConnected(android.os.Bundle)">ConnectionCallbacks.onConnected()</a></code>
466</p>
467<p>
468    Remember that the user may want to turn off location updates for various reasons. You should
469    provide a way for the user to do this, and you should ensure that you don't start updates in
470    {@link android.support.v4.app.FragmentActivity#onStart onStart()} if updates were previously
471    turned off. To track the user's preference, store it in your app's
472    {@link android.content.SharedPreferences} in
473    {@link android.support.v4.app.FragmentActivity#onPause onPause()} and retrieve it in
474    {@link android.support.v4.app.FragmentActivity#onResume onResume()}.
475</p>
476<p>
477    The following snippet shows how to set up the client in
478    {@link android.support.v4.app.FragmentActivity#onCreate onCreate()}, and how to connect it
479    and request updates in {@link android.support.v4.app.FragmentActivity#onStart onStart()}:
480</p>
481<pre>
482public class MainActivity extends FragmentActivity implements
483        GooglePlayServicesClient.ConnectionCallbacks,
484        GooglePlayServicesClient.OnConnectionFailedListener,
485        LocationListener {
486    ...
487    // Global variables
488    ...
489    LocationClient mLocationClient;
490    boolean mUpdatesRequested;
491    ...
492    &#64;Override
493    protected void onCreate(Bundle savedInstanceState) {
494        ...
495        // Open the shared preferences
496        mPrefs = getSharedPreferences("SharedPreferences",
497                Context.MODE_PRIVATE);
498        // Get a SharedPreferences editor
499        mEditor = mPrefs.edit();
500        /*
501         * Create a new location client, using the enclosing class to
502         * handle callbacks.
503         */
504        mLocationClient = new LocationClient(this, this, this);
505        // Start with updates turned off
506        mUpdatesRequested = false;
507        ...
508    }
509    ...
510    &#64;Override
511    protected void onPause() {
512        // Save the current setting for updates
513        mEditor.putBoolean("KEY_UPDATES_ON", mUpdatesRequested);
514        mEditor.commit();
515        super.onPause();
516    }
517    ...
518    &#64;Override
519    protected void onStart() {
520        ...
521        mLocationClient.connect();
522    }
523    ...
524    &#64;Override
525    protected void onResume() {
526        /*
527         * Get any previous setting for location updates
528         * Gets "false" if an error occurs
529         */
530        if (mPrefs.contains("KEY_UPDATES_ON")) {
531            mUpdatesRequested =
532                    mPrefs.getBoolean("KEY_UPDATES_ON", false);
533
534        // Otherwise, turn off location updates
535        } else {
536            mEditor.putBoolean("KEY_UPDATES_ON", false);
537            mEditor.commit();
538        }
539    }
540    ...
541    /*
542     * Called by Location Services when the request to connect the
543     * client finishes successfully. At this point, you can
544     * request the current location or start periodic updates
545     */
546    &#64;Override
547    public void onConnected(Bundle dataBundle) {
548        // Display the connection status
549        Toast.makeText(this, "Connected", Toast.LENGTH_SHORT).show();
550        // If already requested, start periodic updates
551        if (mUpdatesRequested) {
552            mLocationClient.requestLocationUpdates(mLocationRequest, this);
553        }
554    }
555    ...
556}
557</pre>
558<p>
559    For more information about saving preferences, read
560<a href="{@docRoot}training/basics/data-storage/shared-preferences.html">Saving Key-Value Sets</a>.
561</p>
562<!--
563    Stop Location Updates
564 -->
565<h2 id="StopUpdates">Stop Location Updates</h2>
566<p>
567    To stop location updates, save the state of the update flag in
568    {@link android.support.v4.app.FragmentActivity#onPause onPause()}, and stop updates in
569    {@link android.support.v4.app.FragmentActivity#onStop onStop()} by calling
570<code><a href="{@docRoot}reference/com/google/android/gms/location/LocationClient.html#removeLocationUpdates(com.google.android.gms.location.LocationListener)">removeLocationUpdates(LocationListener)</a></code>.
571    For example:
572</p>
573<pre>
574public class MainActivity extends FragmentActivity implements
575        GooglePlayServicesClient.ConnectionCallbacks,
576        GooglePlayServicesClient.OnConnectionFailedListener,
577        LocationListener {
578    ...
579    /*
580     * Called when the Activity is no longer visible at all.
581     * Stop updates and disconnect.
582     */
583    &#64;Override
584    protected void onStop() {
585        // If the client is connected
586        if (mLocationClient.isConnected()) {
587            /*
588             * Remove location updates for a listener.
589             * The current Activity is the listener, so
590             * the argument is "this".
591             */
592            removeLocationUpdates(this);
593        }
594        /*
595         * After disconnect() is called, the client is
596         * considered "dead".
597         */
598        mLocationClient.disconnect();
599        super.onStop();
600    }
601    ...
602}
603</pre>
604<p>
605    You now have the basic structure of an app that requests and receives periodic location updates.
606    You can combine the features described in this lesson with the geofencing, activity recognition,
607    or reverse geocoding features described in other lessons in this class.
608</p>
609<p>
610    The next lesson, <a href="display-address.html">Displaying a Location Address</a>, shows you how
611    to use the current location to display the current street address.
612</p>
613