-
Notifications
You must be signed in to change notification settings - Fork 240
Philosophy of Operation
The core philosophy of the Background Geolocation library is to track a device's location in the most battery efficient manner possible. Detecting when the device is moving and stationary is central to this philosophy.
The plugin has two states, moving and stationary. The plugin will automatically toggle between these states by monitoring the motion activity API's. When the plugin detects the device is not moving, it will enter the stationary state, reducing battery usage. When motion is detected, the plugin will enter the moving state, and begin recording locations each distanceFilter
meters. These locations are stored in the plugin's database and can be uploaded to your http server automatically (See Config option #url
).
You can listen to these state-changes by subscribing to the onMotionChange
event:
bg.BackgroundGeolocation.onMotionChange((bg.Location location) {
print("[onMotionChange] isMoving? ${location.isMoving}");
});
The plugin can be manually switched between these states by using the method changePace
. Passing true to changePace
will force the device into the moving state. Passing false to changePace
will force the device into the stationary state, shutting off location-services until motion is detected.
bg.BackgroundGeolocation.changePace(true).then((bool isMoving) {
print('- BackgroundGeolocation is now in the tracking (moving) state');
});
.
.
.
bg.BackgroundGeolocation.changePace(false).then((bool isMoving) {
print('- plugin is in the stationary state');
});
While in the moving state, when the motion activity API reports an activity of still
the plugin will engage its stop-detection system, initiating a timer of stopTimeout
minutes. If any motion is detected before this timer expires, the plugin will remain in the moving state. If the stopTimeout
timer expires, the plugin will enter the stationary state, reducing valuable battery usage.
By default, the plugin requests Always
location (See API docs Config.locationAuthorizationRequest
.
While the plugin can work with WhenInUse
authorization, there are major consequences to doing so:
- With
WhenInUse
authorization, apps are forbidden from automatically triggering location-tracking while your app is in the background (as described above, the plugin cannot automatically transition from the "stationary" to "moving" state in the background). - With
WhenInUse
authorization, your app must manually transition to the "moving" state while your app is in the foreground by calling.changePace(true)
, like a "Jogging App" would do, where the user might click a[Start Workout]
button before putting the phone in their pocket. -
Config.stopTimeout
will still apply: oncestopTimeout
expires, your app will transition to the stationary state by turning location-services off. - Geofencing requires
Always
location authorization. Geofences cannot operate withWhenInUse
authorization.
Background Geolocation monitors the CMMotionActivtyManager
API. This plugin is optimized for this API and requires the Motion & Fitness
permission. Disabling this permission will increase battery usage. For more information, see CMMotionActivityManager
.
Only the CMMotionActivityManager
API can determine the motion of the device (ie: still
, on_foot
, running
, on_bicycle
,in_vehicle
). If a car stops at a red light, the still
activity will be detected and location services will be disabled, reducing battery usage. Once the car advances through the green light, the plugin will detect motion and begin recording locations.
iOS is more strict than Android when apps are running in the background. When your iOS app is in the background, in the stationary state, iOS will completely suspend your app. There is no code running at all. The plugin will create a "stationary geofence" of stationaryRadius
meters around the current position. When iOS determines the device has exited this geofence, the plugin will turn on location-services and begin tracking according to the configured distanceFilter
. Your iOS app is now completely awake in the background. If you've configured the plugin with stopOnTerminate: false
, iOS will continue monitoring the "stationary geofence" even after app termination. If the device exits the "stationary geofence" in the terminated state, iOS will relaunch your app in the background to service that event and tracking will resume.
NOTE: Exiting the stationary geofence typically requires ~200 meters of movement. Even if you configure a stationaryRadius: 25
, iOS will still require the device to move ~200 meters.
In debug mode, once the plugin has created this geofence it will enter the stationary state and emit a sound.
bling
Because your app is completely awake in the background with #preventSuspend
, the plugin is able to constantly monitor the CMMotionActivityManager
API and respond to activity changes quickly. iOS will behave like Android, requiring only a few meters of movement to change to the moving state.
#preventSuspend
will consume more power. Take special care to manage this feature. For most users #preventSuspend
is not required.
With #preventSuspend
enabled, the plugin can execute a heartbeat
event at specified intervals (#heartbeatInterval
). The heartbeat
event can be configured to execute your Javascript code.
bg.BackgroundGeolocation.onHeartbeat((bg.HeartbeatEvent event) {
print('- heartbeat event received: $event');
});
When iOS detects a transition out of this geofence, the plugin will change state from stationary to moving. The following image shows the device exiting this geofence, where location services are engaged and aggressive tracking is initiated:
Once in the moving state, the plugin will begin recording a location at each distanceFilter
meters. In the moving state, your app will remain awake.
In debug mode, the plugin will emit a sound to announce stationary exit:
dee-do-dee-do...dee-do-dee-do
The iOS [CLLocationManager
] API is strictly distance based. If you configure a distanceFilter: 100
and remain in the same location, the iOS will not return a location until you move ~100 meters. The device battery is precious. For more information see https://developer.apple.com/reference/corelocation/cllocationmanager
Android does not require the use of a geofence and is capable of constantly monitoring changes in motion, typically requiring less than ten meters of movement. If the device has not detected motion for long periods of time Android can suspend your WebView where your Javascript is and delay motion activity updates even when you have configured #activityRecognitionInterval
. For more information https://developers.google.com/android/reference/com/google/android/gms/location/ActivityRecognitionApi.
While in the stationary state, Android does not completely suspend apps in the background, the plugin is able to fire a heartbeat
event periodically. The heartbeat
event will stop once the plugin enters the moving state. For more information see #heartbeatInterval
.
bg.BackgroundGeolocation.onHeartbeat((bg.HeartbeatEvent event) {
print('- heartbeat event received');
});
ℹ️ The heartbeat
event will cease once the plugin enters the moving state.
When the plugin detects a motion-activity of on_foot
, running
, on_bicycle
or in_vehicle
, it will immediately change state to moving. Location-services will be engaged and the plugin will begin tracking according to your configured distanceFilter
or locationUpdateInterval
.
While in the moving state, if the ActivityRecognitionAPI
reports a motion-activity of still
, the plugin will engage the "stop-detection" system, initiating a timer of stopTimeout
minutes. If the stopTimeout
timer expires, the plugin will enter the stationary state. If a "moving"-type motion-activity is detected during while the stopTimeout
timer is running, the timer will be cleared and the plugin will remain in the moving state.
Unlike iOS, Android allows both distance and time-based tracking. Like iOS, engage distance-based tracking simply by providing a distanceFilter > 0
(eg: distanceFilter: 50
)
To engage time-based tracking on Android, simply configure distanceFilter: 0
. The plugin will record a location each locationUpdateInterval
milliseconds (eg: locationUpdateInterval: 30000
will record a location every 30s.