1 page.title=Receiving Location Updates 2 trainingnavtop=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"><manifest></a></code> 72 element: 73 </p> 74 <pre> 75 <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/> 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> 111 public 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 @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 @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> 241 public 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 @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 @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 @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> 324 public class MainActivity extends FragmentActivity implements 325 GooglePlayServicesClient.ConnectionCallbacks, 326 GooglePlayServicesClient.OnConnectionFailedListener, 327 LocationListener { 328 ... 329 // Define the callback method that receives location updates 330 @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> 407 public 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 @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> 482 public 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 @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 @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 @Override 519 protected void onStart() { 520 ... 521 mLocationClient.connect(); 522 } 523 ... 524 @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 @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> 574 public 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 @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