summaryrefslogtreecommitdiffstats
path: root/docs/html/guide/topics/location/strategies.jd
blob: f5f61a0e968b467883624836a7a19108b9b5b2c2 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
page.title=Location Strategies
excludeFromSuggestions=true
page.tags="geolocation","maps","mapview"

@jd:body

<div id="tb-wrapper">
<div id="tb">
  <h2>In this document</h2>
  <ol>
    <li><a href="#Challenges">Challenges in Determining User Location</a></li>
    <li><a href="#Updates">Requesting Location Updates</a>
      <ol>
        <li><a href="#Permission">Requesting User Permissions</a></li>
      </ol>
    </li>
    <li><a href="#BestPerformance">Defining a Model for the Best Performance</a>
      <ol>
        <li><a href="#Flow">Flow for obtaining user location</a></li>
        <li><a href="#StartListening">Deciding when to start listening for updates</a></li>
        <li><a href="#FastFix">Getting a fast fix with the last known location</a></li>
        <li><a href="#StopListening">Deciding when to stop listening for updates</a></li>
        <li><a href="#BestEstimate">Maintaining a current best estimate</a></li>
        <li><a href="#Adjusting">Adjusting the model to save battery and data exchange</a></li>
      </ol>
    </li>
    <li><a href="#MockData">Providing Mock Location Data</a></li>
  </ol>
  <h2>Key classes</h2>
  <ol>
    <li>{@link android.location.LocationManager}</li>
    <li>{@link android.location.LocationListener}</li>
  </ol>
</div>
</div>
<div class="note">
<p>
    <strong>Note:</strong> The strategies described in this guide apply to the platform location
    API in {@link android.location}. The Google Location Services API, part of Google Play
    Services, provides a more powerful, high-level framework that automatically handles location
    providers, user movement, and location accuracy. It also handles
    location update scheduling based on power consumption parameters you provide. In most cases,
    you'll get better battery performance, as well as more appropriate accuracy, by using the
    Location Services API.
</p>
<p>
    To learn more about the Location Services API, see
    <a href="{@docRoot}google/play-services/location.html">Google Location Services for Android</a>.
</p>
</div>
  <p>Knowing where the user is allows your application to be smarter and deliver
better information to the user. When developing a location-aware application for Android, you can
utilize GPS and Android's Network Location Provider to acquire the user location. Although
GPS is most accurate, it only works outdoors, it quickly consumes battery power, and doesn't return
the location as quickly as users want. Android's Network Location Provider determines user location
using cell tower and Wi-Fi signals, providing location information in a way that
works indoors and outdoors, responds faster, and uses less battery power. To obtain the user
location in your application, you can use both GPS and the Network Location Provider, or just
one.</p>


<h2 id="Challenges">Challenges in Determining User Location</h2>

<p>Obtaining user location from a mobile device can be complicated. There are several reasons
why a location reading (regardless of the source) can contain errors and be inaccurate.
Some sources of error in the user location include:</p>

<ul>
  <li><b>Multitude of location sources</b>
    <p>GPS, Cell-ID, and Wi-Fi can each provide a clue to users location. Determining which to use
and trust is a matter of trade-offs in accuracy, speed, and battery-efficiency.</p>
  </li>
  <li><b>User movement</b>
    <p>Because the user location changes, you must account for movement by re-estimating user
location every so often.</p>
  </li>
  <li><b>Varying accuracy</b>
    <p>Location estimates coming from each location source are not consistent in their
accuracy. A location obtained 10 seconds ago from one source might be more accurate than the newest
location from another or same source.</p>
  </li>
</ul>

  <p>These problems can make it difficult to obtain a reliable user location reading. This
document provides information to help you meet these challenges to obtain a reliable location
reading. It also provides ideas that you can use in your
application to provide the user with an accurate and responsive geo-location experience.</p>


<h2 id="Updates">Requesting Location Updates</h2>

  <p>Before addressing some of the location errors described above, here is an introduction to
how you can obtain user location on Android.</p>

  <p>Getting user location in Android works by means of callback. You indicate that you'd
like to receive location updates from the {@link android.location.LocationManager} ("Location
Manager") by calling {@link android.location.LocationManager#requestLocationUpdates
requestLocationUpdates()}, passing it a
{@link android.location.LocationListener}. Your {@link android.location.LocationListener} must
implement several callback methods that the Location Manager calls when the user location
changes or when the status of the service changes.</p>

<p>For example, the following code shows how to define a {@link android.location.LocationListener}
and request location updates:
  </p>

<pre>
// Acquire a reference to the system Location Manager
LocationManager locationManager = (LocationManager) this.getSystemService(Context.LOCATION_SERVICE);

