| page.title=Displaying a Location Address |
| trainingnavtop=true |
| @jd:body |
| |
| <div id="tb-wrapper"> |
| <div id="tb"> |
| |
| <h2>This lesson teaches you how to</h2> |
| <ol> |
| <li><a href="#connect">Get a Geographic Location</a></li> |
| <li><a href="#fetch-address">Define an Intent Service to Fetch the |
| Address</a></li> |
| <li><a href="#start-intent">Start the Intent Service</a></li> |
| <li><a href="#result-receiver">Receive the Geocoding Results</a></li> |
| </ol> |
| |
| <h2>You should also read</h2> |
| <ul> |
| <li> |
| <a href="{@docRoot}google/play-services/setup.html">Setting up Google |
| Play Services</a> |
| </li> |
| <li> |
| <a href="retrieve-current.html">Getting the Last Known Location</a> |
| </li> |
| <li> |
| <a href="receive-location-updates.html">Receiving Location Updates</a> |
| </li> |
| </ul> |
| <h2>Try it out</h2> |
| |
| <ul> |
| <li> |
| <a href="https://github.com/googlesamples/android-play-location/tree/master/LocationAddress" class="external-link">LocationAddress</a> |
| </li> |
| </ul> |
| </div> |
| </div> |
| |
| <p>The lessons <a href="retrieve-current.html">Getting the Last Known |
| Location</a> and <a href="receive-location-updates.html">Receiving Location |
| Updates</a> describe how to get the user's location in the form of a |
| {@link android.location.Location} object that contains latitude and longitude |
| coordinates. Although latitude and longitude are useful for calculating |
| distance or displaying a map position, in many cases the address of the |
| location is more useful. For example, if you want to let your users know where |
| they are or what is close by, a street address is more meaningful than the |
| geographic coordinates (latitude/longitude) of the location.</p> |
| |
| <p>Using the {@link android.location.Geocoder} class in the Android framework |
| location APIs, you can convert an address to the corresponding geographic |
| coordinates. This process is called <em>geocoding</em>. Alternatively, you can |
| convert a geographic location to an address. The address lookup feature is |
| also known as <em>reverse geocoding</em>.</p> |
| |
| <p>This lesson shows you how to use the |
| {@link android.location.Geocoder#getFromLocation getFromLocation()} method to |
| convert a geographic location to an address. The method returns an estimated |
| street address corresponding to a given latitude and longitude.</p> |
| |
| <h2 id="connect">Get a Geographic Location</h2> |
| |
| <p>The last known location of the device is a useful starting point for the |
| address lookup feature. The lesson on |
| <a href="retrieve-current.html">Getting the Last Known Location</a> shows you |
| how to use the |
| <a href="{@docRoot}reference/com/google/android/gms/location/FusedLocationProviderApi.html#getLastLocation(com.google.android.gms.common.api.GoogleApiClient)">{@code getLastLocation()}</a> |
| method provided by the |
| <a href="{@docRoot}reference/com/google/android/gms/location/FusedLocationProviderApi.html">fused |
| location provider</a> to find the latest location of the device.</p> |
| |
| <p>To access the fused location provider, you need to create an instance of the |
| Google Play services API client. To learn how to connect your client, see |
| <a href="{@docRoot}training/location/retrieve-current.html#play-services">Connect |
| to Google Play Services</a>.</p> |
| |
| <p>In order for the fused location provider to retrieve a precise street |
| address, set the location permission in your app manifest to |
| {@code ACCESS_FINE_LOCATION}, as shown in the following example:</p> |
| |
| <pre> |
| <manifest xmlns:android="http://schemas.android.com/apk/res/android" |
| package="com.google.android.gms.location.sample.locationupdates" > |
| |
| <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/> |
| </manifest> |
| </pre> |
| |
| <h2 id="fetch-address">Define an Intent Service to Fetch the Address</h2> |
| |
| <p>The {@link android.location.Geocoder#getFromLocation getFromLocation()} |
| method provided by the {@link android.location.Geocoder} class accepts a |
| latitude and longitude, and returns a list of addresses. The method is |
| synchronous, and may take a long time to do its work, so you should not call |
| it from the main, user interface (UI) thread of your app.</p> |
| |
| <p>The {@link android.app.IntentService IntentService} class provides a |
| structure for running a task on a background thread. Using this class, you can |
| handle a long-running operation without affecting your UI's responsiveness. |
| Note that the {@link android.os.AsyncTask AsyncTask} class also allows you to |
| perform background operations, but it's designed for short operations. An |
| {@link android.os.AsyncTask AsyncTask} shouldn't keep a reference to the UI if |
| the activity is recreated, for example when the device is rotated. In |
| contrast, an {@link android.app.IntentService IntentService} doesn't need to |
| be cancelled when the activity is rebuilt.</p> |
| |
| <p>Define a {@code FetchAddressIntentService} class that extends |
| {@link android.app.IntentService}. This class is your address lookup service. |
| The intent service handles an intent asynchronously on a worker thread, and |
| stops itself when it runs out of work. The intent extras provide the data |
| needed by the service, including a {@link android.location.Location} object |
| for conversion to an address, and a {@link android.os.ResultReceiver} object |
| to handle the results of the address lookup. The service uses a {@link |
| android.location.Geocoder} to fetch the address for the location, and sends |
| the results to the {@link android.os.ResultReceiver}.</p> |
| |
| <h3>Define the Intent Service in your App Manifest</h3> |
| |
| <p>Add an entry to your app manifest defining the intent service:</p> |
| |
| <pre> |
| <manifest xmlns:android="http://schemas.android.com/apk/res/android" |
| package="com.google.android.gms.location.sample.locationaddress" > |
| <application |
| ... |
| <service |
| android:name=".FetchAddressIntentService" |
| android:exported="false"/> |
| </application> |
| ... |
| </manifest> |
| </pre> |
| |
| <p class="note"><strong>Note:</strong> The {@code <service>} element in |
| the manifest doesn't need to include an intent filter, because your main |
| activity creates an explicit intent by specifying the name of the class to use |
| for the intent.</p> |
| |
| <h3>Create a Geocoder</h3> |
| |
| <p>The process of converting a geographic location to an address is called |
| <em>reverse geocoding</em>. To perform the main work of the intent service, |
| that is, your reverse geocoding request, implement |
| {@link android.app.IntentService#onHandleIntent onHandleIntent()} within the |
| {@code FetchAddressIntentService} class. Create a |
| {@link android.location.Geocoder} object to handle the reverse geocoding.</p> |
| |
| <p>A locale represents a specific geographical or linguistic region. Locale |
| objects are used to adjust the presentation of information, such as numbers or |
| dates, to suit the conventions in the region represented by the locale. Pass a |
| <a href="{@docRoot}reference/java/util/Locale.html">{@code Locale}</a> object |
| to the {@link android.location.Geocoder} object, to ensure that the resulting |
| address is localized to the user's geographic region.</p> |
| |
| <pre> |
| @Override |
| protected void onHandleIntent(Intent intent) { |
| Geocoder geocoder = new Geocoder(this, Locale.getDefault()); |
| ... |
| } |
| </pre> |
| |
| <h3 id="retrieve-street-address">Retrieve the street address data</h3> |
| |
| <p>The next step is to retrieve the street address from the geocoder, handle |
| any errors that may occur, and send the results back to the activity that |
| requested the address. To report the results of the geocoding |
| process, you need two numeric constants that indicate success or failure. |
| Define a {@code Constants} class to contain the values, as shown in this code |
| snippet:</p> |
| |
| <pre> |
| public final class Constants { |
| public static final int SUCCESS_RESULT = 0; |
| public static final int FAILURE_RESULT = 1; |
| public static final String PACKAGE_NAME = |
| "com.google.android.gms.location.sample.locationaddress"; |
| public static final String RECEIVER = PACKAGE_NAME + ".RECEIVER"; |
| public static final String RESULT_DATA_KEY = PACKAGE_NAME + |
| ".RESULT_DATA_KEY"; |
| public static final String LOCATION_DATA_EXTRA = PACKAGE_NAME + |
| ".LOCATION_DATA_EXTRA"; |
| } |
| </pre> |
| |
| <p>To get a street address corresponding to a geographical location, call |
| {@link android.location.Geocoder#getFromLocation getFromLocation()}, |
| passing it the latitude and longitude from the location object, and the |
| maximum number of addresses you want returned. In this case, you want just one |
| address. The geocoder returns an array of addresses. If no addresses were |
| found to match the given location, it returns an empty list. If there is no |
| backend geocoding service available, the geocoder returns null.</p> |
| |
| <p>Check for the following errors as shown in the code sample below. If an error |
| occurs, place the corresponding error message in the {@code errorMessage} |
| variable, so you can send it back to the requesting activity:</p> |
| |
| <ul> |
| <li><strong>No location data provided</strong> - The intent extras do not |
| include the {@link android.location.Location} object required for reverse |
| geocoding.</li> |
| <li><strong>Invalid latitude or longitude used</strong> - The latitude |
| and/or longitude values provided in the {@link android.location.Location} |
| object are invalid.</li> |
| <li><strong>No geocoder available</strong> - The background geocoding service |
| is not available, due to a network error or IO exception.</li> |
| <li><strong>Sorry, no address found</strong> - The geocoder could not find an |
| address for the given latitude/longitude.</li> |
| </ul> |
| |
| <p>To get the individual lines of an address object, use the |
| {@link android.location.Address#getAddressLine getAddressLine()} |
| method provided by the {@link android.location.Address} class. Then join the |
| lines into a list of address fragments ready to return to the activity that |
| requested the address.</p> |
| |
| <p>To send the results back to the requesting activity, call the |
| {@code deliverResultToReceiver()} method (defined in |
| <a href="#return-address">Return the address to the requestor</a>). The |
| results consist of the previously-mentioned numeric success/failure code and |
| a string. In the case of a successful reverse geocoding, the string contains |
| the address. In the case of a failure, the string contains the error message, |
| as shown in the code sample below:</p> |
| |
| <pre> |
| @Override |
| protected void onHandleIntent(Intent intent) { |
| String errorMessage = ""; |
| |
| // Get the location passed to this service through an extra. |
| Location location = intent.getParcelableExtra( |
| Constants.LOCATION_DATA_EXTRA); |
| |
| ... |
| |
| List<Address> addresses = null; |
| |
| try { |
| addresses = geocoder.getFromLocation( |
| location.getLatitude(), |
| location.getLongitude(), |
| // In this sample, get just a single address. |
| 1); |
| } catch (IOException ioException) { |
| // Catch network or other I/O problems. |
| errorMessage = getString(R.string.service_not_available); |
| Log.e(TAG, errorMessage, ioException); |
| } catch (IllegalArgumentException illegalArgumentException) { |
| // Catch invalid latitude or longitude values. |
| errorMessage = getString(R.string.invalid_lat_long_used); |
| Log.e(TAG, errorMessage + ". " + |
| "Latitude = " + location.getLatitude() + |
| ", Longitude = " + |
| location.getLongitude(), illegalArgumentException); |
| } |
| |
| // Handle case where no address was found. |
| if (addresses == null || addresses.size() == 0) { |
| if (errorMessage.isEmpty()) { |
| errorMessage = getString(R.string.no_address_found); |
| Log.e(TAG, errorMessage); |
| } |
| deliverResultToReceiver(Constants.FAILURE_RESULT, errorMessage); |
| } else { |
| Address address = addresses.get(0); |
| ArrayList<String> addressFragments = new ArrayList<String>(); |
| |
| // Fetch the address lines using {@code getAddressLine}, |
| // join them, and send them to the thread. |
| for(int i = 0; i < address.getMaxAddressLineIndex(); i++) { |
| addressFragments.add(address.getAddressLine(i)); |
| } |
| Log.i(TAG, getString(R.string.address_found)); |
| deliverResultToReceiver(Constants.SUCCESS_RESULT, |
| TextUtils.join(System.getProperty("line.separator"), |
| addressFragments)); |
| } |
| } |
| </pre> |
| |
| <h3 id="return-address">Return the address to the requestor</h3> |
| |
| <p>The final thing the intent service must do is send the address back to a |
| {@link android.os.ResultReceiver} in the activity that started the service. |
| The {@link android.os.ResultReceiver} class allows you to send a |
| numeric result code as well as a message containing the result data. The |
| numeric code is useful for reporting the success or failure of the geocoding |
| request. In the case of a successful reverse geocoding, the message contains |
| the address. In the case of a failure, the message contains some text |
| describing the reason for failure.</p> |
| |
| <p>You have already retrieved the address from the geocoder, trapped any errors |
| that may occur, and called the {@code deliverResultToReceiver()} method. Now |
| you need to define the {@code deliverResultToReceiver()} method that sends |
| a result code and message bundle to the result receiver.</p> |
| |
| <p>For the result code, use the value that you've passed to the |
| {@code deliverResultToReceiver()} method in the {@code resultCode} parameter. |
| To construct the message bundle, concatenate the {@code RESULT_DATA_KEY} |
| constant from your {@code Constants} class (defined in |
| <a href="#retrieve-street-address">Retrieve the street address data</a>) and |
| the value in the {@code message} parameter passed to the |
| {@code deliverResultToReceiver()} method, as shown in the following sample: |
| </p> |
| |
| <pre> |
| public class FetchAddressIntentService extends IntentService { |
| protected ResultReceiver mReceiver; |
| ... |
| private void deliverResultToReceiver(int resultCode, String message) { |
| Bundle bundle = new Bundle(); |
| bundle.putString(Constants.RESULT_DATA_KEY, message); |
| mReceiver.send(resultCode, bundle); |
| } |
| } |
| </pre> |
| |
| <h2 id="start-intent">Start the Intent Service</h2> |
| |
| <p>The intent service, as defined in the previous section, runs in the |
| background and is responsible for fetching the address corresponding to a |
| given geographic location. When you start the service, the Android framework |
| instantiates and starts the service if it isn't already running, and creates a |
| process if needed. If the service is already running then it remains running. |
| Because the service extends {@link android.app.IntentService IntentService}, |
| it shuts down automatically when all intents have been processed.</p> |
| |
| <p>Start the service from your app's main activity, |
| and create an {@link android.content.Intent} to pass data to the service. You |
| need an <em>explicit</em> intent, because you want only your service |
| to respond to the intent. For more information, see |
| <a href="{@docRoot}guide/components/intents-filters.html#Types">Intent |
| Types</a>.</p> |
| |
| <p>To create an explicit intent, specify the name of the |
| class to use for the service: {@code FetchAddressIntentService.class}. |
| Pass two pieces of information in the intent extras:</p> |
| |
| <ul> |
| <li>A {@link android.os.ResultReceiver} to handle the results of the address |
| lookup.</li> |
| <li>A {@link android.location.Location} object containing the latitude and |
| longitude that you want to convert to an address.</li> |
| </ul> |
| |
| <p>The following code sample shows you how to start the intent service:</p> |
| |
| <pre> |
| public class MainActivity extends ActionBarActivity implements |
| ConnectionCallbacks, OnConnectionFailedListener { |
| |
| protected Location mLastLocation; |
| private AddressResultReceiver mResultReceiver; |
| ... |
| |
| protected void startIntentService() { |
| Intent intent = new Intent(this, FetchAddressIntentService.class); |
| intent.putExtra(Constants.RECEIVER, mResultReceiver); |
| intent.putExtra(Constants.LOCATION_DATA_EXTRA, mLastLocation); |
| startService(intent); |
| } |
| } |
| </pre> |
| |
| <p>Call the above {@code startIntentService()} method when the |
| user takes an action that requires a geocoding address lookup. For example, |
| the user may press a <em>Fetch address</em> button on your app's UI. Before |
| starting the intent service, you need to check that the connection to Google |
| Play services is present. The following code snippet shows the call to the |
| {@code startIntentService()} method in the button handler:</p> |
| |
| <pre> |
| public void fetchAddressButtonHandler(View view) { |
| // Only start the service to fetch the address if GoogleApiClient is |
| // connected. |
| if (mGoogleApiClient.isConnected() && mLastLocation != null) { |
| startIntentService(); |
| } |
| // If GoogleApiClient isn't connected, process the user's request by |
| // setting mAddressRequested to true. Later, when GoogleApiClient connects, |
| // launch the service to fetch the address. As far as the user is |
| // concerned, pressing the Fetch Address button |
| // immediately kicks off the process of getting the address. |
| mAddressRequested = true; |
| updateUIWidgets(); |
| } |
| </pre> |
| |
| <p>You must also start the intent service when the connection to Google Play |
| services is established, if the user has already clicked the button on your |
| app's UI. The following code snippet shows the call to the |
| {@code startIntentService()} method in the |
| <a href="{@docRoot}reference/com/google/android/gms/common/api/GoogleApiClient.ConnectionCallbacks.html#onConnected(android.os.Bundle)">{@code onConnected()}</a> |
| callback provided by the Google API Client:</p> |
| |
| <pre> |
| public class MainActivity extends ActionBarActivity implements |
| ConnectionCallbacks, OnConnectionFailedListener { |
| ... |
| @Override |
| public void onConnected(Bundle connectionHint) { |
| // Gets the best and most recent location currently available, |
| // which may be null in rare cases when a location is not available. |
| mLastLocation = LocationServices.FusedLocationApi.getLastLocation( |
| mGoogleApiClient); |
| |
| if (mLastLocation != null) { |
| // Determine whether a Geocoder is available. |
| if (!Geocoder.isPresent()) { |
| Toast.makeText(this, R.string.no_geocoder_available, |
| Toast.LENGTH_LONG).show(); |
| return; |
| } |
| |
| if (mAddressRequested) { |
| startIntentService(); |
| } |
| } |
| } |
| } |
| </pre> |
| |
| <h2 id="result-receiver">Receive the Geocoding Results</h2> |
| |
| <p>The intent service has handled the geocoding request, and uses a |
| {@link android.os.ResultReceiver} to return the results to the activity that |
| made the request. In the activity that makes the request, define an |
| {@code AddressResultReceiver} that extends {@link android.os.ResultReceiver} |
| to handle the response from {@code FetchAddressIntentService}.</p> |
| |
| <p>The result includes a numeric result code (<code>resultCode</code>) as well |
| as a message containing the result data (<code>resultData</code>). If the |
| reverse geocoding process was successful, the <code>resultData</code> contains |
| the address. In the case of a failure, the <code>resultData</code> contains |
| text describing the reason for failure. For details of the possible errors, |
| see <a href="#return-address">Return the address to the requestor</a>.</p> |
| |
| <p>Override the |
| {@link android.os.ResultReceiver#onReceiveResult onReceiveResult()} method |
| to handle the results delivered to the result receiver, as shown in the |
| following code sample:</p> |
| |
| <pre> |
| public class MainActivity extends ActionBarActivity implements |
| ConnectionCallbacks, OnConnectionFailedListener { |
| ... |
| class AddressResultReceiver extends ResultReceiver { |
| public AddressResultReceiver(Handler handler) { |
| super(handler); |
| } |
| |
| @Override |
| protected void onReceiveResult(int resultCode, Bundle resultData) { |
| |
| // Display the address string |
| // or an error message sent from the intent service. |
| mAddressOutput = resultData.getString(Constants.RESULT_DATA_KEY); |
| displayAddressOutput(); |
| |
| // Show a toast message if an address was found. |
| if (resultCode == Constants.SUCCESS_RESULT) { |
| showToast(getString(R.string.address_found)); |
| } |
| |
| } |
| } |
| } |
| </pre> |