Sensors API — интерфейс на платформата Android за достъп до хардуерните сензори на устройството. API предоставя унифициран начин за получаване на данни от акселерометър, жироскоп, магнитометър, сензор за осветеност и други сензори. Според данни на Google, 2026, над 95% от съвременните Android устройства са оборудвани с поне три типа сензори, което отваря широки възможности за разработка на контекстно-зависими приложения.
Основни моменти
Sensors API — програмен интерфейс на Android Framework, предоставящ на разработчиците достъп до хардуерните сензори на мобилното устройство. API е част от Android SDK от версия 1.0 и е достъпен чрез пакета android.hardware. Интерфейсът абстрахира разликите между имплементациите на сензори от различни производители — Qualcomm, MediaTek, Samsung Exynos — и предоставя унифициран модел за работа със сензори.
API позволява изпълнението на три ключови операции: получаване на списък с налични сензори, регистриране на слушатели за получаване на данни и управление на честотата на обновяване на показанията. SensorManager е централната точка за достъп до всички операции. Системата уведомява приложението чрез callback onSensorChanged, предавайки обект SensorEvent с масив от float стойности.
По време на съществуването на Android, API премина през няколко етапа на развитие. Във версия 2.3 (Gingerbread) се появи поддръжка за Batch Processing — групиране на събития от сензори за намаляване на консумацията на енергия. Android 4.0 (Ice Cream Sandwich) добави виртуалните сензори Gravity и Linear Acceleration. От Android 8.0 системата ограничи фонов достъп до сензори за повишаване на поверителността.
val sensorManager = getSystemService(Context.SENSOR_SERVICE) as SensorManager
val sensorList: List<Sensor> = sensorManager.getSensorList(Sensor.TYPE_ALL)
sensorList.forEach { sensor ->
Log.d("Сензори", "${sensor.name} — ${sensor.type}")
}
Не всички устройства имат еднакъв набор от сензори. Приложението трябва да провери наличността на конкретен сензор преди регистриране на слушател. Android емулаторът поддържа само ограничен набор от сензори — акселерометър и магнитометър. За тестване на останалите сензори е необходимо физическо устройство. От Android 12 приложенията трябва да декларират използването на сензори в манифеста.
Android класифицира всички сензори в три категории: Motion (движение), Position (положение) и Environment (околна среда). Motion сензорите включват акселерометър, жироскоп и крачкомер. Position сензорите включват магнитометър и Orientation. Environment — сензор за осветеност, налягане, температура и влажност.
| Категория | Сензори | Тип (константа) | Физически/Виртуален |
|---|---|---|---|
| Motion | Акселерометър | TYPE_ACCELEROMETER | Физически |
| Motion | Жироскоп | TYPE_GYROSCOPE | Физически |
| Motion | Крачкомер | TYPE_STEP_COUNTER | Физически |
| Motion | Gravity | TYPE_GRAVITY | Виртуален |
| Position | Магнитометър | TYPE_MAGNETIC_FIELD | Физически |
| Position | Rotation Vector | TYPE_ROTATION_VECTOR | Виртуален |
| Environment | Осветеност | TYPE_LIGHT | Физически |
| Environment | Налягане | TYPE_PRESSURE | Физически |
Акселерометърът измерва ускорението в m/s² по три оси — X, Y и Z. Стойностите включват гравитацията (9.8 m/s² по оста Z в покой). Жироскопът измерва ъгловата скорост в rad/s — скоростта на въртене на устройството около всяка ос. Комбинацията от тези два сензора се използва в навигация, игри и AR приложения.
class SensorActivity : Activity(), SensorEventListener {
override fun onSensorChanged(event: SensorEvent) {
if (event.sensor.type == Sensor.TYPE_ACCELEROMETER) {
val x = event.values[0]
val y = event.values[1]
val z = event.values[2]
textView.text = "X: $x, Y: $y, Z: $z"
}
}
override fun onAccuracyChanged(sensor: Sensor, accuracy: Int) {}
}
Магнитометърът (TYPE_MAGNETIC_FIELD) измерва магнитното поле в микротесла (µT) по три оси. Използва се като цифров компас — в комбинация с акселерометъра позволява определяне на азимута на устройството. Rotation Vector — виртуален сензор, който обединява данни от жироскоп, акселерометър и магнитометър за точно определяне на ориентацията.
Сензорът за осветеност (TYPE_LIGHT) измерва нивото на външно осветление в лукс (lx). Използва се за автоматично регулиране на яркостта на екрана. Сензорът за налягане (TYPE_PRESSURE) измерва атмосферното налягане в хектопаскали (hPa) — прилага се в навигационни приложения за определяне на надморска височина.
Sensors API е изграден върху клиент-сървър архитектура. Приложението действа като клиент, SensorManager — като системна услуга на Android. Когато приложението регистрира слушател чрез registerListener, SensorManager се свързва с HAL (Hardware Abstraction Layer) — нисконивов драйвер на сензора, работещ на ниво ядро на Linux.
HAL драйверът получава сурови данни от хардуерния чип, филтрира шума и ги предава нагоре по стека — чрез SensorService (системен процес) към приложението. Данните се предават като обект SensorEvent, съдържащ масив от float стойности values и времеви клеймо в наносекунди. Честотата на събитията зависи от избрания режим на доставка.
При регистриране на слушател, разработчикът задава желаното закъснение между събитията чрез константите SENSOR_DELAY_NORMAL (200 ms), SENSOR_DELAY_UI (60 ms), SENSOR_DELAY_GAME (20 ms) и SENSOR_DELAY_FASTEST (0 ms — максимална честота). Реалната честота може да се различава — системата оптимизира консумацията на енергия.
val sensor = sensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER)
sensorManager.registerListener(
this,
sensor,
SensorManager.SENSOR_DELAY_GAME
)
От Android 2.3 сензорите поддържат пакетен режим — събитията се натрупват в FIFO буфер и се доставят в един пакет. Това позволява на процесора да остане по-дълго в режим на сън, намалявайки консумацията на енергия до 60%. Размерът на буфера зависи от чипа на сензора и е посочен в полето Sensor.fifoMaxEventCount.
SensorManager — системна услуга, достъпна чрез getSystemService(Context.SENSOR_SERVICE). Предоставя методи за получаване на списък със сензори, получаване на конкретен сензор по подразбиране и регистриране/отмяна на слушатели. SensorManager — сингълтън, един екземпляр за цялото приложение.
Преди използване на сензора трябва да се провери неговото наличие. Методът getDefaultSensor(type) връща null, ако сензорът не е наличен на устройството. Получаване на null без проверка ще доведе до NullPointerException при регистриране на слушател. Списъкът на всички сензори се връща от метода getSensorList(type).
fun checkSensorAvailability(type: Int): Boolean {
val sensorManager =
getSystemService(Context.SENSOR_SERVICE) as SensorManager
return sensorManager.getDefaultSensor(type) != null
}
// Използване:
if (checkSensorAvailability(Sensor.TYPE_GYROSCOPE)) {
Log.d("Sensor", "Жироскопът е наличен")
}
Слушателят се регистрира чрез registerListener(listener, sensor, delay). Важно: отмяната на слушателя трябва да се извърши в метода onPause() чрез unregisterListener(listener). Ако това не бъде направено, приложението ще продължи да получава събития от сензора, изтощавайки батерията дори на фонов режим. Android 8+ показва предупреждение в logcat за нерегистрирани слушатели.
Методът onAccuracyChanged(sensor, accuracy) се извиква при промяна на точността на сензора. Стойности: SENSOR_STATUS_ACCURACY_HIGH, MEDIUM, LOW и UNRELIABLE. При получаване на UNRELIABLE данните от сензора трябва да се игнорират до възстановяване на точността. Например, магнитометърът изисква калибриране — разклащане на устройството във формата на осмица.
Нека разгледаме практически примери за използване на Sensors API в Android приложения на Kotlin. Първи пример — определяне на ориентацията на екрана с помощта на акселерометър. Втори — отчитане на показанията на магнитометъра с калибриране. Трети — детектор на стъпки с крачкомер.
За определяне на ориентацията се използва комбинация от акселерометър и магнитометър. Методът SensorManager.getRotationMatrix() изчислява ротационна матрица, а getOrientation() извлича ъглите pitch, roll и azimuth. Azimuth (азимут) — ъгълът спрямо магнитния север в радиани.
val gravity = FloatArray(3)
val geomagnetic = FloatArray(3)
val rotationMatrix = FloatArray(9)
val orientation = FloatArray(3)
SensorManager.getRotationMatrix(
rotationMatrix, null, gravity, geomagnetic
)
SensorManager.getOrientation(rotationMatrix, orientation)
// orientation[0] — azimuth, [1] — pitch, [2] — roll
val azimuthDeg = Math.toDegrees(orientation[0].toDouble())
Крачкомерът (TYPE_STEP_COUNTER) връща общия брой стъпки, направени от потребителя от последното рестартиране на устройството. Сензорът работи на хардуерно ниво — активен е дори когато приложението не работи. За получаване на броя стъпки на сесия е необходимо да се запази началната стойност и да се изчисли разликата.
var initialSteps = 0
var isInitialized = false
override fun onSensorChanged(event: SensorEvent) {
if (event.sensor.type == Sensor.TYPE_STEP_COUNTER) {
if (!isInitialized) {
initialSteps = event.values[0].toInt()
isInitialized = true
}
val currentSteps = event.values[0].toInt()
val sessionSteps = currentSteps - initialSteps
Log.d("Стъпки", "Стъпки на сесия: $sessionSteps")
}
}
Сензорът за осветеност позволява адаптиране на интерфейса към условията на околната среда. При ниска осветеност (под 10 lx) може да се превключи на тъмна тема. При ярка слънчева светлина (над 10000 lx) — да се увеличи контрастът. За филтриране на шума се използва пълзяща средна на последните 5 стойности.
Android предоставя няколко виртуални сензора, които нямат пряк хардуерен еквивалент. Виртуалният сензор изчислява своите показания на базата на комбинация от физически сензори. Това намалява натоварването на разработчика — системата сама имплементира алгоритмите за сливане на данни.
Сензорът Gravity (TYPE_GRAVITY) извлича гравитационния компонент от показанията на акселерометъра с помощта на нискочестотен (low-pass) филтър. Linear Acceleration (TYPE_LINEAR_ACCELERATION) — обратно, премахва гравитацията и оставя само линейното ускорение на устройството. Сумата от Gravity и Linear Acceleration дава суровите показания на акселерометъра.
Rotation Vector — най-сложният виртуален сензор. Той комбинира данни от жироскоп (високочестотни промени), акселерометър (гравитационен вектор) и магнитометър (ориентация спрямо север). Резултатът — кватернион, описващ абсолютната ориентация на устройството в пространството. Използва се в AR приложения и VR очила.
Game Rotation Vector — опростена версия на Rotation Vector без използване на магнитометър. Това осигурява по-ниска точност на определяне на азимута, но по-висока честота на обновяване и стабилност. Препоръчва се за игри, където ниската латентност е важна, а не абсолютната ориентация спрямо север.
Сензорите — един от най-енергоемките компоненти на устройството. Акселерометърът и жироскопът при максимална честота на запитване могат да изтощят батерията за 3–4 часа непрекъсната работа. Оптимизацията на работата със сензори е критична задача за всяко приложение, използващо Sensors API.
Не използвайте SENSOR_DELAY_FASTEST без необходимост. За UI анимации е достатъчен SENSOR_DELAY_UI (60 ms). За определяне на ориентацията на екрана — SENSOR_DELAY_NORMAL (200 ms). Колкото по-висока е честотата, толкова повече време процесорът прекарва в активно състояние. Разликата между NORMAL и FASTEST е до 10x в консумацията на енергия.
От Android 8.0 (API 26) фоноворежимните приложения получават събития от сензори с намалена честота. В Android 12+ фоновият достъп до Motion и Position сензори изисква разрешение BODY_SENSORS_BACKGROUND. Отменяйте регистрацията на слушателя при преминаване на приложението във фонов режим — това удължава живота на батерията.
Използвайте пакетна обработка чрез метода registerListener(listener, sensor, delay, maxReportLatencyUs). Параметърът maxReportLatencyUs задава максималното закъснение на доставка на събития в микросекунди. При стойност 1000000 (1 секунда) събитията се натрупват във FIFO буфера на сензора и се доставят веднъж в секунда — процесорът по-рядко излиза от режим на сън.
Често задавани въпроси
Минималният набор — акселерометър и магнитометър. Тези два сензора присъстват на над 95% от устройствата. Жироскоп, сензор за осветеност и сензор за близост — на 70–80% от съвременните модели. Останалите сензори (налягане, температура, влажност, крачкомер) се срещат по-рядко и зависят от ценовата категория на устройството.
TYPE_ACCELEROMETER връща пълното ускорение, включително гравитацията (9.8 m/s²). TYPE_LINEAR_ACCELERATION връща ускорение без гравитация — само ускорението от движението на устройството. Ако устройството лежи неподвижно, акселерометърът ще покаже 9.8 m/s² по оста Z, а Linear Acceleration — 0 по всички оси.
Използвайте getPackageManager().hasSystemFeature() със съответните константи: FEATURE_SENSOR_ACCELEROMETER, FEATURE_SENSOR_GYROSCOPE, FEATURE_SENSOR_PROXIMITY и други. Алтернативен начин — извикайте getDefaultSensor(type) и проверете резултата за null. Първият начин е за предпочитане за проверка в етапа на инициализация.
Суровите данни от сензорите съдържат шум. За изглаждане използвайте нискочестотен (low-pass) филтър: newValue = alpha * rawValue + (1 — alpha) * previousValue. Коефициентът alpha = 0.1–0.3 дава добър баланс между отзивчивост и гладкост. За жироскопа се прилага и високочестотен (high-pass) филтър за премахване на нулевия дрейф.
Да, но с ограничения. От Android 8.0 честотата на събитията от сензори се намалява за фоноворежимни приложения. От Android 12 се изисква разрешение BODY_SENSORS_BACKGROUND за достъп до сензори на фонов режим. За фонова работа се препоръчва използването на Foreground Service с известие.
Обобщение
Ще разработим мобилно приложение под ключ
IT Sectr създава iOS и Android приложения за стартъпи и бизнеси от 2017 г. Ще ви консултираме и ще предложим най-доброто решение.
Прочетете също