438 lines
21 KiB
Plaintext
438 lines
21 KiB
Plaintext
page.title=Creating and Monitoring Geofences
|
||
|
||
trainingnavtop=true
|
||
@jd:body
|
||
|
||
|
||
<div id="tb-wrapper">
|
||
<div id="tb">
|
||
|
||
<h2>This lesson teaches you to</h2>
|
||
<ol>
|
||
<li><a href="#RequestGeofences">Set up for Geofence Monitoring</a></li>
|
||
<li><a href="#CreateAdd">Create and Add Geofences</a></li>
|
||
<li><a href="#HandleGeofenceTransitions">Handle Geofence Transitions</a></li>
|
||
<li><a href="#StopGeofenceMonitoring">Stop Geofence Monitoring</a></li>
|
||
<li><a href="#BestPractices">Use Best Practices for Geofencing</a></li>
|
||
<li><a href="#Troubleshooting">Troubleshoot the Geofence Entrance Event</a></li>
|
||
</ol>
|
||
|
||
<h2>You should also read</h2>
|
||
<ul>
|
||
<li>
|
||
<a href="{@docRoot}google/play-services/setup.html">Setup Google Play Services SDK</a>
|
||
</li>
|
||
</ul>
|
||
|
||
<h2>Try it out</h2>
|
||
|
||
<ul>
|
||
<li>
|
||
<a href="https://github.com/googlesamples/android-play-location/tree/master/Geofencing"
|
||
class="external-link">Geofencing</a>
|
||
</li>
|
||
</ul>
|
||
|
||
</div>
|
||
</div>
|
||
<p>
|
||
Geofencing combines awareness of the user's current location with awareness of the user's
|
||
proximity to locations that may be of interest. To mark a
|
||
location of interest, you specify its latitude and longitude. To adjust the proximity for the
|
||
location, you add a radius. The latitude, longitude, and radius define a geofence, creating a
|
||
circular area, or fence, around the location of interest.
|
||
</p>
|
||
<p>
|
||
You can have multiple active geofences, with a limit of 100 per device user. For each geofence,
|
||
you can ask Location Services to send you entrance and exit events, or you can specify a
|
||
duration within the geofence area to wait, or <em>dwell</em>, before triggering an event. You
|
||
can limit the duration of any geofence by specifying an expiration duration in milliseconds.
|
||
After the geofence expires, Location Services automatically removes it.
|
||
</p>
|
||
|
||
<img src="{@docRoot}images/training/geofence@2x.png"
|
||
srcset="{@docRoot}images/training/geofence.png 1x, {@docRoot}images/training/geofence@2x.png 2x" alt=""
|
||
width="400" height="400"/>
|
||
<p>
|
||
This lesson shows you how to add and remove geofences, and then listen for geofence transitions
|
||
using an {@link android.app.IntentService}.</p>
|
||
|
||
<p>We recommend upgrading existing apps to use the
|
||
<a href="{@docRoot}reference/com/google/android/gms/location/LocationServices.html">
|
||
LocationServices</a> class, which contains the
|
||
<a href="{@docRoot}reference/com/google/android/gms/location/GeofencingApi.html">
|
||
GeofencingApi</a> interface. The
|
||
<a href="{@docRoot}reference/com/google/android/gms/location/LocationServices.html">
|
||
LocationServices</a> class replaces the
|
||
<a href="{@docRoot}reference/com/google/android/gms/location/LocationClient.html">
|
||
LocationClient</a> (deprecated).</p>
|
||
|
||
<h2 id="RequestGeofences">Set up for Geofence Monitoring</h2>
|
||
<p>
|
||
The first step in requesting geofence monitoring is to request the necessary permission.
|
||
To use geofencing, your app must request
|
||
{@link android.Manifest.permission#ACCESS_FINE_LOCATION ACCESS_FINE_LOCATION}. To request this
|
||
permission, add the following element as a child element of the
|
||
<code><a href="{@docRoot}guide/topics/manifest/manifest-element.html"><manifest></a></code>
|
||
element in your app manifest:
|
||
</p>
|
||
<pre>
|
||
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
|
||
</pre>
|
||
|
||
<p>
|
||
If you want to use an {@link android.app.IntentService} to listen for geofence transitions,
|
||
add an element specifying the service name. This element must be
|
||
a child of the <code><a href="{@docRoot}guide/topics/manifest/application-element.html">
|
||
<application></a></code> element:
|
||
</p>
|
||
|
||
<pre>
|
||
<application
|
||
android:allowBackup="true">
|
||
...
|
||
<service android:name=".GeofenceTransitionsIntentService"/>
|
||
<application/>
|
||
</pre>
|
||
|
||
<p>To access the location APIs, 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>
|
||
|
||
<h2 id="CreateAdd">Create and Add Geofences</h2>
|
||
|
||
<p>Your app needs to create and add geofences using the location API's builder class for
|
||
creating Geofence objects, and the convenience class for adding them. Also, to handle the
|
||
intents sent from Location Services when geofence transitions occur, you can define a
|
||
{@link android.app.PendingIntent} as shown in this section.
|
||
</p>
|
||
|
||
<p class="note"><strong>Note:</strong> On single-user devices, there is a limit of 100 geofences per app. For multi-user devices, the limit is 100 geofences per app per device user.</p>
|
||
|
||
<h3>Create geofence objects</h3>
|
||
|
||
<p>
|
||
First, use <code><a href="{@docRoot}reference/com/google/android/gms/location/Geofence.Builder.html">
|
||
Geofence.Builder</a></code> to create a geofence, setting the desired radius, duration, and
|
||
transition types for the geofence. For example, to populate a list object named
|
||
{@code mGeofenceList}:
|
||
</p>
|
||
|
||
<pre>
|
||
mGeofenceList.add(new Geofence.Builder()
|
||
// Set the request ID of the geofence. This is a string to identify this
|
||
// geofence.
|
||
.setRequestId(entry.getKey())
|
||
|
||
.setCircularRegion(
|
||
entry.getValue().latitude,
|
||
entry.getValue().longitude,
|
||
Constants.GEOFENCE_RADIUS_IN_METERS
|
||
)
|
||
.setExpirationDuration(Constants.GEOFENCE_EXPIRATION_IN_MILLISECONDS)
|
||
.setTransitionTypes(Geofence.GEOFENCE_TRANSITION_ENTER |
|
||
Geofence.GEOFENCE_TRANSITION_EXIT)
|
||
.build());
|
||
</pre>
|
||
|
||
<p>This example pulls data from a constants file. In actual practice, apps might
|
||
dynamically create geofences based on the user's location.</p>
|
||
|
||
<h3>Specify geofences and initial triggers</h3>
|
||
|
||
<p>
|
||
The following snippet uses the <code><a href="{@docRoot}reference/com/google/android/gms/location/GeofencingRequest.html">
|
||
GeofencingRequest</a></code> class
|
||
and its nested <code><a href="{@docRoot}reference/com/google/android/gms/location/GeofencingRequest.Builder.html">
|
||
GeofencingRequestBuilder</a></code> class to
|
||
specify the geofences to monitor and to set how related geofence events are triggered:
|
||
</p>
|
||
<pre>
|
||
private GeofencingRequest getGeofencingRequest() {
|
||
GeofencingRequest.Builder builder = new GeofencingRequest.Builder();
|
||
builder.setInitialTrigger(GeofencingRequest.INITIAL_TRIGGER_ENTER);
|
||
builder.addGeofences(mGeofenceList);
|
||
return builder.build();
|
||
}
|
||
</pre>
|
||
|
||
<p>
|
||
This example shows the use of two geofence triggers. The <code>
|
||
<a href="{@docRoot}reference/com/google/android/gms/location/Geofence.html#GEOFENCE_TRANSITION_ENTER">
|
||
GEOFENCE_TRANSITION_ENTER</a></code>
|
||
transition triggers when a device enters a geofence, and the <code>
|
||
<a href="{@docRoot}reference/com/google/android/gms/location/Geofence.html#GEOFENCE_TRANSITION_EXIT">
|
||
GEOFENCE_TRANSITION_EXIT</a></code>
|
||
transition triggers when a device exits a geofence. Specifying
|
||
<code>
|
||
<a href="{@docRoot}reference/com/google/android/gms/location/GeofencingRequest.html#INITIAL_TRIGGER_ENTER">
|
||
INITIAL_TRIGGER_ENTER</a></code> tells Location services that
|
||
<code>
|
||
<a href="{@docRoot}reference/com/google/android/gms/location/Geofence.html#GEOFENCE_TRANSITION_ENTER">
|
||
GEOFENCE_TRANSITION_ENTER</a></code>
|
||
should be triggered if the the device is already inside the geofence.</p>
|
||
</p>
|
||
|
||
<p>In many cases, it may be preferable to use instead <code>
|
||
<a href="{@docRoot}reference/com/google/android/gms/location/GeofencingRequest.html#INITIAL_TRIGGER_DWELL">
|
||
INITIAL_TRIGGER_DWELL</a></code>,
|
||
which triggers events only when the user stops for a defined duration within a geofence.
|
||
This approach can help reduce "alert spam" resulting from large numbers notifications when a
|
||
device briefly enters and exits geofences. Another strategy for getting best results from your
|
||
geofences is to set a minimum radius of 100 meters. This helps account for the location accuracy
|
||
of typical Wi-Fi networks, and also helps reduce device power consumption.
|
||
</p>
|
||
|
||
<h3>Define an Intent for geofence transitions</h3>
|
||
<p>
|
||
The {@link android.content.Intent} sent from Location Services can trigger various actions in
|
||
your app, but you should <i>not</i> have it start an activity or fragment, because components
|
||
should only become visible in response to a user action. In many cases, an
|
||
{@link android.app.IntentService} is a good way to handle the intent. An
|
||
{@link android.app.IntentService} can post a notification, do long-running background work,
|
||
send intents to other services, or send a broadcast intent. The following snippet shows how
|
||
to define a {@link android.app.PendingIntent} that starts an {@link android.app.IntentService}:
|
||
</p>
|
||
<pre>
|
||
public class MainActivity extends FragmentActivity {
|
||
...
|
||
private PendingIntent getGeofencePendingIntent() {
|
||
// Reuse the PendingIntent if we already have it.
|
||
if (mGeofencePendingIntent != null) {
|
||
return mGeofencePendingIntent;
|
||
}
|
||
Intent intent = new Intent(this, GeofenceTransitionsIntentService.class);
|
||
// We use FLAG_UPDATE_CURRENT so that we get the same pending intent back when
|
||
// calling addGeofences() and removeGeofences().
|
||
return PendingIntent.getService(this, 0, intent, PendingIntent.
|
||
FLAG_UPDATE_CURRENT);
|
||
}
|
||
</pre>
|
||
|
||
<h3>Add geofences</h3>
|
||
|
||
<p>
|
||
To add geofences, use the <code>
|
||
<a href="{@docRoot}reference/com/google/android/gms/location/GeofencingApi.html#addGeofences(com.google.android.gms.common.api.GoogleApiClient, com.google.android.gms.location.GeofencingRequest, android.app.PendingIntent)">{@code GeoencingApi.addGeofences()}</a></code> method.
|
||
Provide the Google API client, the <code>
|
||
<a href="{@docRoot}reference/com/google/android/gms/location/GeofencingRequest">
|
||
GeofencingRequest</a></code> object, and the {@link android.app.PendingIntent}.
|
||
The following snippet, which processes the results in <code>
|
||
<a href="{@docRoot}reference/com/google/android/gms/common/api/ResultCallback.html#onResult(R)">
|
||
onResult()</a></code>, assumes that the main activity implements <code>
|
||
<a href="{@docRoot}reference/com/google/android/gms/common/api/ResultCallback.html">
|
||
ResultCallback</a></code>:
|
||
</p>
|
||
<pre>
|
||
public class MainActivity extends FragmentActivity {
|
||
...
|
||
LocationServices.GeofencingApi.addGeofences(
|
||
mGoogleApiClient,
|
||
getGeofencingRequest(),
|
||
getGeofencePendingIntent()
|
||
).setResultCallback(this);
|
||
</pre>
|
||
|
||
|
||
<h2 id="HandleGeofenceTransitions">Handle Geofence Transitions</h2>
|
||
<p>
|
||
When Location Services detects that the user has entered or exited a geofence, it
|
||
sends out the {@link android.content.Intent} contained in the {@link android.app.PendingIntent}
|
||
you included in the request to add geofences. This {@link android.content.Intent} is received
|
||
by a service like <code>GeofenceTransitionsIntentService</code>,
|
||
which obtains the geofencing event from the intent, determines the type of Geofence transition(s),
|
||
and determines which of the defined geofences was triggered. It then sends a notification as
|
||
the output.
|
||
</p>
|
||
<p>
|
||
The following snippet shows how to define an {@link android.app.IntentService} that posts a
|
||
notification when a geofence transition occurs. When the user clicks the notification, the
|
||
app's main activity appears:
|
||
</p>
|
||
<pre>
|
||
public class GeofenceTransitionsIntentService extends IntentService {
|
||
...
|
||
protected void onHandleIntent(Intent intent) {
|
||
GeofencingEvent geofencingEvent = GeofencingEvent.fromIntent(intent);
|
||
if (geofencingEvent.hasError()) {
|
||
String errorMessage = GeofenceErrorMessages.getErrorString(this,
|
||
geofencingEvent.getErrorCode());
|
||
Log.e(TAG, errorMessage);
|
||
return;
|
||
}
|
||
|
||
// Get the transition type.
|
||
int geofenceTransition = geofencingEvent.getGeofenceTransition();
|
||
|
||
// Test that the reported transition was of interest.
|
||
if (geofenceTransition == Geofence.GEOFENCE_TRANSITION_ENTER ||
|
||
geofenceTransition == Geofence.GEOFENCE_TRANSITION_EXIT) {
|
||
|
||
// Get the geofences that were triggered. A single event can trigger
|
||
// multiple geofences.
|
||
List<Geofence> triggeringGeofences = geofencingEvent.getTriggeringGeofences();
|
||
|
||
// Get the transition details as a String.
|
||
String geofenceTransitionDetails = getGeofenceTransitionDetails(
|
||
this,
|
||
geofenceTransition,
|
||
triggeringGeofences
|
||
);
|
||
|
||
// Send notification and log the transition details.
|
||
sendNotification(geofenceTransitionDetails);
|
||
Log.i(TAG, geofenceTransitionDetails);
|
||
} else {
|
||
// Log the error.
|
||
Log.e(TAG, getString(R.string.geofence_transition_invalid_type,
|
||
geofenceTransition));
|
||
}
|
||
}
|
||
</pre>
|
||
|
||
<p>After detecting the transition event via the {@link android.app.PendingIntent},
|
||
this {@link android.app.IntentService} gets the geofence transition type and tests whether
|
||
it is one of the events the app uses to trigger notifications -- either
|
||
<code><a href="{@docRoot}reference/com/google/android/gms/location/Geofence.html#GEOFENCE_TRANSITION_ENTER">GEOFENCE_TRANSITION_ENTER</a></code>
|
||
or <code><a href="{@docRoot}reference/com/google/android/gms/location/Geofence.html#GEOFENCE_TRANSITION_EXIT">GEOFENCE_TRANSITION_EXIT</a></code>
|
||
in this case. The service then sends a notification and logs the transition details.</p>
|
||
<!--
|
||
Remove Geofences
|
||
-->
|
||
<h2 id="StopGeofenceMonitoring">Stop Geofence Monitoring</h2>
|
||
|
||
<p>Stopping geofence monitoring when it is no longer needed or desired can help save battery
|
||
power and CPU cycles on the device. You can stop geofence monitoring
|
||
in the main activity used to add and remove geofences; removing a geofence stops it
|
||
immediately. The API provides methods to
|
||
remove geofences either by request IDs, or by removing geofences associated with a given
|
||
{@link android.app.PendingIntent}.
|
||
</p>
|
||
<p>
|
||
The following snippet removes geofences by {@link android.app.PendingIntent}, stopping all
|
||
further notification when the device enters or exits previously added geofences:
|
||
</p>
|
||
<pre>
|
||
LocationServices.GeofencingApi.removeGeofences(
|
||
mGoogleApiClient,
|
||
// This is the same pending intent that was used in addGeofences().
|
||
getGeofencePendingIntent()
|
||
).setResultCallback(this); // Result processed in onResult().
|
||
}
|
||
</pre>
|
||
|
||
<p>
|
||
You can combine geofencing with other location-aware features, such as periodic location updates.
|
||
For more information, see the other lessons in this class.
|
||
</p>
|
||
|
||
<h2 id="BestPractices">Use Best Practices for Geofencing</h2>
|
||
|
||
<p>This section outlines recommendations for using geofencing with the location
|
||
APIs for Android.</p>
|
||
|
||
<h3>Reduce power consumption</h3>
|
||
|
||
<p>You can use the following techniques to optimize power consumption in your apps that use geofencing:</p>
|
||
|
||
<ul>
|
||
<li><p>Set the <a href="{@docRoot}android/reference/com/google/android/gms/location/Geofence.Builder.html#setNotificationResponsiveness(int)">
|
||
notification responsiveness</a> to a higher value. Doing so improves power consumption by
|
||
increasing the latency of geofence alerts. For example, if you set a responsiveness value of five
|
||
minutes your app only checks for an entrance or exit alert once every five minutes.
|
||
Setting lower values does not necessarily mean that users will be notified within that time period
|
||
(for example, if you set a value of 5 seconds it may take a bit longer than that to receive the
|
||
alert).</p></li>
|
||
<li><p>Use a larger geofence radius for locations where a user spends a significant amount of time,
|
||
such as home or work. While a larger radius doesn't directly reduce power consumption, it reduces
|
||
the frequency at which the app checks for entrance or exit, effectively lowering overall power
|
||
consumption.</p></li>
|
||
</ul>
|
||
|
||
<h3>Choose the optimal radius for your geofence</h3>
|
||
<p>For best results, the minimium radius of the geofence should be set between 100 - 150 meters.
|
||
When Wi-Fi is available location accuracy is usually between 20 - 50 meters. When indoor
|
||
location is available, the accuracy range can be as small as 5 meters. Unless you know indoor
|
||
location is available inside the geofence, assume that Wi-Fi location accuracy is about
|
||
50 meters.</p>
|
||
|
||
<p>When Wi-Fi location is not available (for example, when you are driving in rural areas) the
|
||
location accuracy degrades. The accuracy range can be as large as several hundred meters to
|
||
several kilometers. In cases like this, you should create geofences using a larger radius.</p>
|
||
|
||
<h3>Use the dwell transition type to reduce alert spam</h3>
|
||
|
||
<p>If you receive a large number of alerts when driving briefly past a geofence, the best way to
|
||
reduce the alerts is to use a transition type of <code>
|
||
<a href="{@docRoot}reference/com/google/android/gms/location/Geofence.html#GEOFENCE_TRANSITION_DWELL">
|
||
GEOFENCE_TRANSITION_DWELL</a></code> instead of <code>
|
||
<a href="{@docRoot}reference/com/google/android/gms/location/Geofence.html#GEOFENCE_TRANSITION_ENTER">
|
||
GEOFENCE_TRANSITION_ENTER</a></code>. This way, the dwelling alert is sent only when the user stops
|
||
inside a geofence for a given period of time. You can choose the duration by setting a
|
||
<a href="{@docRoot}reference/com/google/android/gms/location/Geofence.Builder.html#setLoiteringDelay(int)">
|
||
loitering delay</a>.</p>
|
||
|
||
<h3>Re-register geofences only when required</h3>
|
||
|
||
<p>Registered geofences are kept in the <code>com.google.process.location</code> process owned by
|
||
the <code>com.google.android.gms</code> package.
|
||
The app doesn’t need to do anything to handle the following events, because the system
|
||
restores geofences after these events:</p>
|
||
<ul>
|
||
<li>Google Play services is upgraded.</li>
|
||
<li>Google Play services is killed and restarted by the system due resource restriction.</li>
|
||
<li>The location process crashes.</li>
|
||
</ul>
|
||
<p>The app must re-register geofences if they're still needed after the following events, since
|
||
the system cannot recover the geofences in the following cases:</p>
|
||
|
||
<ul>
|
||
<li>The device is rebooted. The app should listen for the device's boot complete action, and then re-
|
||
register the geofences required.</li>
|
||
<li>The app is uninstalled and re-installed.</li>
|
||
<li>The app's data is cleared.</li>
|
||
<li>Google Play services data is cleared.</li>
|
||
<li>The app has received a <code><a href="{@docRoot}reference/com/google/android/gms/location/LocationStatusCodes.html#GEOFENCE_NOT_AVAILABLE">GEOFENCE_NOT_AVAILABLE</a></code>
|
||
alert. This typically happens
|
||
after NLP (Android's Network Location Provider) is disabled.</li>
|
||
</ul>
|
||
|
||
<h2 id="Troubleshooting">Troubleshoot the Geofence Entrance Event</h2>
|
||
|
||
<p>If geofences are not being triggered when the device enters a geofence
|
||
(the <code><a href="{@docRoot}reference/com/google/android/gms/location/Geofence.html#GEOFENCE_TRANSITION_ENTER">
|
||
GEOFENCE_TRANSITION_ENTER</a></code> alert isn’t triggered), first ensure that your geofences are
|
||
registered properly as described in this guide.</p>
|
||
|
||
<p>Here are some possible reasons for alerts not working as expected:</p>
|
||
|
||
<ul>
|
||
<li><strong>Accurate location is not available inside your geofence or your geofence is too
|
||
small.</strong> On most devices, the geofence service uses only network location for geofence
|
||
triggering. The service uses this approach because network location consumes much less
|
||
power, it takes less time to get discrete locations, and most importantly it’s available indoors.
|
||
Starting with Google Play services 3.2, the geofence service calculates the overlapping ratio of
|
||
the location circle and the geofence circle and only generates the entrance alert when the ratio
|
||
is at least 85% for a bigger geofence or 75% for a smaller geofence. For an exit alert, the ratio
|
||
threshold used is 15% or 25%. Any ratio between these thresholds makes the geofence service mark
|
||
the geofence state as <code>INSIDE_LOW_CONFIDENCE</code> or <code>OUTSIDE_LOW_CONFIDENCE</code> and
|
||
no alert is sent.</li>
|
||
<li><strong>Wi-Fi is turned off on the device.</strong> Having Wi-Fi on can significantly improve
|
||
the location accuracy, so if Wi-Fi is turned off, your application might never get geofence alerts
|
||
depending on several settings including the radius of the geofence, the device model, or the
|
||
Android version. Starting from Android 4.3 (API level 18), we added the capability of “Wi-Fi scan
|
||
only mode” which allows users to disable Wi-Fi but still get good network location. It’s good
|
||
practice to prompt the user and provide a shortcut for the user to enable Wi-Fi or Wi-Fi scan only
|
||
mode if both of them are disabled. Use <a href="{@docRoot}reference/com/google/android/gms/location/SettingsApi">
|
||
SettingsApi</a> to ensure that the device's system settings are properly configured for optimal
|
||
location detection.</li>
|
||
<li><strong>There is no reliable network connectivity inside your geofence.</strong> If there is
|
||
no reliable data connection, alerts might not be generated. This is because the geofence service
|
||
depends on the network location provider which in turn requires a data connection.</li>
|
||
<li><strong>Alerts can be late.</strong> The geofence service does not continuously query for
|
||
location, so expect some latency when receiving alerts. Usually the latency is less than 2
|
||
minutes, even less when the device has been moving. If the device has been stationary for a
|
||
significant period of time, the latency may increase (up to 6 minutes).</li>
|
||
</ul>
|