Skip to content

Philosophy of Operation

Chris Scott edited this page Apr 18, 2024 · 5 revisions

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.

Two States: Moving and Stationary

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}");
});

Manually Toggling Between States

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');
});

stopTimeout parameter

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.

Location Authorization: WhenInUse vs Always

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: once stopTimeout expires, your app will transition to the stationary state by turning location-services off.
  • Geofencing requires Always location authorization. Geofences cannot operate with WhenInUse authorization.

iOS

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 "Stationary" State

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

iOS #preventSuspend mode

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.

⚠️ WARNING: #preventSuspend will consume more power. Take special care to manage this feature. For most users #preventSuspend is not required.

iOS heartbeat Event

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');
});

iOS "Moving" state

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

iOS Distance-based Tracking

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

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.

Android heartbeat Event

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.

Android "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.

Distance-based Tracking

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)

Time-based Tracking

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.