| 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/[email protected]" |
| srcset="{@docRoot}images/training/geofence.png 1x, {@docRoot}images/training/[email protected] 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= |
| "https://developers.google.com/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> |