• 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, 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&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 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&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 &lt;service&gt;} 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&#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>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>
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 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> &ndash; 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> &ndash; 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> &ndash; 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> &ndash; 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&#64;Override
229protected 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&lt;Address&gt; 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&lt;String&gt; addressFragments = new ArrayList&lt;String&gt;();
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>
309public 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>
351public 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
368explicit intent when starting a {@link android.app.Service} and do not declare intent filters for
369your services. Using an implicit intent to start a service is a security hazard because you cannot
370be certain of the service that will respond to the intent, and the user cannot see which service
371starts.</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>
381public 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>
405public class MainActivity extends ActionBarActivity implements
406        ConnectionCallbacks, OnConnectionFailedListener {
407    ...
408    &#64;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>
452public 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        &#64;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