// Define a listener that responds to location updates
LocationListener locationListener = new LocationListener() {
    public void onLocationChanged(Location location) {
      // Called when a new location is found by the network location provider.
      makeUseOfNewLocation(location);
    }

    public void onStatusChanged(String provider, int status, Bundle extras) {}

    public void onProviderEnabled(String provider) {}

    public void onProviderDisabled(String provider) {}
  };

// Register the listener with the Location Manager to receive location updates
locationManager.requestLocationUpdates(LocationManager.NETWORK_PROVIDER, 0, 0, locationListener);
</pre>

  <p>The first parameter in {@link
android.location.LocationManager#requestLocationUpdates requestLocationUpdates()} is the type of
location provider to use (in this case, the Network Location Provider for cell tower and Wi-Fi
based location). You can control the frequency at which your listener receives updates
with the second and third parameter&mdash;the second is the minimum time interval between
notifications and the third is the minimum change in distance between notifications&mdash;setting
both to zero requests location notifications as frequently as possible. The last parameter is your
{@link android.location.LocationListener}, which receives callbacks for location updates.</p>

<p>To request location updates from the GPS provider,
substitute <code>GPS_PROVIDER</code> for <code>NETWORK_PROVIDER</code>. You can also request
location updates from both the GPS and the Network Location Provider by calling {@link
android.location.LocationManager#requestLocationUpdates requestLocationUpdates()} twice&mdash;once
for <code>NETWORK_PROVIDER</code> and once for <code>GPS_PROVIDER</code>.</p>


<h3 id="Permission">Requesting User Permissions</h3>

<p>In order to receive location updates from <code>NETWORK_PROVIDER</code> or
<code>GPS_PROVIDER</code>, you must request user permission by declaring either the {@code
ACCESS_COARSE_LOCATION} or {@code ACCESS_FINE_LOCATION} permission, respectively, in your Android
manifest file. For example:</p>

<pre>
&lt;manifest ... &gt;
    &lt;uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" /&gt;
    ...
&lt;/manifest&gt;
</pre>

<p>Without these permissions, your application will fail at runtime when requesting
location updates.</p>

<p class="note"><strong>Note:</strong> If you are using both <code>NETWORK_PROVIDER</code> and
<code>GPS_PROVIDER</code>, then you need to request only the {@code ACCESS_FINE_LOCATION}
permission, because it includes permission for both providers. (Permission for {@code
ACCESS_COARSE_LOCATION} includes permission only for <code>NETWORK_PROVIDER</code>.)</p>


<h2 id="BestPerformance">Defining a Model for the Best Performance</h2>

  <p>Location-based applications are now commonplace, but due to the less than optimal
accuracy, user movement, the multitude of methods to obtain the location, and the desire to conserve
battery, getting user location is complicated. To overcome the obstacles of obtaining a good user
location while preserving battery power, you must define a consistent model that specifies how your
application obtains the user location. This model includes when you start and stop listening for
updates and when to use cached location data.</p>


  <h3 id="Flow">Flow for obtaining user location</h3>

  <p>Here's the typical flow of procedures for obtaining the user location:</p>

  <ol>
    <li>Start application.</li>
    <li>Sometime later, start listening for updates from desired location providers.</li>
    <li>Maintain a "current best estimate" of location by filtering out new, but less accurate
fixes.</li>
    <li>Stop listening for location updates.</li>
    <li>Take advantage of the last best location estimate.</li>
  </ol>

  <p>Figure 1 demonstrates this model in a timeline that visualizes the period in which an
application is listening for location updates and the events that occur during that time.</p>

<img src="{@docRoot}images/location/getting-location.png" alt="" />
<p class="img-caption"><strong>Figure 1.</strong> A timeline representing the window in which an
application listens for location updates.</p>

  <p>This model of a window&mdash;during which location updates are received&mdash;frames many of
the decisions you need to make when adding location-based services to your application.</p>


  <h3 id="StartListening">Deciding when to start listening for updates</h3>

  <p>You might want to start listening for location updates as soon as your application starts, or
only after users activate a certain feature. Be aware that long windows of listening for location
fixes can consume a lot of battery power, but short periods might not allow for sufficient
accuracy.</p>

  <p>As demonstrated above, you can begin listening for updates by calling {@link
android.location.LocationManager#requestLocationUpdates requestLocationUpdates()}:</p>

<pre>
String locationProvider = LocationManager.NETWORK_PROVIDER;
// Or, use GPS location data:
// String locationProvider = LocationManager.GPS_PROVIDER;

locationManager.requestLocationUpdates(locationProvider, 0, 0, locationListener);
</pre>


  <h3 id="FastFix">Getting a fast fix with the last known location</h3>

  <p>The time it takes for your location listener to receive the first location fix is often too
long for users wait. Until a more accurate location is provided to your location listener, you
should utilize a cached location by calling {@link
android.location.LocationManager#getLastKnownLocation}:</p>
<pre>
String locationProvider = LocationManager.NETWORK_PROVIDER;
// Or use LocationManager.GPS_PROVIDER

Location lastKnownLocation = locationManager.getLastKnownLocation(locationProvider);
</pre>


  <h3 id="StopListening">Deciding when to stop listening for updates</h3>

  <p>The logic of deciding when new fixes are no longer necessary might range from very simple to
very complex depending on your application. A short gap between when the location is acquired and
when the location is used, improves the accuracy of the estimate. Always beware that listening for a
long time consumes a lot of battery power, so as soon as you have the information you need, you
should stop
listening for updates by calling {@link android.location.LocationManager#removeUpdates}:</p>
<pre>
// Remove the listener you previously added
locationManager.removeUpdates(locationListener);
</pre>


  <h3 id="BestEstimate">Maintaining a current best estimate</h3>

  <p>You might expect that the most recent location fix is the most accurate.
However, because the accuracy of a location fix varies, the most recent fix is not always the best.
You should include logic for choosing location fixes based on several criteria. The criteria also
varies depending on the use-cases of the application and field testing.</p>

  <p>Here are a few steps you can take to validate the accuracy of a location fix:</p>
  <ul>
    <li>Check if the location retrieved is significantly newer than the previous estimate.</li>
    <li>Check if the accuracy claimed by the location is better or worse than the previous
estimate.</li>
    <li>Check which provider the new location is from and determine if you trust it more.</li>
  </ul>

  <p>An elaborate example of this logic can look something like this:</p>

<pre>
private static final int TWO_MINUTES = 1000 * 60 * 2;

/** Determines whether one Location reading is better than the current Location fix
  * @param location  The new Location that you want to evaluate
  * @param currentBestLocation  The current Location fix, to which you want to compare the new one
  */
protected boolean isBetterLocation(Location location, Location currentBestLocation) {
    if (currentBestLocation == null) {
        // A new location is always better than no location
        return true;
    }

    // Check whether the new location fix is newer or older
    long timeDelta = location.getTime() - currentBestLocation.getTime();
    boolean isSignificantlyNewer = timeDelta &gt; TWO_MINUTES;
    boolean isSignificantlyOlder = timeDelta &lt; -TWO_MINUTES;
    boolean isNewer = timeDelta > 0;

    // If it's been more than two minutes since the current location, use the new location
    // because the user has likely moved
    if (isSignificantlyNewer) {
        return true;
    // If the new location is more than two minutes older, it must be worse
    } else if (isSignificantlyOlder) {
        return false;
    }

    // Check whether the new location fix is more or less accurate
    int accuracyDelta = (int) (location.getAccuracy() - currentBestLocation.getAccuracy());
    boolean isLessAccurate = accuracyDelta &gt; 0;
    boolean isMoreAccurate = accuracyDelta &lt; 0;
    boolean isSignificantlyLessAccurate = accuracyDelta &gt; 200;

    // Check if the old and new location are from the same provider
    boolean isFromSameProvider = isSameProvider(location.getProvider(),
            currentBestLocation.getProvider());

    // Determine location quality using a combination of timeliness and accuracy
    if (isMoreAccurate) {
        return true;
    } else if (isNewer &amp;&amp; !isLessAccurate) {
        return true;
    } else if (isNewer &amp;&amp; !isSignificantlyLessAccurate &amp;&amp; isFromSameProvider) {
        return true;
    }
    return false;
}

/** Checks whether two providers are the same */
private boolean isSameProvider(String provider1, String provider2) {
    if (provider1 == null) {
      return provider2 == null;
    }
    return provider1.equals(provider2);
}
</pre>


  <h3 id="Adjusting">Adjusting the model to save battery and data exchange</h3>

  <p>As you test your application, you might find that your model for providing good location and
good performance needs some adjustment. Here are some things you might change to find a good
balance between the two.</p>

  <h4>Reduce the size of the window</h4>

  <p>A smaller window in which you listen for location updates means less interaction with GPS and
network location services, thus, preserving battery life. But it also allows for fewer locations
from which to choose a best estimate.</p>

  <h4>Set the location providers to return updates less frequently</h4>

  <p>Reducing the rate at which new updates appear during the window can also improve battery
efficiency, but at the cost of accuracy. The value of the trade-off depends on how your
application is used. You can reduce the rate of updates by increasing the parameters in {@link
android.location.LocationManager#requestLocationUpdates requestLocationUpdates()} that specify the
interval time and minimum distance change.</p>

  <h4>Restrict a set of providers</h4>

  <p>Depending on the environment where your application is used or the desired level of accuracy,
you might choose to use only the Network Location Provider or only GPS, instead of both. Interacting
with only one of the services reduces battery usage at a potential cost of accuracy.</p>


  <h2>Common application cases</h2>

  <p>There are many reasons you might want to obtain the user location in your application. Below
are a couple scenarios in which you can use the user location to enrich your application. Each
scenario also describes good practices for when you should start and stop listening for the
location, in order to get a good reading and help preserve battery life.</p>


  <h3>Tagging user-created content with a location</h3>

  <p>You might be creating an application where user-created content is tagged with a location.
Think of users sharing their local experiences, posting a review for a restaurant, or recording some
content that can be augmented with their current location. A model of how this
interaction might happen, with respect to the location services, is visualized in figure 2.</p>

  <img src="{@docRoot}images/location/content-tagging.png" alt="" />
<p class="img-caption"><strong>Figure 2.</strong> A timeline representing the window in which
the user location is obtained and listening stops when the user consumes the current location.</p>

  <p>This lines up with the previous model of how user location is obtained in code (figure 1). For
best location accuracy, you might choose to start listening for location updates when users begin
creating
the content or even when the application starts, then stop listening for updates when content is
ready to be posted or recorded. You might need to consider how long a typical task of creating the
content takes and judge if this duration allows for efficient collection of a location estimate.</p>


  <h3>Helping the user decide on where to go</h3>

  <p>You might be creating an application that attempts to provide users with a set
of options about where to go. For example, you're looking to provide a list of nearby restaurants,
stores, and entertainment and the order of recommendations changes depending on the user
location.</p>

  <p>To accommodate such a flow, you might choose to:</p>
  <ul>
    <li>Rearrange recommendations when a new best estimate is obtained</li>
    <li>Stop listening for updates if the order of recommendations has stabilized</li>
  </ul>

  <p>This kind of model is visualized in figure 3.</p>

  <img src="{@docRoot}images/location/where-to-go.png" alt="" />
<p class="img-caption"><strong>Figure 3.</strong> A timeline representing the window in which a
dynamic set of data is updated each time the user location updates.</p>




<h2 id="MockData">Providing Mock Location Data</h2>

<p>As you develop your application, you'll certainly need to test how well your model for obtaining
user location works. This is most easily done using a real Android-powered device. If, however, you
don't have a device, you can still test your location-based features by mocking location data in
the Android emulator. There are three different ways to send your application mock location
data: using Eclipse, DDMS, or the "geo" command in the emulator console.</p>

<p class="note"><strong>Note:</strong> Providing mock location data is injected as GPS location
data, so you must request location updates from <code>GPS_PROVIDER</code> in order for mock location
data to work.</p>

<h3 id="MockEclipse">Using Eclipse</h3>

<p>Select <b>Window</b> &gt; <b>Show View</b> &gt; <b>Other</b> &gt; <b>Emulator Control</b>.</p>

<p>In the Emulator Control panel, enter GPS coordinates under Location Controls as individual
lat/long coordinates, with a GPX file for route playback, or a KML file for multiple place marks.
(Be sure that you have a device selected in the Devices panel&mdash;available from <b>Window</b>
&gt; <b>Show View</b> &gt; <b>Other</b> &gt; <b>Devices</b>.)</p>


<h3 id="MockDdms">Using DDMS</h3>

<p>With the DDMS tool, you can simulate location data a few different ways:</p>
<ul>
    <li>Manually send individual longitude/latitude coordinates to the device.</li>
    <li>Use a GPX file describing a route for playback to the device.</li>
    <li>Use a KML file describing individual place marks for sequenced playback to the device.</li>
</ul>

<p>For more information on using DDMS to spoof location data, see
<a href="{@docRoot}tools/debugging/ddms.html">Using DDMS</a>.


<h3 id="MockGeo">Using the "geo" command in the emulator console</h3>

<p>To send mock location data from the command line:</p>

<ol>
  <li>Launch your application in the Android emulator and open a terminal/console in your SDK's
<code>/tools</code> directory.</li>
  <li>Connect to the emulator console:
<pre>telnet localhost <em>&lt;console-port&gt;</em></pre></li>
  <li>Send the location data:</p>
    <ul><li><code>geo fix</code> to send a fixed geo-location.
    <p>This command accepts a longitude and latitude in decimal degrees, and
    an optional altitude in meters. For example:</p>
    <pre>geo fix -121.45356 46.51119 4392</pre>
      </li>
      <li><code>geo nmea</code> to send an NMEA 0183 sentence.
    <p>This command accepts a single NMEA sentence of type '$GPGGA' (fix data) or '$GPRMC' (transit
  data).
    For example:</p>
    <pre>geo nmea $GPRMC,081836,A,3751.65,S,14507.36,E,000.0,360.0,130998,011.3,E*62</pre>
      </li>
    </ul>
  </li>
</ol>

<p>For information about how to connect to the emulator console, see
<a href="{@docRoot}tools/devices/emulator.html#console">Using the Emulator Console</a>.</p>