1 page.title=Displaying a Location Address 2 trainingnavtop=true 3 @jd:body 4 5 <div id="tb-wrapper"> 6 <div id="tb"> 7 8 <h2>This lesson teaches you how to</h2> 9 <ol> 10 <li><a href="#connect">Get a geographic location</a></li> 11 <li><a href="#fetch-address">Define an intent service to fetch the 12 address</a></li> 13 <li><a href="#start-intent">Start the intent service</a></li> 14 <li><a href="#result-receiver">Receive the geocoding results</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">Setting up Google 21 Play Services</a> 22 </li> 23 <li> 24 <a href="retrieve-current.html">Getting the Last Known Location</a> 25 </li> 26 <li> 27 <a href="receive-location-updates.html">Receiving Location Updates</a> 28 </li> 29 </ul> 30 <h2>Try it out</h2> 31 32 <ul> 33 <li> 34 <a href="https://github.com/googlesamples/android-play-location/tree/master/LocationAddress" class="external-link">LocationAddress</a> 35 </li> 36 </ul> 37 </div> 38 </div> 39 40 <p>The lessons <a href="retrieve-current.html">Getting the Last Known 41 Location</a> and <a href="receive-location-updates.html">Receiving Location 42 Updates</a> describe how to get the user's location in the form of a 43 {@link android.location.Location} object that contains latitude and longitude 44 coordinates. Although latitude and longitude are useful for calculating 45 distance or displaying a map position, in many cases the address of the 46 location is more useful. For example, if you want to let your users know where 47 they are or what is close by, a street address is more meaningful than the 48 geographic coordinates (latitude/longitude) of the location.</p> 49 50 <p>Using the {@link android.location.Geocoder} class in the Android framework 51 location APIs, you can convert an address to the corresponding geographic 52 coordinates. This process is called <em>geocoding</em>. Alternatively, you can 53 convert a geographic location to an address. The address lookup feature is 54 also known as <em>reverse geocoding</em>.</p> 55 56 <p>This lesson shows you how to use the 57 {@link android.location.Geocoder#getFromLocation getFromLocation()} method to 58 convert a geographic location to an address. The method returns an estimated 59 street address corresponding to a given latitude and longitude.</p> 60 61 <h2 id="connect">Get a geographic location</h2> 62 63 <p>The last known location of the device is a useful starting point for the 64 address lookup feature. The lesson on 65 <a href="retrieve-current.html">Getting the Last Known Location</a> shows you 66 how to use the 67 <a href="{@docRoot}reference/com/google/android/gms/location/FusedLocationProviderApi.html#getLastLocation(com.google.android.gms.common.api.GoogleApiClient)">{@code getLastLocation()}</a> 68 method provided by the 69 <a href="{@docRoot}reference/com/google/android/gms/location/FusedLocationProviderApi.html">fused 70 location provider</a> to find the latest location of the device.</p> 71 72 <p>To access the fused location provider, create an instance of the 73 Google Play services API client. To learn how to connect your client, see 74 <a href="{@docRoot}training/location/retrieve-current.html#play-services">Connect 75 to Google Play Services</a>.</p> 76 77 <p>To enable the fused location provider to retrieve a precise street 78 address, set the location permission in your app manifest to 79 {@code ACCESS_FINE_LOCATION}, as shown in the following example:</p> 80 81 <pre> 82 <manifest xmlns:android="http://schemas.android.com/apk/res/android" 83 package="com.google.android.gms.location.sample.locationupdates" > 84 85 <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/> 86 </manifest> 87 </pre> 88 89 <h2 id="fetch-address">Define an intent service to fetch the address</h2> 90 91 <p>The {@link android.location.Geocoder#getFromLocation getFromLocation()} 92 method provided by the {@link android.location.Geocoder} class accepts a 93 latitude and longitude and returns a list of addresses. The method is 94 synchronous and may take a long time to do its work, so you should not call 95 it from the main, user interface (UI) thread of your app.</p> 96 97 <p>The {@link android.app.IntentService IntentService} class provides a 98 structure for running a task on a background thread. Using this class, you can 99 handle a long-running operation without affecting your UI's responsiveness. 100 Note that the {@link android.os.AsyncTask AsyncTask} class also allows you to 101 perform background operations, but it's designed for short operations. An 102 {@link android.os.AsyncTask AsyncTask} shouldn't keep a reference to the UI if 103 the activity is re-created, such as when the device is rotated. In 104 contrast, an {@link android.app.IntentService IntentService} doesn't need to 105 be cancelled when the activity is rebuilt.</p> 106 107 <p>Define a {@code FetchAddressIntentService} class that extends 108 {@link android.app.IntentService}. This class is your address lookup service. 109 The intent service handles an intent asynchronously on a worker thread and 110 stops itself when it runs out of work. The intent extras provide the data 111 needed by the service, including a {@link android.location.Location} object 112 for conversion to an address and a {@link android.os.ResultReceiver} object 113 to handle the results of the address lookup. The service uses a {@link 114 android.location.Geocoder} to fetch the address for the location and sends 115 the results to the {@link android.os.ResultReceiver}.</p> 116 117 <h3>Define the intent service in your app manifest</h3> 118 119 <p>Add an entry to your app manifest that defines the intent service, as shown here:</p> 120 121 <pre> 122 <manifest xmlns:android="http://schemas.android.com/apk/res/android" 123 package="com.google.android.gms.location.sample.locationaddress" > 124 <application 125 ... 126 <service 127 android:name=".FetchAddressIntentService" 128 android:exported="false"/> 129 </application> 130 ... 131 </manifest> 132 </pre> 133 134 <p class="note"><strong>Note:</strong> The {@code <service>} element in 135 the manifest doesn't need to include an intent filter because your main 136 activity creates an explicit intent by specifying the name of the class to use 137 for the intent.</p> 138 139 <h3>Create a geocoder</h3> 140 141 <p>The process of converting a geographic location to an address is called 142 <em>reverse geocoding</em>. To perform the main work of the intent service (your reverse 143 geocoding request), implement 144 {@link android.app.IntentService#onHandleIntent onHandleIntent()} within the 145 {@code FetchAddressIntentService} class. Create a 146 {@link android.location.Geocoder} object to handle the reverse geocoding.</p> 147 148 <p>A locale represents a specific geographical or linguistic region. Locale 149 objects adjust the presentation of information, such as numbers or 150 dates, to suit the conventions in the region that is represented by the locale. Pass a 151 <a href="{@docRoot}reference/java/util/Locale.html">{@code Locale}</a> object 152 to the {@link android.location.Geocoder} object to ensure that the resulting 153 address is localized to the user's geographic region. Here is an example:</p> 154 155 <pre> 156 @Override 157 protected void onHandleIntent(Intent intent) { 158 Geocoder geocoder = new Geocoder(this, Locale.getDefault()); 159 ... 160 } 161 </pre> 162 163 <h3 id="retrieve-street-address">Retrieve the street address data</h3> 164 165 <p>You can now retrieve the street address from the geocoder, handle 166 any errors that may occur, and send the results back to the activity that 167 requested the address. To report the results of the geocoding 168 process, you need two numeric constants that indicate success or failure. 169 Define a {@code Constants} class to contain the values, as shown in this code 170 snippet:</p> 171 172 <pre> 173 public final class Constants { 174 public static final int SUCCESS_RESULT = 0; 175 public static final int FAILURE_RESULT = 1; 176 public static final String PACKAGE_NAME = 177 "com.google.android.gms.location.sample.locationaddress"; 178 public static final String RECEIVER = PACKAGE_NAME + ".RECEIVER"; 179 public static final String RESULT_DATA_KEY = PACKAGE_NAME + 180 ".RESULT_DATA_KEY"; 181 public static final String LOCATION_DATA_EXTRA = PACKAGE_NAME + 182 ".LOCATION_DATA_EXTRA"; 183 } 184 </pre> 185 186 <p>To get a street address corresponding to a geographical location, call 187 {@link android.location.Geocoder#getFromLocation getFromLocation()}, 188 passing it the latitude and longitude from the location object and the 189 maximum number of addresses that you want returned. In this case, you want just one 190 address. The geocoder returns an array of addresses. If no addresses are 191 found to match the given location, it returns an empty list. If there is no 192 backend geocoding service available, the geocoder returns null.</p> 193 194 <p>Check for the following errors, as shown in the code sample below:</p> 195 196 <ul> 197 <li><strong>No location data provided</strong> – The intent extras do not 198 include the {@link android.location.Location} object that is required for reverse 199 geocoding.</li> 200 <li><strong>Invalid latitude or longitude used</strong> – The latitude 201 and/or longitude values that are provided in the {@link android.location.Location} 202 object are invalid.</li> 203 <li><strong>No geocoder available</strong> – The background geocoding service 204 is not available due to a network error or IO exception.</li> 205 <li><strong>Sorry, no address found</strong> – The geocoder can't find an 206 address for the given latitude/longitude.</li> 207 </ul> 208 209 <p>If an error 210 occurs, place the corresponding error message in the {@code errorMessage} 211 variable so that you can send it back to the requesting activity. 212 213 <p>To get the individual lines of an address object, use the 214 {@link android.location.Address#getAddressLine getAddressLine()} 215 method that is provided by the {@link android.location.Address} class. Join the 216 lines into a list of address fragments ready to return to the activity that 217 requested the address.</p> 218 219 <p>To send the results back to the requesting activity, call the 220 {@code deliverResultToReceiver()} method (defined in 221 <a href="#return-address">Return the address to the requestor</a>). The 222 results consist of the previously-mentioned numeric success/failure code and 223 a string. In the case of a successful reverse geocoding, the string contains 224 the address. In the case of a failure, the string contains the error message, 225 as shown in this code sample:</p> 226 227 <pre> 228 @Override 229 protected void onHandleIntent(Intent intent) { 230 String errorMessage = ""; 231 232 // Get the location passed to this service through an extra. 233 Location location = intent.getParcelableExtra( 234 Constants.LOCATION_DATA_EXTRA); 235 236 ... 237 238 List<Address> addresses = null; 239 240 try { 241 addresses = geocoder.getFromLocation( 242 location.getLatitude(), 243 location.getLongitude(), 244 // In this sample, get just a single address. 245 1); 246 } catch (IOException ioException) { 247 // Catch network or other I/O problems. 248 errorMessage = getString(R.string.service_not_available); 249 Log.e(TAG, errorMessage, ioException); 250 } catch (IllegalArgumentException illegalArgumentException) { 251 // Catch invalid latitude or longitude values. 252 errorMessage = getString(R.string.invalid_lat_long_used); 253 Log.e(TAG, errorMessage + ". " + 254 "Latitude = " + location.getLatitude() + 255 ", Longitude = " + 256 location.getLongitude(), illegalArgumentException); 257 } 258 259 // Handle case where no address was found. 260 if (addresses == null || addresses.size() == 0) { 261 if (errorMessage.isEmpty()) { 262 errorMessage = getString(R.string.no_address_found); 263 Log.e(TAG, errorMessage); 264 } 265 deliverResultToReceiver(Constants.FAILURE_RESULT, errorMessage); 266 } else { 267 Address address = addresses.get(0); 268 ArrayList<String> addressFragments = new ArrayList<String>(); 269 270 // Fetch the address lines using {@code getAddressLine}, 271 // join them, and send them to the thread. 272 for(int i = 0; i < address.getMaxAddressLineIndex(); i++) { 273 addressFragments.add(address.getAddressLine(i)); 274 } 275 Log.i(TAG, getString(R.string.address_found)); 276 deliverResultToReceiver(Constants.SUCCESS_RESULT, 277 TextUtils.join(System.getProperty("line.separator"), 278 addressFragments)); 279 } 280 } 281 </pre> 282 283 <h3 id="return-address">Return the address to the requestor</h3> 284 285 <p>The final action that the intent service must complete is sending the address back to a 286 {@link android.os.ResultReceiver} in the activity that started the service. 287 The {@link android.os.ResultReceiver} class allows you to send a 288 numeric result code as well as a message containing the result data. The 289 numeric code is useful for reporting the success or failure of the geocoding 290 request. In the case of a successful reverse geocoding, the message contains 291 the address. In the case of a failure, the message contains some text 292 describing the reason for the failure.</p> 293 294 <p>You have already retrieved the address from the geocoder, trapped any errors 295 that may occur, and called the {@code deliverResultToReceiver()} method, so now 296 you must define the {@code deliverResultToReceiver()} method that sends 297 a result code and message bundle to the result receiver.</p> 298 299 <p>For the result code, use the value that you've passed to the 300 {@code deliverResultToReceiver()} method in the {@code resultCode} parameter. 301 To construct the message bundle, concatenate the {@code RESULT_DATA_KEY} 302 constant from your {@code Constants} class (defined in 303 <a href="#retrieve-street-address">Retrieve the street address data</a>) and 304 the value in the {@code message} parameter that is passed to the 305 {@code deliverResultToReceiver()} method, as shown in the following sample: 306 </p> 307 308 <pre> 309 public class FetchAddressIntentService extends IntentService { 310 protected ResultReceiver mReceiver; 311 ... 312 private void deliverResultToReceiver(int resultCode, String message) { 313 Bundle bundle = new Bundle(); 314 bundle.putString(Constants.RESULT_DATA_KEY, message); 315 mReceiver.send(resultCode, bundle); 316 } 317 } 318 </pre> 319 320 <h2 id="start-intent">Start the intent service</h2> 321 322 <p>The intent service, as defined in the previous section, runs in the 323 background and fetches the address corresponding to a 324 given geographic location. When you start the service, the Android framework 325 instantiates and starts the service if it isn't already running, and it creates a 326 process if needed. If the service is already running, it remains running. 327 Because the service extends {@link android.app.IntentService IntentService}, 328 it shuts down automatically after all intents are processed.</p> 329 330 <p>Start the service from your app's main activity 331 and create an {@link android.content.Intent} to pass data to the service. You 332 need an <em>explicit</em> intent because you want only your service 333 to respond to the intent. For more information, see 334 <a href="{@docRoot}guide/components/intents-filters.html#Types">Intent 335 Types</a>.</p> 336 337 <p>To create an explicit intent, specify the name of the 338 class to use for the service: {@code FetchAddressIntentService.class}. 339 Pass this information in the intent extras:</p> 340 341 <ul> 342 <li>A {@link android.os.ResultReceiver} to handle the results of the address 343 lookup.</li> 344 <li>A {@link android.location.Location} object containing the latitude and 345 longitude that you want to convert to an address.</li> 346 </ul> 347 348 <p>The following code sample shows you how to start the intent service:</p> 349 350 <pre> 351 public class MainActivity extends ActionBarActivity implements 352 ConnectionCallbacks, OnConnectionFailedListener { 353 354 protected Location mLastLocation; 355 private AddressResultReceiver mResultReceiver; 356 ... 357 358 protected void startIntentService() { 359 Intent intent = new Intent(this, FetchAddressIntentService.class); 360 intent.putExtra(Constants.RECEIVER, mResultReceiver); 361 intent.putExtra(Constants.LOCATION_DATA_EXTRA, mLastLocation); 362 startService(intent); 363 } 364 } 365 </pre> 366 367 <p class="caution"><strong>Caution</strong>: To ensure that your app is secure, always use an 368 explicit intent when starting a {@link android.app.Service} and do not declare intent filters for 369 your services. Using an implicit intent to start a service is a security hazard because you cannot 370 be certain of the service that will respond to the intent, and the user cannot see which service 371 starts.</p> 372 373 <p>Call the above {@code startIntentService()} method when the 374 user takes an action that requires a geocoding address lookup. For example, 375 the user may press a <em>Fetch address</em> button on your app's UI. Before 376 starting the intent service, you need to check that the connection to Google 377 Play services is present. The following code snippet shows the call to the 378 {@code startIntentService()} method in the button handler:</p> 379 380 <pre> 381 public void fetchAddressButtonHandler(View view) { 382 // Only start the service to fetch the address if GoogleApiClient is 383 // connected. 384 if (mGoogleApiClient.isConnected() && mLastLocation != null) { 385 startIntentService(); 386 } 387 // If GoogleApiClient isn't connected, process the user's request by 388 // setting mAddressRequested to true. Later, when GoogleApiClient connects, 389 // launch the service to fetch the address. As far as the user is 390 // concerned, pressing the Fetch Address button 391 // immediately kicks off the process of getting the address. 392 mAddressRequested = true; 393 updateUIWidgets(); 394 } 395 </pre> 396 397 <p>You must also start the intent service when the connection to Google Play 398 services is established, if the user has already clicked the button on your 399 app's UI. The following code snippet shows the call to the 400 {@code startIntentService()} method in the 401 <a href="{@docRoot}reference/com/google/android/gms/common/api/GoogleApiClient.ConnectionCallbacks.html#onConnected(android.os.Bundle)">{@code onConnected()}</a> 402 callback that is provided by the Google API Client:</p> 403 404 <pre> 405 public class MainActivity extends ActionBarActivity implements 406 ConnectionCallbacks, OnConnectionFailedListener { 407 ... 408 @Override 409 public void onConnected(Bundle connectionHint) { 410 // Gets the best and most recent location currently available, 411 // which may be null in rare cases when a location is not available. 412 mLastLocation = LocationServices.FusedLocationApi.getLastLocation( 413 mGoogleApiClient); 414 415 if (mLastLocation != null) { 416 // Determine whether a Geocoder is available. 417 if (!Geocoder.isPresent()) { 418 Toast.makeText(this, R.string.no_geocoder_available, 419 Toast.LENGTH_LONG).show(); 420 return; 421 } 422 423 if (mAddressRequested) { 424 startIntentService(); 425 } 426 } 427 } 428 } 429 </pre> 430 431 <h2 id="result-receiver">Receive the geocoding results</h2> 432 433 <p>After the intent service handles the geocoding request, it uses a 434 {@link android.os.ResultReceiver} to return the results to the activity that 435 made the request. In the activity that makes the request, define an 436 {@code AddressResultReceiver} that extends {@link android.os.ResultReceiver} 437 to handle the response from {@code FetchAddressIntentService}.</p> 438 439 <p>The result includes a numeric result code (<code>resultCode</code>) as well 440 as a message containing the result data (<code>resultData</code>). If the 441 reverse geocoding process is successful, the <code>resultData</code> contains 442 the address. In the case of a failure, the <code>resultData</code> contains 443 text describing the reason for the failure. For details of the possible errors, 444 see <a href="#return-address">Return the address to the requestor</a>.</p> 445 446 <p>Override the 447 {@link android.os.ResultReceiver#onReceiveResult onReceiveResult()} method 448 to handle the results that are delivered to the result receiver, as shown in the 449 following code sample:</p> 450 451 <pre> 452 public class MainActivity extends ActionBarActivity implements 453 ConnectionCallbacks, OnConnectionFailedListener { 454 ... 455 class AddressResultReceiver extends ResultReceiver { 456 public AddressResultReceiver(Handler handler) { 457 super(handler); 458 } 459 460 @Override 461 protected void onReceiveResult(int resultCode, Bundle resultData) { 462 463 // Display the address string 464 // or an error message sent from the intent service. 465 mAddressOutput = resultData.getString(Constants.RESULT_DATA_KEY); 466 displayAddressOutput(); 467 468 // Show a toast message if an address was found. 469 if (resultCode == Constants.SUCCESS_RESULT) { 470 showToast(getString(R.string.address_found)); 471 } 472 473 } 474 } 475 } 476 </pre> 477