Sensors API — to interfejs platformy Android do dostępu do sprzętowych czujników urządzenia. API zapewnia jednolity sposób pobierania danych z akcelerometru, żyroskopu, magnetometru, czujnika oświetlenia i innych sensorów. Według danych Google, 2026, ponad 95% nowoczesnych urządzeń z Androidem jest wyposażonych w co najmniej trzy typy czujników, co otwiera szerokie możliwości tworzenia aplikacji kontekstowych.
Najważniejsze
Sensors API — to interfejs programowy Android Framework, zapewniający programistom dostęp do sprzętowych czujników urządzenia mobilnego. API wchodzi w skład Android SDK od wersji 1.0 i jest dostępne przez pakiet android.hardware. Interfejs abstrahuje różnice między implementacjami czujników różnych producentów — Qualcomm, MediaTek, Samsung Exynos — i zapewnia jednolity model pracy z sensorami.
API umożliwia wykonanie trzech kluczowych operacji: pobranie listy dostępnych czujników, rejestrację słuchaczy do otrzymywania danych oraz zarządzanie częstotliwością aktualizacji odczytów. SensorManager jest centralnym punktem dostępu do wszystkich operacji. System powiadamia aplikację przez callback onSensorChanged, przekazując obiekt SensorEvent z tablicą wartości float.
W trakcie istnienia Androida API przeszło kilka etapów rozwoju. W wersji 2.3 (Gingerbread) pojawiło się wsparcie Batch Processing — grupowanie zdarzeń czujnika w celu zmniejszenia zużycia energii. Android 4.0 (Ice Cream Sandwich) dodał wirtualne czujniki Gravity i Linear Acceleration. Od Androida 8.0 system ograniczył dostęp w tle do czujników w celu zwiększenia prywatności.
val sensorManager = getSystemService(Context.SENSOR_SERVICE) as SensorManager
val sensorList: List<Sensor> = sensorManager.getSensorList(Sensor.TYPE_ALL)
sensorList.forEach { sensor ->
Log.d("Czujniki", "${sensor.name} — ${sensor.type}")
}
Nie wszystkie urządzenia mają ten sam zestaw czujników. Aplikacja musi sprawdzać dostępność konkretnego sensora przed rejestracją słuchacza. Emulator Androida obsługuje tylko ograniczony zestaw czujników — akcelerometr i magnetometr. Do testowania pozostałych czujników wymagane jest fizyczne urządzenie. Od Androida 12 aplikacje muszą deklarować użycie czujników w manifeście.
Android klasyfikuje wszystkie czujniki na trzy kategorie: Motion (ruch), Position (położenie) i Environment (otoczenie). Czujniki Motion obejmują akcelerometr, żyroskop i licznik kroków. Czujniki Position zawierają magnetometr i Orientation. Environment — czujnik oświetlenia, ciśnienia, temperatury i wilgotności.
| Kategoria | Czujniki | Typ (stała) | Fizyczny/Wirtualny |
|---|---|---|---|
| Motion | Akcelerometr | TYPE_ACCELEROMETER | Fizyczny |
| Motion | Żyroskop | TYPE_GYROSCOPE | Fizyczny |
| Motion | Licznik kroków | TYPE_STEP_COUNTER | Fizyczny |
| Motion | Gravity | TYPE_GRAVITY | Wirtualny |
| Position | Magnetometr | TYPE_MAGNETIC_FIELD | Fizyczny |
| Position | Rotation Vector | TYPE_ROTATION_VECTOR | Wirtualny |
| Environment | Oświetlenie | TYPE_LIGHT | Fizyczny |
| Environment | Ciśnienie | TYPE_PRESSURE | Fizyczny |
Akcelerometr mierzy przyspieszenie w m/s² w trzech osiach — X, Y i Z. Wartości obejmują grawitację (9.8 m/s² w osi Z w stanie spoczynku). Żyroskop mierzy prędkość kątową w rad/s — prędkość obrotu urządzenia wokół każdej osi. Kombinacja tych dwóch czujników jest używana w nawigacji, grach i aplikacjach 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) {}
}
Magnetometr (TYPE_MAGNETIC_FIELD) mierzy pole magnetyczne w mikroteślach (µT) w trzech osiach. Używany jako cyfrowy kompas — w połączeniu z akcelerometrem pozwala określić azymut urządzenia. Rotation Vector — wirtualny czujnik, który łączy dane z żyroskopu, akcelerometru i magnetometru w celu dokładnego określenia orientacji.
Czujnik oświetlenia (TYPE_LIGHT) mierzy poziom oświetlenia zewnętrznego w luksach (lx). Używany do automatycznej regulacji jasności ekranu. Czujnik ciśnienia (TYPE_PRESSURE) mierzy ciśnienie atmosferyczne w hektopaskalach (hPa) — stosowany w aplikacjach nawigacyjnych do określania wysokości nad poziomem morza.
Sensors API jest zbudowany na architekturze klient-serwer. Aplikacja występuje jako klient, SensorManager — jako usługa systemu Android. Gdy aplikacja rejestruje słuchacza przez registerListener, SensorManager komunikuje się z HAL (Hardware Abstraction Layer) — niskopoziomowym sterownikiem czujnika działającym na poziomie jądra Linux.
Sterownik HAL otrzymuje surowe dane z układu scalonego, filtruje szumy i przekazuje je w górę stosu — przez SensorService (proces systemowy) do aplikacji. Dane są przesyłane jako obiekt SensorEvent, zawierający tablicę wartości float values i znacznik czasu w nanosekundach. Częstotliwość zdarzeń zależy od wybranego trybu dostarczania.
Podczas rejestracji słuchacza programista określa żądane opóźnienie między zdarzeniami za pomocą stałych SENSOR_DELAY_NORMAL (200 ms), SENSOR_DELAY_UI (60 ms), SENSOR_DELAY_GAME (20 ms) i SENSOR_DELAY_FASTEST (0 ms — maksymalna częstotliwość). Rzeczywista częstotliwość może się różnić — system optymalizuje zużycie energii.
val sensor = sensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER)
sensorManager.registerListener(
this,
sensor,
SensorManager.SENSOR_DELAY_GAME
)
Od Androida 2.3 czujniki obsługują tryb wsadowy — zdarzenia gromadzą się w buforze FIFO i są dostarczane w jednym pakiecie. Pozwala to procesorowi dłużej pozostawać w trybie uśpienia, zmniejszając zużycie energii nawet o 60%. Rozmiar bufora zależy od układu czujnika i jest określony w polu Sensor.fifoMaxEventCount.
SensorManager — usługa systemowa dostępna przez getSystemService(Context.SENSOR_SERVICE). Udostępnia metody do pobierania listy czujników, uzyskiwania konkretnego czujnika domyślnego oraz rejestracji/anulowania słuchaczy. SensorManager — singleton, jedna instancja na całą aplikację.
Przed użyciem czujnika należy sprawdzić jego dostępność. Metoda getDefaultSensor(type) zwraca null, jeśli czujnik nie jest dostępny na urządzeniu. Otrzymanie null bez sprawdzenia doprowadzi do NullPointerException przy rejestracji słuchacza. Listę wszystkich czujników zwraca metoda getSensorList(type).
fun checkSensorAvailability(type: Int): Boolean {
val sensorManager =
getSystemService(Context.SENSOR_SERVICE) as SensorManager
return sensorManager.getDefaultSensor(type) != null
}
// Użycie:
if (checkSensorAvailability(Sensor.TYPE_GYROSCOPE)) {
Log.d("Sensor", "Żyroskop dostępny")
}
Słuchacz jest rejestrowany przez registerListener(listener, sensor, delay). Ważne: anulowanie słuchacza należy wykonać w metodzie onPause() przez unregisterListener(listener). Jeśli tego nie zrobisz, aplikacja będzie nadal otrzymywać zdarzenia czujnika, rozładowując baterię nawet w tle. Android 8+ wyświetla ostrzeżenie w logcat o niezarejestrowanych słuchaczach.
Metoda onAccuracyChanged(sensor, accuracy) jest wywoływana przy zmianie dokładności czujnika. Wartości: SENSOR_STATUS_ACCURACY_HIGH, MEDIUM, LOW i UNRELIABLE. Po otrzymaniu UNRELIABLE dane czujnika należy ignorować do czasu przywrócenia dokładności. Na przykład magnetometr wymaga kalibracji — potrząśnięcia urządzeniem w kształcie ósemki.
Rozważmy praktyczne przykłady użycia Sensors API w aplikacjach Android w Kotlin. Pierwszy przykład — określanie orientacji ekranu za pomocą akcelerometru. Drugi — odczytywanie wskazań magnetometru z kalibracją. Trzeci — detektor kroków z użyciem licznika kroków.
Do określenia orientacji używana jest kombinacja akcelerometru i magnetometru. Metoda SensorManager.getRotationMatrix() oblicza macierz obrotu, a getOrientation() wyodrębnia kąty pitch, roll i azimuth. Azimuth (azymut) — kąt względem północy magnetycznej w radianach.
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] — azymut, [1] — pochylenie, [2] — obrót
val azimuthDeg = Math.toDegrees(orientation[0].toDouble())
Licznik kroków (TYPE_STEP_COUNTER) zwraca całkowitą liczbę kroków wykonanych przez użytkownika od ostatniego restartu urządzenia. Czujnik działa na poziomie sprzętowym — jest aktywny nawet gdy aplikacja nie jest uruchomiona. Aby uzyskać liczbę kroków w sesji, należy zapisać wartość początkową i obliczyć różnicę.
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("Kroki", "Kroki w sesji: $sessionSteps")
}
}
Czujnik oświetlenia pozwala dostosować interfejs do warunków otoczenia. Przy niskim oświetleniu (poniżej 10 lx) można przełączyć się na ciemny motyw. Przy jasnym świetle słonecznym (powyżej 10000 lx) — zwiększyć kontrast. Do filtrowania szumów używana jest średnia ruchoma z 5 ostatnich wartości.
Android udostępnia kilka wirtualnych czujników, które nie mają bezpośredniego odpowiednika sprzętowego. Wirtualny czujnik oblicza swoje wskazania na podstawie kombinacji fizycznych sensorów. Zmniejsza to obciążenie programisty — system sam implementuje algorytmy fuzji danych.
Czujnik Gravity (TYPE_GRAVITY) wyodrębnia składową grawitacyjną z odczytów akcelerometru za pomocą filtra dolnoprzepustowego (low-pass). Linear Acceleration (TYPE_LINEAR_ACCELERATION) — odwrotnie, usuwa grawitację i pozostawia tylko liniowe przyspieszenie urządzenia. Suma Gravity i Linear Acceleration daje surowe odczyty akcelerometru.
Rotation Vector — najbardziej złożony wirtualny czujnik. Łączy dane z żyroskopu (zmiany wysokiej częstotliwości), akcelerometru (wektor grawitacji) i magnetometru (orientacja względem północy). Wynik — kwaternion opisujący absolutną orientację urządzenia w przestrzeni. Używany w aplikacjach AR i goglach VR.
Game Rotation Vector — uproszczona wersja Rotation Vector bez użycia magnetometru. Daje to niższą dokładność określania azymutu, ale wyższą częstotliwość aktualizacji i stabilność. Zalecany do gier, gdzie ważne jest niskie opóźnienie, a nie absolutna orientacja względem północy.
Czujniki — jedne z najbardziej energochłonnych komponentów urządzenia. Akcelerometr i żyroskop przy maksymalnej częstotliwości odpytywania mogą rozładować baterię w ciągu 3–4 godzin ciągłej pracy. Optymalizacja pracy z czujnikami to krytyczne zadanie dla każdej aplikacji używającej Sensors API.
Nie używaj SENSOR_DELAY_FASTEST bez potrzeby. Do animacji UI wystarczy SENSOR_DELAY_UI (60 ms). Do określania orientacji ekranu — SENSOR_DELAY_NORMAL (200 ms). Im wyższa częstotliwość, tym więcej czasu procesor spędza w stanie aktywnym. Różnica między NORMAL a FASTEST wynosi do 10x w zużyciu energii.
Od Androida 8.0 (API 26) aplikacje w tle otrzymują zdarzenia czujników ze zmniejszoną częstotliwością. W Androidzie 12+ dostęp w tle do czujników Motion i Position wymaga uprawnienia BODY_SENSORS_BACKGROUND. Anuluj rejestrację słuchacza przy przejściu aplikacji w tryb tła — wydłuża to czas pracy urządzenia.
Używaj przetwarzania wsadowego przez metodę registerListener(listener, sensor, delay, maxReportLatencyUs). Parametr maxReportLatencyUs określa maksymalne opóźnienie dostarczania zdarzeń w mikrosekundach. Przy wartości 1000000 (1 sekunda) zdarzenia gromadzą się w buforze FIFO czujnika i są dostarczane raz na sekundę — procesor rzadziej wychodzi ze stanu uśpienia.
Często zadawane pytania
Minimalny zestaw — akcelerometr i magnetometr. Te dwa czujniki są obecne na ponad 95% urządzeń. Żyroskop, czujnik oświetlenia i czujnik zbliżeniowy — na 70–80% nowoczesnych modeli. Pozostałe czujniki (ciśnienie, temperatura, wilgotność, licznik kroków) występują rzadziej i zależą od kategorii cenowej urządzenia.
TYPE_ACCELEROMETER zwraca pełne przyspieszenie, włączając grawitację (9.8 m/s²). TYPE_LINEAR_ACCELERATION zwraca przyspieszenie bez grawitacji — tylko przyspieszenie od ruchu urządzenia. Jeśli urządzenie leży nieruchomo, akcelerometr pokaże 9.8 m/s² w osi Z, a Linear Acceleration — 0 we wszystkich osiach.
Użyj getPackageManager().hasSystemFeature() z odpowiednimi stałymi: FEATURE_SENSOR_ACCELEROMETER, FEATURE_SENSOR_GYROSCOPE, FEATURE_SENSOR_PROXIMITY i innymi. Alternatywny sposób — wywołaj getDefaultSensor(type) i sprawdź wynik na null. Pierwszy sposób jest preferowany do sprawdzania na etapie inicjalizacji.
Surowe dane z czujników zawierają szumy. Do wygładzania używaj filtra dolnoprzepustowego (low-pass): newValue = alpha * rawValue + (1 — alpha) * previousValue. Współczynnik alpha = 0.1–0.3 daje dobry balans między responsywnością a gładkością. Dla żyroskopu stosuje się również filtr górnoprzepustowy (high-pass) do usuwania dryftu zera.
Tak, ale z ograniczeniami. Od Androida 8.0 częstotliwość zdarzeń czujnika jest zmniejszana dla aplikacji w tle. Od Androida 12 wymagane jest uprawnienie BODY_SENSORS_BACKGROUND do dostępu do czujników w tle. Do pracy w tle zaleca się użycie Foreground Service z powiadomieniem.
Podsumowanie
Opracujemy aplikację mobilną pod klucz
IT Sectr tworzy aplikacje na iOS i Androida dla startupów i firm od 2017 roku. Doradzimy Ci i zaproponujemy najlepsze rozwiązanie.
Przeczytaj również