Większość urządzeń z Androidem ma wbudowane czujniki, które mierzą ruch, orientację i różne warunki środowiskowe. Te czujniki mogą dostarczać dane nieprzetworzone z wysoką dokładnością i precyzją. Są przydatne, jeśli chcesz monitorować trójwymiarowy ruch lub pozycjonowanie urządzenia albo chcesz monitorować zmiany w otoczeniu wokół urządzenia. Na przykład gra może śledzić odczyty z czujnika grawitacji urządzenia, aby wywnioskować z nich złożone gesty i ruchy użytkownika, takie jak przechylanie, potrząsanie, obracanie czy kołysanie. Podobnie aplikacja pogodowa może używać czujnika temperatury i czujnika wilgotności, aby obliczać i przekazywać punkt rosy, a aplikacja podróżnicza może używać czujnika pola geomagnetycznego i akcelerometru, aby przekazywać kierunek kompasu.
Zapoznaj się z tymi materiałami:
Platforma Android obsługuje 3 szerokie kategorie czujników:
- Czujniki ruchu
Te czujniki mierzą siły przyspieszenia i obrotowe wzdłuż 3 osi. Ta kategoria obejmuje akcelerometry, czujniki grawitacyjne, żyroskopy i czujniki wektorów obrotowych.
- Czujniki środowiskowe
Te czujniki mierzą różne parametry środowiskowe, takie jak temperatura i ciśnienie powietrza, oświetlenie oraz wilgotność. Ta kategoria obejmuje barometry, fotometry i termometry.
- Czujniki położenia
Te czujniki mierzą fizyczną pozycję urządzenia. Ta kategoria obejmuje czujniki orientacji i magnetometry.
Za pomocą interfejsu API framework czujników Androida możesz uzyskać dostęp do czujników dostępnych na urządzeniu i pozyskiwać z nich dane. Platforma sensorów udostępnia kilka klas i interfejsów, które ułatwiają wykonywanie wielu zadań związanych z czujnikami. Za pomocą tej platformy możesz na przykład:
- Określ, które czujniki są dostępne na urządzeniu.
- określać możliwości poszczególnych czujników, takie jak maksymalny zasięg, producent, wymagania dotyczące zasilania i rozdzielczość;
- Pobieraj dane czujnika w postaci nieprzetworzonej i określaj minimalną częstotliwość ich pobierania.
- Rejestrowanie i odrejestrowanie detektorów zdarzeń czujnika, które monitorują zmiany czujnika.
W tym temacie znajdziesz omówienie czujników dostępnych na platformie Android. Zawiera on też wprowadzenie do frameworku czujników.
Wprowadzenie do czujników
Platforma czujników Androida umożliwia dostęp do wielu typów czujników. Niektóre z tych czujników są oparte na sprzęcie, a inne na oprogramowaniu. Czujniki sprzętowe to komponenty fizyczne wbudowane w telefon lub tablet. Pobierają one dane bezpośrednio z określonych właściwości środowiska, takich jak przyspieszenie, natężenie pola geomagnetycznego czy zmiana kątowa. Oprogramowanie czujników nie jest urządzeniem fizycznym, ale naśladuje działanie czujników sprzętowych. Czujniki oparte na oprogramowaniu uzyskują dane z co najmniej jednego czujnika opartego na sprzęcie i czasem są nazywane czujnikami wirtualnymi lub syntetycznymi. Przykładami czujników oprogramowania są czujnik przyspieszenia liniowego i czujnik przyspieszenia ziemskiego. W tabeli 1 podsumowano czujniki obsługiwane przez platformę Android.
Niewiele urządzenia z Androidem mają wszystkie typy czujników. Na przykład większość telefonów i tabletów ma akcelerometr i magnetometr, ale mniej urządzeń ma barometry lub termometry. Urządzenie może też mieć więcej niż 1 czujnik danego typu. Na przykład urządzenie może mieć 2 czujniki grawitacyjne, z których każdy ma inny zakres.
Czujnik | Typ | Opis | Częste zastosowania |
---|---|---|---|
TYPE_ACCELEROMETER |
Sprzęt | Mierzy przyspieszenie w m/s2, które jest stosowane na urządzeniu na wszystkich 3 osiach fizycznych (x, y i z), w tym siła grawitacji. | wykrywanie ruchu (potrząsanie, przechylanie itp.); |
TYPE_AMBIENT_TEMPERATURE |
Sprzęt | Mierzy temperaturę otoczenia w stopniach Celsjusza (°C). Zobacz notatkę poniżej. | monitorowanie temperatury powietrza; |
TYPE_GRAVITY |
Oprogramowanie lub sprzęt | Mierzy siłę grawitacji w m/s2, która działa na urządzenie na wszystkich 3 osiach fizycznych (x, y, z). | wykrywanie ruchu (potrząsanie, przechylanie itp.); |
TYPE_GYROSCOPE |
Sprzęt | Pomiar szybkości obrotu urządzenia w rad/s wokół każdej z 3 osi fizycznych (x, y i z). | wykrywanie obrotu (kręcenie, obracanie itp.); |
TYPE_LIGHT |
Sprzęt | Pomiar poziomu światła otoczenia (oświetlenia) w lx. | kontrolowanie jasności ekranu; |
TYPE_LINEAR_ACCELERATION |
Oprogramowanie lub sprzęt | Mierzy przyspieszenie w m/s2, które jest stosowane do urządzenia na wszystkich 3 osiach fizycznych (x, y i z), z wyłączeniem siły grawitacji. | Monitorowanie przyspieszenia wzdłuż jednej osi. |
TYPE_MAGNETIC_FIELD |
Sprzęt | Pomiar pola geomagnetycznego otoczenia na wszystkich 3 osiach fizycznych (x, y, z) w μT. | Tworzenie kompasu. |
TYPE_ORIENTATION |
Oprogramowanie | Pomiar stopnia obrotu urządzenia wokół wszystkich 3 fizycznych osi (x, y, z).
Od poziomu 3 interfejsu API możesz uzyskać matrycę pochylenia i matrycę obrotu urządzenia, używając czujnika grawitacyjnego i czujnika pola geomagnetycznego w połączeniu z metodą getRotationMatrix() . |
określanie położenia urządzenia; |
TYPE_PRESSURE |
Sprzęt | Pomiar ciśnienia powietrza otoczenia w hPa lub mbar. | monitorowanie zmian ciśnienia powietrza; |
TYPE_PROXIMITY |
Sprzęt | Pomiar odległości obiektu w cm od ekranu urządzenia. Ten czujnik jest zwykle używany do określania, czy telefon jest przyłożony do ucha. | Pozycja telefonu podczas rozmowy. |
TYPE_RELATIVE_HUMIDITY |
Sprzęt | Pomiar wilgotności względnej w środowisku w procentach (%). | Monitorowanie punktu rosy, wilgotności bezwzględnej i względnej. |
TYPE_ROTATION_VECTOR |
Oprogramowanie lub sprzęt | Pomiar orientacji urządzenia przez podanie 3 elementów wektora obrotu urządzenia. | wykrywanie ruchu i obrotu; |
TYPE_TEMPERATURE |
Sprzęt | Mierzy temperaturę urządzenia w stopniach Celsjusza (°C). Implementacja tego czujnika różni się w zależności od urządzenia. W poziomie API 14 został on zastąpiony czujnikiem TYPE_AMBIENT_TEMPERATURE . |
monitorowanie temperatury; |
Sensor Framework
Za pomocą interfejsu API czujników Androida możesz uzyskać dostęp do tych czujników i ich danych.
Ramka czujnika jest częścią pakietu android.hardware
i zawiera te klasy i interfejsy:
SensorManager
- Za pomocą tej klasy możesz utworzyć instancję usługi czujnika. Ta klasa udostępnia różne metody dostępu do czujników i wyświetlania ich listy, rejestrowania i rejestrowania detektorów zdarzeń czujników oraz uzyskiwania informacji o orientacji. Ta klasa zawiera też kilka stałych wartości czujników, które służą do raportowania dokładności czujników, ustawiania szybkości pozyskiwania danych i kalibrowania czujników.
Sensor
- Za pomocą tej klasy możesz utworzyć instancję konkretnego czujnika. Ta klasa udostępnia różne metody, które umożliwiają określenie możliwości czujnika.
SensorEvent
- System używa tej klasy do tworzenia obiektu zdarzenia czujnika, który zawiera informacje o zdarzeniu czujnika. Obiekt zdarzenia czujnika zawiera te informacje: nieprzetworzone dane czujnika, typ czujnika, który wygenerował zdarzenie, dokładność danych i znak czasu zdarzenia.
SensorEventListener
- Za pomocą tego interfejsu możesz utworzyć 2 metody wywołania zwrotnego, które będą otrzymywać powiadomienia (zdarzenia czujnika) w przypadku zmiany wartości czujnika lub zmiany jego dokładności.
W typowej aplikacji interfejsy API związane z czujnikiem służą do wykonywania 2 podstawowych zadań:
- Identyfikowanie czujników i ich możliwości
Identyfikowanie czujników i ich możliwości w czasie działania jest przydatne, jeśli aplikacja ma funkcje, które opierają się na określonych typach lub możliwościach czujników. Możesz na przykład zidentyfikować wszystkie czujniki obecne na urządzeniu i wyłączyć funkcje aplikacji, które korzystają z czujników, których nie ma na urządzeniu. Możesz też zidentyfikować wszystkie czujniki danego typu, aby wybrać implementację czujnika, która zapewni optymalną wydajność w Twojej aplikacji.
- Monitorowanie zdarzeń związanych z czujnikiem
Monitorowanie zdarzeń czujnika pozwala uzyskiwać nieprzetworzone dane z czujnika. Zdarzenie czujnika występuje za każdym razem, gdy czujnik wykryje zmianę w parametrach, które mierzy. Zdarzenie czujnika zawiera 4 rodzaje informacji: nazwę czujnika, który wywołał zdarzenie, sygnaturę czasową zdarzenia, dokładność zdarzenia oraz nieprzetworzone dane czujnika, które wywołały zdarzenie.
Dostępność czujnika
Dostępność czujników może się różnić w zależności od urządzenia, a także od wersji Androida. Dzieje się tak, ponieważ czujniki Androida zostały wprowadzone w ciągu kilku wersji platformy. Na przykład wiele czujników zostało wprowadzonych w Androidzie 1.5 (poziom interfejsu API 3), ale niektóre z nich nie zostały zaimplementowane i nie były dostępne do użycia aż do Androida 2.3 (poziom interfejsu API 9). W Androidzie 2.3 (poziom interfejsu API 9) i Androidzie 4.0 (poziom interfejsu API 14) wprowadzono też kilka czujników. Dwa czujniki zostały wycofane i zastąpione nowszymi, lepszymi czujnikami.
Tabela 2 zawiera podsumowanie dostępności poszczególnych czujników na poszczególnych platformach. Wyświetlane są tylko 4 platformy, ponieważ to one wymagają zmiany ustawień czujnika. Czujniki oznaczone jako przestarzałe są nadal dostępne na kolejnych platformach (o ile są obecne na urządzeniu), co jest zgodne z polityką Androida dotyczącą zgodności wstecznej.
Czujnik | Android 4.0 (poziom 14 interfejsu API) |
Android 2.3 (poziom 9 interfejsu API) |
Android 2.2 (poziom API 8) |
Android 1.5 (poziom 3 interfejsu API) |
---|---|---|---|---|
TYPE_ACCELEROMETER |
Tak | Tak | Tak | Tak |
TYPE_AMBIENT_TEMPERATURE |
Tak | nie dotyczy | nie dotyczy | nie dotyczy |
TYPE_GRAVITY |
Tak | Tak | nie dotyczy | nie dotyczy |
TYPE_GYROSCOPE |
Tak | Tak | n/a1 | n/a1 |
TYPE_LIGHT |
Tak | Tak | Tak | Tak |
TYPE_LINEAR_ACCELERATION |
Tak | Tak | nie dotyczy | nie dotyczy |
TYPE_MAGNETIC_FIELD |
Tak | Tak | Tak | Tak |
TYPE_ORIENTATION |
Tak2 | Tak2 | Tak2 | Tak |
TYPE_PRESSURE |
Tak | Tak | n/a1 | n/a1 |
TYPE_PROXIMITY |
Tak | Tak | Tak | Tak |
TYPE_RELATIVE_HUMIDITY |
Tak | nie dotyczy | nie dotyczy | nie dotyczy |
TYPE_ROTATION_VECTOR |
Tak | Tak | nie dotyczy | nie dotyczy |
TYPE_TEMPERATURE |
Tak2 | Tak | Tak | Tak |
1 Ten typ czujnika został dodany w Androidzie 1.5 (poziom interfejsu API 3), ale nie był dostępny do użycia do Androida 2.3 (poziom interfejsu API 9).
2 Ten czujnik jest dostępny, ale został wycofany.
Identyfikowanie czujników i ich możliwości
Platforma czujników Androida udostępnia kilka metod, które ułatwiają określenie w czasie wykonywania, które czujniki są dostępne na urządzeniu. Interfejs API udostępnia też metody, które umożliwiają określenie możliwości poszczególnych czujników, takich jak maksymalny zasięg, rozdzielczość i wymagania dotyczące zasilania.
Aby zidentyfikować czujniki na urządzeniu, musisz najpierw uzyskać odwołanie do usługi czujnika. W tym celu utwórz instancję klasy SensorManager
, wywołując metodę getSystemService()
i przekazując argument SENSOR_SERVICE
. Może to obejmować np. te funkcje:
Kotlin
private lateinit var sensorManager: SensorManager ... sensorManager = getSystemService(Context.SENSOR_SERVICE) as SensorManager
Java
private SensorManager sensorManager; ... sensorManager = (SensorManager) getSystemService(Context.SENSOR_SERVICE);
Następnie możesz uzyskać listę wszystkich czujników na urządzeniu, wywołując metodę getSensorList()
i korzystając z konstantej TYPE_ALL
. Może to obejmować np. te funkcje:
Kotlin
val deviceSensors: List<Sensor> = sensorManager.getSensorList(Sensor.TYPE_ALL)
Java
List<Sensor> deviceSensors = sensorManager.getSensorList(Sensor.TYPE_ALL);
Jeśli chcesz wyświetlić listę wszystkich czujników danego typu, zamiast TYPE_ALL
możesz użyć innej stałej, np. TYPE_GYROSCOPE
, TYPE_LINEAR_ACCELERATION
lub TYPE_GRAVITY
.
Możesz też sprawdzić, czy na urządzeniu jest określony typ czujnika, używając metody getDefaultSensor()
i przekazując stałą typu dla konkretnego czujnika. Jeśli urządzenie ma więcej niż 1 czujnik danego typu, jeden z nich musi zostać wyznaczony jako domyślny. Jeśli domyślny czujnik nie istnieje w przypadku danego typu czujnika, wywołanie metody zwraca wartość null, co oznacza, że urządzenie nie ma czujnika tego typu. Na przykład ten kod sprawdza, czy na urządzeniu jest magnetometr:
Kotlin
private lateinit var sensorManager: SensorManager ... sensorManager = getSystemService(Context.SENSOR_SERVICE) as SensorManager if (sensorManager.getDefaultSensor(Sensor.TYPE_MAGNETIC_FIELD) != null) { // Success! There's a magnetometer. } else { // Failure! No magnetometer. }
Java
private SensorManager sensorManager; ... sensorManager = (SensorManager) getSystemService(Context.SENSOR_SERVICE); if (sensorManager.getDefaultSensor(Sensor.TYPE_MAGNETIC_FIELD) != null){ // Success! There's a magnetometer. } else { // Failure! No magnetometer. }
Uwaga: Android nie wymaga od producentów urządzeń z Androidem instalowania określonych typów czujników, więc urządzenia mogą mieć szeroki zakres konfiguracji czujników.
Oprócz wyświetlania listy czujników na urządzeniu możesz też używać publicznych metod klasy Sensor
, aby określać możliwości i atrybuty poszczególnych czujników. Jest to przydatne, jeśli chcesz, aby aplikacja zachowywała się inaczej w zależności od tego, jakie czujniki lub funkcje czujników są dostępne na urządzeniu. Aby uzyskać rozdzielczość i maksymalny zakres pomiarowy czujnika, możesz na przykład użyć metod getResolution()
i getMaximumRange()
. Aby poznać wymagania dotyczące zasilania czujnika, możesz też użyć metody getPower()
.
2 publiczne metody są szczególnie przydatne, jeśli chcesz zoptymalizować aplikację pod kątem czujników innego producenta lub różnych wersji czujnika. Jeśli na przykład Twoja aplikacja musi monitorować gesty użytkownika, takie jak przechylanie i potrząsanie, możesz utworzyć jeden zestaw reguł filtrowania danych i optymalizacji dla nowszych urządzeń z czujnikiem grawitacji danego producenta oraz inny zestaw reguł filtrowania danych i optymalizacji dla urządzeń, które nie mają czujnika grawitacji, a mają tylko akcelerometr. Poniższy przykładowy kod pokazuje, jak użyć do tego metod getVendor()
i getVersion()
. W tym przykładzie szukamy czujnika grawitacyjnego, którego dostawcą jest Google LLC, a numer wersji to 3. Jeśli dany czujnik nie jest obecny na urządzeniu, próbujemy użyć akcelerometru.
Kotlin
private lateinit var sensorManager: SensorManager private var mSensor: Sensor? = null ... sensorManager = getSystemService(Context.SENSOR_SERVICE) as SensorManager if (sensorManager.getDefaultSensor(Sensor.TYPE_GRAVITY) != null) { val gravSensors: List<Sensor> = sensorManager.getSensorList(Sensor.TYPE_GRAVITY) // Use the version 3 gravity sensor. mSensor = gravSensors.firstOrNull { it.vendor.contains("Google LLC") && it.version == 3 } } if (mSensor == null) { // Use the accelerometer. mSensor = if (sensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER) != null) { sensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER) } else { // Sorry, there are no accelerometers on your device. // You can't play this game. null } }
Java
private SensorManager sensorManager; private Sensor mSensor; ... sensorManager = (SensorManager) getSystemService(Context.SENSOR_SERVICE); mSensor = null; if (sensorManager.getDefaultSensor(Sensor.TYPE_GRAVITY) != null){ List<Sensor> gravSensors = sensorManager.getSensorList(Sensor.TYPE_GRAVITY); for(int i=0; i<gravSensors.size(); i++) { if ((gravSensors.get(i).getVendor().contains("Google LLC")) && (gravSensors.get(i).getVersion() == 3)){ // Use the version 3 gravity sensor. mSensor = gravSensors.get(i); } } } if (mSensor == null){ // Use the accelerometer. if (sensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER) != null){ mSensor = sensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER); } else{ // Sorry, there are no accelerometers on your device. // You can't play this game. } }
Inną przydatną metodą jest getMinDelay()
, która zwraca minimalny przedział czasu (w mikrosekundach), którego może użyć czujnik do rejestrowania danych. Każdy czujnik, który zwraca wartość różną od 0 w przypadku metody getMinDelay()
, jest czujnikiem strumieniowym. Czujniki strumieniowe zbierają dane w regularnych odstępach czasu. Zostały one wprowadzone w Androidzie 2.3 (poziom interfejsu API 9). Jeśli po wywołaniu metody getMinDelay()
czujnik zwróci wartość 0, oznacza to, że nie jest to czujnik strumieniowy, ponieważ raportuje dane tylko wtedy, gdy nastąpi zmiana w parametrach, które mierzy.
Metoda getMinDelay()
jest przydatna, ponieważ pozwala określić maksymalną szybkość, z jaką czujnik może pobierać dane. Jeśli niektóre funkcje w aplikacji wymagają wysokich współczynników pozyskiwania danych lub czujnika strumieniowego, możesz użyć tej metody, aby określić, czy czujnik spełnia te wymagania, a następnie odpowiednio włączyć lub wyłączyć odpowiednie funkcje w aplikacji.
Uwaga: maksymalna szybkość pozyskiwania danych przez czujnik nie musi być szybkością, z jaką platforma czujnika dostarcza dane do aplikacji. Framework czujnika przekazuje dane za pomocą zdarzeń czujnika, a na szybkość, z jaką aplikacja otrzymuje zdarzenia czujnika, wpływa kilka czynników. Więcej informacji znajdziesz w artykule Monitorowanie zdarzeń czujnika.
Monitorowanie zdarzeń czujnika
Aby monitorować nieprzetworzone dane z czujnika, musisz zaimplementować 2 metody wywołania, które są dostępne w interfejsie SensorEventListener
: onAccuracyChanged()
i onSensorChanged()
. System Androida wywołuje te metody, gdy:
- Zmienia się dokładność czujnika.
W tym przypadku system wywołuje metodę
onAccuracyChanged()
, przekazując Ci odwołanie do obiektuSensor
, który uległ zmianie, oraz nową dokładność czujnika. Dokładność jest reprezentowana przez jedną z 4 konstant stanu:SENSOR_STATUS_ACCURACY_LOW
,SENSOR_STATUS_ACCURACY_MEDIUM
,SENSOR_STATUS_ACCURACY_HIGH
lubSENSOR_STATUS_UNRELIABLE
. - Czujnik zgłasza nową wartość.
W tym przypadku system wywołuje metodę
onSensorChanged()
, przekazując obiektSensorEvent
. ObiektSensorEvent
zawiera informacje o nowych danych z czujnika, w tym: dokładność danych, czujnik, który wygenerował dane, sygnaturę czasową, w której dane zostały wygenerowane, oraz nowe dane zarejestrowane przez czujnik.
Poniższy kod pokazuje, jak za pomocą metody onSensorChanged()
monitorować dane z czujnika światła. W tym przykładzie dane nieprzetworzone z czujnika są wyświetlane w elementach TextView
zdefiniowanych w pliku main.xml jako sensor_data
.
Kotlin
class SensorActivity : Activity(), SensorEventListener { private lateinit var sensorManager: SensorManager private var mLight: Sensor? = null public override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.main) sensorManager = getSystemService(Context.SENSOR_SERVICE) as SensorManager mLight = sensorManager.getDefaultSensor(Sensor.TYPE_LIGHT) } override fun onAccuracyChanged(sensor: Sensor, accuracy: Int) { // Do something here if sensor accuracy changes. } override fun onSensorChanged(event: SensorEvent) { // The light sensor returns a single value. // Many sensors return 3 values, one for each axis. val lux = event.values[0] // Do something with this sensor value. } override fun onResume() { super.onResume() mLight?.also { light -> sensorManager.registerListener(this, light, SensorManager.SENSOR_DELAY_NORMAL) } } override fun onPause() { super.onPause() sensorManager.unregisterListener(this) } }
Java
public class SensorActivity extends Activity implements SensorEventListener { private SensorManager sensorManager; private Sensor mLight; @Override public final void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); sensorManager = (SensorManager) getSystemService(Context.SENSOR_SERVICE); mLight = sensorManager.getDefaultSensor(Sensor.TYPE_LIGHT); } @Override public final void onAccuracyChanged(Sensor sensor, int accuracy) { // Do something here if sensor accuracy changes. } @Override public final void onSensorChanged(SensorEvent event) { // The light sensor returns a single value. // Many sensors return 3 values, one for each axis. float lux = event.values[0]; // Do something with this sensor value. } @Override protected void onResume() { super.onResume(); sensorManager.registerListener(this, mLight, SensorManager.SENSOR_DELAY_NORMAL); } @Override protected void onPause() { super.onPause(); sensorManager.unregisterListener(this); } }
W tym przykładzie podczas wywołania metody registerListener()
jest podawane domyślne opóźnienie danych (SENSOR_DELAY_NORMAL
). Opóźnienie danych (lub częstotliwość próbkowania) określa odstęp czasu, w jakim zdarzenia czujnika są wysyłane do aplikacji za pomocą metody wywołania zwrotnego onSensorChanged()
. Domyślne opóźnienie danych jest odpowiednie do monitorowania typowych zmian orientacji ekranu i wynosi 200 tys. mikrosekund. Możesz określić inne opóźnienia danych, takie jak SENSOR_DELAY_GAME
(20 tys. mikrosekund), SENSOR_DELAY_UI
(60 tys. mikrosekund) lub SENSOR_DELAY_FASTEST
(0 mikrosekund). Począwszy od Androida 3.0 (interfejs API na poziomie 11) możesz też określić opóźnienie jako wartość bezwzględną (w mikrosekundach).
Określone przez Ciebie opóźnienie jest tylko sugerowanym opóźnieniem. System Android i inne aplikacje mogą zmieniać to opóźnienie. Sprawdzoną metodą jest określenie jak największego opóźnienia, ponieważ system zwykle stosuje mniejsze opóźnienie niż określone przez Ciebie (czyli należy wybrać najniższą częstotliwość próbkowania, która nadal spełnia wymagania aplikacji). Użycie większego opóźnienia powoduje mniejsze obciążenie procesora, a co za tym idzie, mniejsze zużycie energii.
Nie ma żadnej publicznej metody określania szybkości, z jaką platforma sensorów wysyła do aplikacji zdarzenia sensora. Możesz jednak używać sygnatur czasowych powiązanych z poszczególnymi zdarzeniami sensora do obliczania szybkości próbkowania w ciągu kilku zdarzeń. Raz ustawionej częstotliwości próbkowania (opóźnienia) nie trzeba już zmieniać. Jeśli z jakiegoś powodu musisz zmienić opóźnienie, musisz najpierw anulować rejestrację czujnika, a potem ponownie go zarejestrować.
Pamiętaj też, że w tym przykładzie do rejestrowania i odrejestrowania czujnika używa się metod onResume()
i onPause()
. Zalecamy, aby zawsze wyłączać czujniki, których nie potrzebujesz, zwłaszcza gdy aktywność jest wstrzymana. Jeśli tego nie zrobisz, bateria może się rozładować w zaledwie kilka godzin, ponieważ niektóre czujniki mają znaczne wymagania dotyczące zasilania i mogą szybko wyczerpywać baterię. System nie wyłączy automatycznie czujników po wyłączeniu ekranu.
Obsługa różnych konfiguracji czujników
Android nie określa standardowej konfiguracji czujników na urządzeniach, co oznacza, że producenci urządzeń mogą stosować dowolną konfigurację czujników na swoich urządzeniach z Androidem. W efekcie urządzenia mogą zawierać różne czujniki w różnych konfiguracjach. Jeśli aplikacja korzysta z konkretnego typu czujnika, musisz się upewnić, że jest on obecny na urządzeniu, aby aplikacja mogła działać prawidłowo.
Aby sprawdzić, czy dany czujnik jest obecny na urządzeniu, możesz skorzystać z 2 opcji:
- Wykrywanie czujników w czasie działania aplikacji i właściwe włączanie lub wyłączanie funkcji aplikacji.
- Używaj filtrów Google Play, aby kierować reklamy na urządzenia z określonymi konfiguracjami czujników.
W następnych sekcjach omawiamy każdą z nich.
Wykrywanie czujników w czasie działania aplikacji
Jeśli Twoja aplikacja używa określonego typu czujnika, ale nie jest od niego zależna, możesz użyć frameworku czujników, aby wykryć czujnik w czasie działania, a następnie odpowiednio wyłączyć lub włączyć funkcje aplikacji. Na przykład aplikacja do nawigacji może używać czujnika temperatury, czujnika ciśnienia, czujnika GPS i czujnika pola magnetycznego, aby wyświetlać temperaturę, ciśnienie barometryczne, lokalizację i kierunek kompasu. Jeśli urządzenie nie ma czujnika ciśnienia, możesz użyć frameworka czujnika, aby wykryć brak czujnika ciśnienia w czasie działania, a następnie wyłączyć część interfejsu aplikacji, która wyświetla ciśnienie. Na przykład ten kod sprawdza, czy na urządzeniu jest czujnik ciśnienia:
Kotlin
private lateinit var sensorManager: SensorManager ... sensorManager = getSystemService(Context.SENSOR_SERVICE) as SensorManager if (sensorManager.getDefaultSensor(Sensor.TYPE_PRESSURE) != null) { // Success! There's a pressure sensor. } else { // Failure! No pressure sensor. }
Java
private SensorManager sensorManager; ... sensorManager = (SensorManager) getSystemService(Context.SENSOR_SERVICE); if (sensorManager.getDefaultSensor(Sensor.TYPE_PRESSURE) != null){ // Success! There's a pressure sensor. } else { // Failure! No pressure sensor. }
Używanie filtrów Google Play do kierowania na konkretne konfiguracje czujników
Jeśli publikujesz aplikację w Google Play, możesz użyć elementu <uses-feature>
w pliku manifestu, aby odfiltrować aplikację z urządzeń, które nie mają odpowiedniej konfiguracji czujników dla Twojej aplikacji. Element <uses-feature>
zawiera kilka deskryptorów sprzętowych, które umożliwiają filtrowanie aplikacji na podstawie obecności określonych czujników. Czujniki, które możesz uwzględnić: akcelerometr, barometr, kompas (pole geomagnetyczne), żyroskop, światło i zbliżenie. Poniżej znajduje się przykładowy wpis w pliku manifestu, który filtruje aplikacje bez akcelerometru:
<uses-feature android:name="android.hardware.sensor.accelerometer" android:required="true" />
Jeśli dodasz ten element i opis do pliku manifestu aplikacji, użytkownicy zobaczą Twoją aplikację w Google Play tylko wtedy, gdy ich urządzenie będzie wyposażone w akcelerometr.
Wartość opisu powinna wynosić android:required="true"
tylko wtedy, gdy aplikacja całkowicie polega na określonym czujniku. Jeśli aplikacja używa czujnika do niektórych funkcji, ale działa również bez niego, należy uwzględnić czujnik w elemencie <uses-feature>
, ale ustawić deskryptor na android:required="false"
. Dzięki temu urządzenia będą mogły instalować Twoją aplikację, nawet jeśli nie mają tego konkretnego czujnika. Jest to też sprawdzona metoda zarządzania projektem, która pomaga śledzić funkcje używane przez aplikację.
Pamiętaj, że jeśli Twoja aplikacja używa określonego czujnika, ale działa bez niego, powinna wykrywać czujnik w czasie wykonywania i w odpowiednich przypadkach wyłączać lub włączać funkcje aplikacji.
System współrzędnych czujnika
Ogólnie rzecz biorąc, do wyrażania wartości danych framework czujnika używa standardowego układu współrzędnych 3-osiowych. W przypadku większości czujników układ współrzędnych jest zdefiniowany względem ekranu urządzenia, gdy urządzenie jest trzymane w domyślnej orientacji (patrz rysunek 1). Gdy urządzenie jest trzymane w domyślnej orientacji, oś X jest pozioma i wskazuje w prawo, oś Y jest pionowa i wskazuje w górę, a oś Z wskazuje na zewnątrz ekranu. W tym systemie współrzędne za ekranem mają ujemne wartości Z. Ten układ współrzędnych jest używany przez te czujniki:
- Czujnik przyspieszenia
- Czujnik przyspieszenia
- Żyroskop
- Czujnik przyspieszenia liniowego
- Czujnik pola geomagnetycznego
Najważniejsze, co należy wiedzieć o tym układzie współrzędnych, to fakt, że osie nie zmieniają się, gdy zmienia się orientacja ekranu urządzenia. Oznacza to, że układ współrzędnych czujnika nigdy się nie zmienia, gdy urządzenie się porusza. Działania te są takie same jak w systemie współrzędnych OpenGL.
Inną kwestią, którą należy wziąć pod uwagę, jest to, że aplikacja nie może zakładać, że naturalny (domyślny) kierunek urządzenia to orientacja pionowa. Naturalna orientacja wielu tabletów to orientacja pozioma. System współrzędnych czujnika zawsze opiera się na naturalnej orientacji urządzenia.
Jeśli aplikacja dopasowuje dane czujnika do wyświetlacza, musisz użyć metody getRotation()
, aby określić obrót ekranu, a potem metody remapCoordinateSystem()
, aby zmapować współrzędne czujnika na współrzędne ekranu. Musisz to zrobić, nawet jeśli manifest określa wyświetlanie tylko w orientacji pionowej.
Uwaga: niektóre czujniki i metody używają układu współrzędnych, który jest względny względem układu odniesienia świata (w opozycji do układu odniesienia urządzenia). Te czujniki i metody zwracają dane, które reprezentują ruch lub położenie urządzenia względem Ziemi. Więcej informacji znajdziesz w metodach getOrientation()
i getRotationMatrix()
, czujniku orientacji oraz czujniku wektorowym obrotu.
Ograniczanie szybkości czujnika
Aby chronić potencjalnie poufne informacje o użytkownikach, jeśli Twoja aplikacja jest kierowana na Androida 12 (poziom API 31) lub nowszego, system ogranicza częstotliwość odświeżania danych z niektórychś czujników ruchu i czujników położenia. Te dane obejmują wartości zarejestrowane przez akcelerometr, żyroskop i czujnik pola geomagnetycznego urządzenia.
Limit częstotliwości odświeżania zależy od sposobu uzyskiwania dostępu do danych z czujników:
- Jeśli wywołasz metodę
registerListener()
, aby monitorować zdarzenia czujnika, częstotliwość próbkowania czujnika jest ograniczona do 200 Hz. Dotyczy to wszystkich przeciążonych wariantów metodyregisterListener()
. - Jeśli używasz klasy
SensorDirectChannel
, częstotliwość próbkowania czujnika jest ograniczona doRATE_NORMAL
, co zwykle wynosi około 50 Hz.
Jeśli aplikacja musi zbierać dane z czujnika ruchu z większą częstotliwością, musisz zadeklarować uprawnienie HIGH_SAMPLING_RATE_SENSORS
, jak pokazano w tym fragmencie kodu. Jeśli jednak aplikacja próbuje zbierać dane z czujnika ruchu z większą częstotliwością bez deklarowania tego uprawnienia, wystąpi błąd SecurityException
.
<manifest ...> <uses-permission android:name="android.permission.HIGH_SAMPLING_RATE_SENSORS"/> <application ...> ... </application> </manifest>
Sprawdzone metody uzyskiwania dostępu do czujników i ich używania
Podczas projektowania implementacji czujnika postępuj zgodnie ze wskazówkami podanymi w tej sekcji. Te wytyczne to zalecane najlepsze praktyki dla wszystkich, którzy korzystają z ramowego interfejsu czujników do uzyskiwania dostępu do czujników i pozyskiwania danych z nich.
zbierać dane z czujników tylko na pierwszym planie;
Na urządzeniach z Androidem 9 (poziom interfejsu API 28) lub nowszym aplikacje działające w tle podlegają tym ograniczeniom:
- Czujniki, które używają ciągłego trybu raportowania, takie jak akcelerometry i żyroskopy, nie otrzymują zdarzeń.
- Czujniki, które używają trybów raportowania na podstawie zmian lub jednorazowego, nie otrzymują zdarzeń.
Z powodu tych ograniczeń najlepiej wykrywać zdarzenia czujnika, gdy aplikacja jest na pierwszym planie lub gdy jest częścią usługi na pierwszym planie.
Rejestrowanie czujników
Po zakończeniu korzystania z czujnika lub wstrzymaniu jego działania pamiętaj o odrejestrowaniu jego listenera. Jeśli czujnik jest zarejestrowany, a jego aktywność jest wstrzymana, nadal będzie zbierać dane i wykorzystywać zasoby baterii, chyba że zarejestrujesz go ponownie. Poniższy kod pokazuje, jak użyć metody onPause()
, aby zarejestrować odbiornik:
Kotlin
private lateinit var sensorManager: SensorManager ... override fun onPause() { super.onPause() sensorManager.unregisterListener(this) }
Java
private SensorManager sensorManager; ... @Override protected void onPause() { super.onPause(); sensorManager.unregisterListener(this); }
Więcej informacji znajdziesz w artykule unregisterListener(SensorEventListener)
.
Testowanie w Emulatorze Androida
Emulator Androida zawiera zestaw elementów sterujących wirtualnym czujnikiem, które umożliwiają testowanie czujników takich jak akcelerometr, temperatura otoczenia, magnetometr, zbliżenie, światło itp.
Emulator korzysta z połączenia z urządzeniem z Androidem, na którym działa aplikacja SdkControllerSensor. Pamiętaj, że ta aplikacja jest dostępna tylko na urządzeniach z Androidem 4.0 (poziom interfejsu API 14) lub nowszym. (jeśli urządzenie ma Androida 4.0, musi mieć zainstalowaną wersję 2). Aplikacja SdkControllerSensor monitoruje zmiany w czujnikach na urządzeniu i przekazuje je do emulatora. Następnie emulowany obraz jest przekształcany na podstawie nowych wartości, które otrzymuje z czujników na urządzeniu.
Kod źródłowy aplikacji SdkControllerSensor możesz wyświetlić w tym miejscu:
$ your-android-sdk-directory/tools/apps/SdkController
Aby przesyłać dane między urządzeniem a emulatorem, wykonaj te czynności:
- Sprawdź, czy na urządzeniu debugowanie USB jest włączone.
- Podłącz urządzenie do komputera programistycznego za pomocą kabla USB.
- Uruchom na urządzeniu aplikację SdkControllerSensor.
- W aplikacji wybierz czujniki, które chcesz emulować.
Uruchom to polecenie
adb
:- Uruchom emulator. Teraz możesz stosować transformacje w emulatorze, przenosząc urządzenie.
$ adb forward tcp:1968 tcp:1968
Uwaga: jeśli ruchy na fizycznym urządzeniu nie powodują zmiany w emulatorze, spróbuj ponownie uruchomić polecenie adb
z kroku 5.
Więcej informacji znajdziesz w przewodniku po emulatorze Androida.
Nie blokuj metody onSensorChanged().
Dane czujnika mogą się zmieniać z dużą częstotliwością, co oznacza, że system może dość często wywoływać metodę onSensorChanged(SensorEvent)
. Sprawdzoną metodą jest ograniczenie do minimum działań wykonywanych w ramach metody onSensorChanged(SensorEvent)
, aby jej nie blokować. Jeśli Twoja aplikacja wymaga od Ciebie odfiltrowania lub zredukowania danych z czujnika, wykonaj tę czynność poza metodą onSensorChanged(SensorEvent)
.
Unikaj korzystania ze starszych metod i typów czujników
Wycofano kilka metod i stałych.
W szczególności wycofany został typ czujnika TYPE_ORIENTATION
. Aby uzyskać dane orientacji, użyj metody getOrientation()
. Wycofany został też typ czujnika TYPE_TEMPERATURE
. Na urządzeniach z Androidem 4.0 należy użyć typu czujnika TYPE_AMBIENT_TEMPERATURE
.
Sprawdzanie czujników przed ich użyciem
Zanim spróbujesz pobrać dane z urządzenia, zawsze sprawdzaj, czy ma ono czujnik. Nie zakładaj, że czujnik istnieje, tylko dlatego, że jest często używany. Producenci urządzeń nie muszą umieszczać w nich żadnych konkretnych czujników.
Uważnie wybieraj opóźnienia czujników
Podczas rejestrowania czujnika za pomocą metody registerListener()
pamiętaj, aby wybrać szybkość dostawy odpowiednią do Twojej aplikacji lub przypadku użycia. Czujniki mogą dostarczać dane z bardzo dużą częstotliwością. Zezwalanie systemowi na wysyłanie dodatkowych danych, których nie potrzebujesz, marnuje zasoby systemowe i energię baterii.