Device overheating due to incorrect sensor polling frequency is a typical problem. Battery discharge in an hour instead of a day. We've encountered a project where sensor fusion produced random quaternions on cheap phones. The solution – proper choice of virtual sensors and batch updates. Over 5 years of experience and over 20 motion sensor projects behind us.
Android Sensor Framework provides access to 13+ sensor types via a single SensorManager. Accelerometer and gyroscope are hardware. TYPE_LINEAR_ACCELERATION, TYPE_ROTATION_VECTOR, TYPE_GRAVITY are virtual: computed from hardware via sensor fusion in firmware. The difference is that virtual sensors consume more CPU at high frequency and may be unavailable on budget devices. More details at Android Developers: Sensors Overview. Our experience: for precise orientation use TYPE_ROTATION_VECTOR, for linear acceleration – TYPE_LINEAR_ACCELERATION.
Registration and Lifecycle
class SensorViewModel(application: Application) : AndroidViewModel(application) { private val sensorManager = application.getSystemService(SENSOR_SERVICE) as SensorManager private val accelerometer = sensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER) private val gyroscope = sensorManager.getDefaultSensor(Sensor.TYPE_GYROSCOPE) private val _sensorData = MutableStateFlow<SensorData>(SensorData.Empty) val sensorData: StateFlow<SensorData> = _sensorData.asStateFlow() private val sensorEventListener = object : SensorEventListener { override fun onSensorChanged(event: SensorEvent) { when (event.sensor.type) { Sensor.TYPE_ACCELEROMETER -> handleAccelerometer(event.values) Sensor.TYPE_GYROSCOPE -> handleGyroscope(event.values) } } override fun onAccuracyChanged(sensor: Sensor, accuracy: Int) {} } fun startListening() { accelerometer?.let { sensorManager.registerListener( sensorEventListener, it, SensorManager.SENSOR_DELAY_GAME // ~20ms ) } } fun stopListening() { sensorManager.unregisterListener(sensorEventListener) } override fun onCleared() { stopListening() } } Key: unregisterListener strictly in onCleared() of ViewModel or in onPause() of Activity. A forgotten listener runs in the background, drains battery and can crash the app when Activity is destroyed.
Sampling Rates: Constants and Reality
| Constant | Nominal Delay | Actual Frequency |
|---|---|---|
SENSOR_DELAY_NORMAL |
200 ms | ~5 Hz |
SENSOR_DELAY_UI |
60 ms | ~16 Hz |
SENSOR_DELAY_GAME |
20 ms | ~50 Hz |
SENSOR_DELAY_FASTEST |
0 ms | maximum hardware rate |
Since Android API 9 you can set a custom interval in microseconds via registerListener(listener, sensor, samplingPeriodUs). For example, 10000 µs = 100 Hz.
SENSOR_DELAY_FASTEST on Snapdragon 8 Gen 2 can deliver up to 500 Hz on accelerometer – only needed for specialized applications (vibration analysis, balancing). For most tasks 50–100 Hz is enough.
Why Sensor Fusion in Android Requires Calibration?
Sensors on cheap devices have offsets and noise. Virtual sensors (TYPE_ROTATION_VECTOR) use built-in firmware calibration, but on old kernels they may drift. We test on devices from different vendors and add our own filter (Mahony/Madgwick) if needed.
TYPE_ROTATION_VECTOR – device orientation quaternion computed from accelerometer + gyroscope + magnetometer. More accurate than doing fusion manually:
val rotationVector = sensorManager.getDefaultSensor(Sensor.TYPE_ROTATION_VECTOR) // In onSensorChanged: val rotationMatrix = FloatArray(9) SensorManager.getRotationMatrixFromVector(rotationMatrix, event.values) val orientationAngles = FloatArray(3) SensorManager.getOrientation(rotationMatrix, orientationAngles) val azimuth = Math.toDegrees(orientationAngles[0].toDouble()) // 0-360° from north val pitch = Math.toDegrees(orientationAngles[1].toDouble()) // -90 to +90° val roll = Math.toDegrees(orientationAngles[2].toDouble()) // -180 to +180° TYPE_LINEAR_ACCELERATION – accelerometer without gravity (similar to userAcceleration in iOS CoreMotion). Use instead of TYPE_ACCELEROMETER when you need to measure only dynamic acceleration.
How to Implement Fall Detection?
- Use accelerometer at 50 Hz.
- Apply a high-pass filter to separate gravity:
val gravity = FloatArray(3) val linearAccel = FloatArray(3) val alpha = 0.8f // In onSensorChanged for TYPE_ACCELEROMETER: gravity[0] = alpha * gravity[0] + (1 - alpha) * event.values[0] gravity[1] = alpha * gravity[1] + (1 - alpha) * event.values[1] gravity[2] = alpha * gravity[2] + (1 - alpha) * event.values[2] linearAccel[0] = event.values[0] - gravity[0] linearAccel[1] = event.values[1] - gravity[1] linearAccel[2] = event.values[2] - gravity[2] val magnitude = sqrt( linearAccel[0].pow(2) + linearAccel[1].pow(2) + linearAccel[2].pow(2) )For wearable devices (elderly, miners) this method works well.
How to Organize Background Monitoring Without Excessive Battery Drain?
Use batch updates (
maxReportLatencyUs) andJobSchedulerfor periodic processing. Avoid virtual sensors in background – they consume CPU constantly. For simple activity detection (steps, stillness) accelerometer at 5-10 Hz with a 1-second batch (1000 ms) is enough.Activity Classification
Patterns: walking – regular peaks 1.5–2.5 m/s² at 1.5–2.5 Hz. Riding – low-frequency vibrations < 0.5 Hz. Still – magnitude < 0.1 m/s². For classification use a simple threshold detector or a machine learning model (e.g., random forest) – this improves accuracy to 95%.
Batch Updates (Android 4.4+)
SensorManager.flush()and themaxReportLatencyUsparameter inregisterListenerallow accumulating data in hardware FIFO and receiving batches. Useful for background apps – sensor collects data while CPU sleeps, wakes up every N seconds, delivers everything at once:sensorManager.registerListener( listener, accelerometer, 10_000, // samplingPeriodUs = 100 Hz 500_000 // maxReportLatencyUs = 500 ms batch )FIFO size varies across chipsets (512–4096 samples). On overflow, old data is replaced – consider this for long sessions.
What's Included in Sensor Integration Work
- Analysis – sensor selection for the scenario (navigation, activity tracking, wearables)
- Architecture – ViewModel/Service, lifecycle handling, sensor battery optimization
- Implementation – registration, filtering, event classification (fall, steps, turns)
- Testing – on real devices (5+ models with different chipsets)
- Support – API documentation, team training, up to 6-month code warranty
Comparison of Sensor Fusion Approaches
Characteristic TYPE_ROTATION_VECTOR (built-in) Manual Madgwick/Mahony Accuracy on high-end devices ±1° ±2-3° after calibration CPU consumption Low High (200+ updates/s) Support on budget phones Often missing Works everywhere Integration complexity Minimal Requires parameter tuning Our experience: for 90% of projects built-in virtual sensors suffice. Manual fusion is only needed for AR/VR or working with outdated SoCs.
Timeline
Basic integration of 1–2 sensors with data processing – from 3 to 5 business days. Multi-sensor algorithm with activity classification, background recording and analytics – from 2 to 4 weeks. Cost is calculated individually after requirements analysis. Timeframes are specified after auditing your project.
Our team has over 5 years of experience and over 20 successful projects with motion sensors. Order a consultation – we'll assess feasibility and propose an architecture for your scenario. Get a quality integration with up to 6-month code warranty.
For more details on Sensor API, refer to the official Android documentation.







