• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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&lt;manifest xmlns:android="http://schemas.android.com/apk/res/android"
83    package="com.google.android.gms.location.sample.locationupdates" &gt;
84
85  &lt;uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/&gt;
86&lt;/manifest&gt;
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&lt;manifest xmlns:android="http://schemas.android.com/apk/res/android"
123    package="com.google.android.gms.location.sample.locationaddress" &gt;
124    &lt;application
125        ...
126        &lt;service
127            android:name=".FetchAddressIntentService"
128            android:exported="false"/&gt;
129    &lt;/application&gt;
130    ...
131&lt;/manifest&gt;
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&#64;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&#64;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&lt;Address&gt; 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&lt;String&gt; addressFragments = new ArrayList&lt;String&gt;();
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    &#64;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        &#64;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