1page.title=Displaying a Location Address 2trainingnavtop=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, you need to 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>In order for 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 recreated, for example 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 defining the intent service:</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, 143 that is, your reverse 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 are used to adjust the presentation of information, such as numbers or 150 dates, to suit the conventions in the region 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.</p> 154 155<pre> 156@Override 157protected 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>The next step is to 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> 173public 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 you want returned. In this case, you want just one 190 address. The geocoder returns an array of addresses. If no addresses were 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. If an error 195 occurs, place the corresponding error message in the {@code errorMessage} 196 variable, so you can send it back to the requesting activity:</p> 197 198<ul> 199 <li><strong>No location data provided</strong> - The intent extras do not 200 include the {@link android.location.Location} object required for reverse 201 geocoding.</li> 202 <li><strong>Invalid latitude or longitude used</strong> - The latitude 203 and/or longitude values provided in the {@link android.location.Location} 204 object are invalid.</li> 205 <li><strong>No geocoder available</strong> - The background geocoding service 206 is not available, due to a network error or IO exception.</li> 207 <li><strong>Sorry, no address found</strong> - The geocoder could not find an 208 address for the given latitude/longitude.</li> 209</ul> 210 211<p>To get the individual lines of an address object, use the 212 {@link android.location.Address#getAddressLine getAddressLine()} 213 method provided by the {@link android.location.Address} class. Then join the 214 lines into a list of address fragments ready to return to the activity that 215 requested the address.</p> 216 217<p>To send the results back to the requesting activity, call the 218 {@code deliverResultToReceiver()} method (defined in 219 <a href="#return-address">Return the address to the requestor</a>). The 220 results consist of the previously-mentioned numeric success/failure code and 221 a string. In the case of a successful reverse geocoding, the string contains 222 the address. In the case of a failure, the string contains the error message, 223 as shown in the code sample below:</p> 224 225<pre> 226@Override 227protected void onHandleIntent(Intent intent) { 228 String errorMessage = ""; 229 230 // Get the location passed to this service through an extra. 231 Location location = intent.getParcelableExtra( 232 Constants.LOCATION_DATA_EXTRA); 233 234 ... 235 236 List<Address> addresses = null; 237 238 try { 239 addresses = geocoder.getFromLocation( 240 location.getLatitude(), 241 location.getLongitude(), 242 // In this sample, get just a single address. 243 1); 244 } catch (IOException ioException) { 245 // Catch network or other I/O problems. 246 errorMessage = getString(R.string.service_not_available); 247 Log.e(TAG, errorMessage, ioException); 248 } catch (IllegalArgumentException illegalArgumentException) { 249 // Catch invalid latitude or longitude values. 250 errorMessage = getString(R.string.invalid_lat_long_used); 251 Log.e(TAG, errorMessage + ". " + 252 "Latitude = " + location.getLatitude() + 253 ", Longitude = " + 254 location.getLongitude(), illegalArgumentException); 255 } 256 257 // Handle case where no address was found. 258 if (addresses == null || addresses.size() == 0) { 259 if (errorMessage.isEmpty()) { 260 errorMessage = getString(R.string.no_address_found); 261 Log.e(TAG, errorMessage); 262 } 263 deliverResultToReceiver(Constants.FAILURE_RESULT, errorMessage); 264 } else { 265 Address address = addresses.get(0); 266 ArrayList<String> addressFragments = new ArrayList<String>(); 267 268 // Fetch the address lines using {@code getAddressLine}, 269 // join them, and send them to the thread. 270 for(int i = 0; i < address.getMaxAddressLineIndex(); i++) { 271 addressFragments.add(address.getAddressLine(i)); 272 } 273 Log.i(TAG, getString(R.string.address_found)); 274 deliverResultToReceiver(Constants.SUCCESS_RESULT, 275 TextUtils.join(System.getProperty("line.separator"), 276 addressFragments)); 277 } 278} 279</pre> 280 281<h3 id="return-address">Return the address to the requestor</h3> 282 283<p>The final thing the intent service must do is send the address back to a 284 {@link android.os.ResultReceiver} in the activity that started the service. 285 The {@link android.os.ResultReceiver} class allows you to send a 286 numeric result code as well as a message containing the result data. The 287 numeric code is useful for reporting the success or failure of the geocoding 288 request. In the case of a successful reverse geocoding, the message contains 289 the address. In the case of a failure, the message contains some text 290 describing the reason for failure.</p> 291 292<p>You have already retrieved the address from the geocoder, trapped any errors 293 that may occur, and called the {@code deliverResultToReceiver()} method. Now 294 you need to define the {@code deliverResultToReceiver()} method that sends 295 a result code and message bundle to the result receiver.</p> 296 297<p>For the result code, use the value that you've passed to the 298 {@code deliverResultToReceiver()} method in the {@code resultCode} parameter. 299 To construct the message bundle, concatenate the {@code RESULT_DATA_KEY} 300 constant from your {@code Constants} class (defined in 301 <a href="#retrieve-street-address">Retrieve the street address data</a>) and 302 the value in the {@code message} parameter passed to the 303 {@code deliverResultToReceiver()} method, as shown in the following sample: 304</p> 305 306<pre> 307public class FetchAddressIntentService extends IntentService { 308 protected ResultReceiver mReceiver; 309 ... 310 private void deliverResultToReceiver(int resultCode, String message) { 311 Bundle bundle = new Bundle(); 312 bundle.putString(Constants.RESULT_DATA_KEY, message); 313 mReceiver.send(resultCode, bundle); 314 } 315} 316</pre> 317 318<h2 id="start-intent">Start the Intent Service</h2> 319 320<p>The intent service, as defined in the previous section, runs in the 321 background and is responsible for fetching the address corresponding to a 322 given geographic location. When you start the service, the Android framework 323 instantiates and starts the service if it isn't already running, and creates a 324 process if needed. If the service is already running then it remains running. 325 Because the service extends {@link android.app.IntentService IntentService}, 326 it shuts down automatically when all intents have been processed.</p> 327 328<p>Start the service from your app's main activity, 329 and create an {@link android.content.Intent} to pass data to the service. You 330 need an <em>explicit</em> intent, because you want only your service 331 to respond to the intent. For more information, see 332 <a href="{@docRoot}guide/components/intents-filters.html#Types">Intent 333 Types</a>.</p> 334 335<p>To create an explicit intent, specify the name of the 336 class to use for the service: {@code FetchAddressIntentService.class}. 337 Pass two pieces of information in the intent extras:</p> 338 339<ul> 340 <li>A {@link android.os.ResultReceiver} to handle the results of the address 341 lookup.</li> 342 <li>A {@link android.location.Location} object containing the latitude and 343 longitude that you want to convert to an address.</li> 344</ul> 345 346<p>The following code sample shows you how to start the intent service:</p> 347 348<pre> 349public class MainActivity extends ActionBarActivity implements 350 ConnectionCallbacks, OnConnectionFailedListener { 351 352 protected Location mLastLocation; 353 private AddressResultReceiver mResultReceiver; 354 ... 355 356 protected void startIntentService() { 357 Intent intent = new Intent(this, FetchAddressIntentService.class); 358 intent.putExtra(Constants.RECEIVER, mResultReceiver); 359 intent.putExtra(Constants.LOCATION_DATA_EXTRA, mLastLocation); 360 startService(intent); 361 } 362} 363</pre> 364 365<p>Call the above {@code startIntentService()} method when the 366 user takes an action that requires a geocoding address lookup. For example, 367 the user may press a <em>Fetch address</em> button on your app's UI. Before 368 starting the intent service, you need to check that the connection to Google 369 Play services is present. The following code snippet shows the call to the 370 {@code startIntentService()} method in the button handler:</p> 371 372<pre> 373public void fetchAddressButtonHandler(View view) { 374 // Only start the service to fetch the address if GoogleApiClient is 375 // connected. 376 if (mGoogleApiClient.isConnected() && mLastLocation != null) { 377 startIntentService(); 378 } 379 // If GoogleApiClient isn't connected, process the user's request by 380 // setting mAddressRequested to true. Later, when GoogleApiClient connects, 381 // launch the service to fetch the address. As far as the user is 382 // concerned, pressing the Fetch Address button 383 // immediately kicks off the process of getting the address. 384 mAddressRequested = true; 385 updateUIWidgets(); 386} 387</pre> 388 389<p>You must also start the intent service when the connection to Google Play 390 services is established, if the user has already clicked the button on your 391 app's UI. The following code snippet shows the call to the 392 {@code startIntentService()} method in the 393 <a href="{@docRoot}reference/com/google/android/gms/common/api/GoogleApiClient.ConnectionCallbacks.html#onConnected(android.os.Bundle)">{@code onConnected()}</a> 394 callback provided by the Google API Client:</p> 395 396<pre> 397public class MainActivity extends ActionBarActivity implements 398 ConnectionCallbacks, OnConnectionFailedListener { 399 ... 400 @Override 401 public void onConnected(Bundle connectionHint) { 402 // Gets the best and most recent location currently available, 403 // which may be null in rare cases when a location is not available. 404 mLastLocation = LocationServices.FusedLocationApi.getLastLocation( 405 mGoogleApiClient); 406 407 if (mLastLocation != null) { 408 // Determine whether a Geocoder is available. 409 if (!Geocoder.isPresent()) { 410 Toast.makeText(this, R.string.no_geocoder_available, 411 Toast.LENGTH_LONG).show(); 412 return; 413 } 414 415 if (mAddressRequested) { 416 startIntentService(); 417 } 418 } 419 } 420} 421</pre> 422 423<h2 id="result-receiver">Receive the Geocoding Results</h2> 424 425<p>The intent service has handled the geocoding request, and uses a 426 {@link android.os.ResultReceiver} to return the results to the activity that 427 made the request. In the activity that makes the request, define an 428 {@code AddressResultReceiver} that extends {@link android.os.ResultReceiver} 429 to handle the response from {@code FetchAddressIntentService}.</p> 430 431<p>The result includes a numeric result code (<code>resultCode</code>) as well 432 as a message containing the result data (<code>resultData</code>). If the 433 reverse geocoding process was successful, the <code>resultData</code> contains 434 the address. In the case of a failure, the <code>resultData</code> contains 435 text describing the reason for failure. For details of the possible errors, 436 see <a href="#return-address">Return the address to the requestor</a>.</p> 437 438<p>Override the 439 {@link android.os.ResultReceiver#onReceiveResult onReceiveResult()} method 440 to handle the results delivered to the result receiver, as shown in the 441 following code sample:</p> 442 443<pre> 444public class MainActivity extends ActionBarActivity implements 445 ConnectionCallbacks, OnConnectionFailedListener { 446 ... 447 class AddressResultReceiver extends ResultReceiver { 448 public AddressResultReceiver(Handler handler) { 449 super(handler); 450 } 451 452 @Override 453 protected void onReceiveResult(int resultCode, Bundle resultData) { 454 455 // Display the address string 456 // or an error message sent from the intent service. 457 mAddressOutput = resultData.getString(Constants.RESULT_DATA_KEY); 458 displayAddressOutput(); 459 460 // Show a toast message if an address was found. 461 if (resultCode == Constants.SUCCESS_RESULT) { 462 showToast(getString(R.string.address_found)); 463 } 464 465 } 466 } 467} 468</pre> 469