Większość urządzeń z Androidem ma wbudowane czujniki, które mierzą ruch, orientację i różne warunki środowiskowe. Czujniki te mogą dostarczać nieprzetworzone dane z dużą precyzją i dokładnością. Są przydatne, gdy chcesz monitorować trójwymiarowe ruchy lub położenie urządzenia albo monitorować zmiany w otoczeniu, które znajduje się w pobliżu urządzenia. Na przykład gra może śledzić odczyty z czujnika grawitacji urządzenia, aby określać złożone gesty i ruchy użytkownika, takie jak przechylanie, wstrząsanie, obracanie czy kołysanie. Podobnie aplikacja pogodowa może korzystać z czujnika temperatury i wilgotności w urządzeniu do obliczania i raportowania punktu rosy, a aplikacja podróżna – do zgłaszania położenia kompasu – z czujnika pola geomagnetycznego i akcelerometru.
Zapoznaj się z tymi powiązanymi materiałami:
Platforma Android obsługuje 3 ogólne kategorie czujników:
- Czujniki ruchu
Czujniki te mierzą siły przyspieszenia i siły obrotowe wzdłuż 3 osi. Do tej kategorii należą akcelerometry, czujniki grawitacji, żyroskopy i czujniki wektorowe obrotowe.
- Czujniki środowiskowe
Czujniki te mierzą różne parametry środowiskowe, takie jak temperatura i ciśnienie powietrza, oświetlenie i wilgotność. Ta kategoria obejmuje barometry, fotometry i termometry.
- Czujniki pozycji
Czujniki te mierzą fizyczne położenie urządzenia. Ta kategoria obejmuje czujniki orientacji i magnetometry.
Możesz korzystać z czujników dostępnych w urządzeniu i pozyskiwać nieprzetworzone dane z czujników za pomocą platformy czujników w Androidzie. Struktura czujników ma kilka klas i interfejsów, które pomagają wykonywać wiele różnych zadań związanych z czujnikami. Można na przykład użyć struktury czujnika do tych zadań:
- Określ, które czujniki są dostępne na urządzeniu.
- Określ możliwości konkretnego czujnika, na przykład jego maksymalny zasięg, producenta, wymagania dotyczące zasilania i rozdzielczość.
- Pobierz nieprzetworzone dane z czujnika i określ minimalną szybkość pobierania danych z czujnika.
- Zarejestruj i wyrejestruj detektory zdarzeń z czujnika, które monitorują zmiany w czujnikach.
Ten artykuł zawiera omówienie czujników dostępnych na platformie Androida. Omówiono także strukturę czujników.
Wprowadzenie do czujników
Struktura czujników w Androidzie zapewnia dostęp do wielu rodzajów czujników. Niektóre z tych czujników są sprzętowe, a inne programowe. Czujniki sprzętowe to fizyczne komponenty wbudowane w słuchawkę lub tablet. Pozyskują dane, mierząc bezpośrednio określone właściwości środowiska, takie jak przyspieszenie, natężenie pola geomagnetycznego czy zmiany kątowe. Czujniki programowe nie są urządzeniami fizycznymi, chociaż naśladują je sprzętowe. Czujniki programowe pobierają dane z co najmniej jednego z czujników sprzętowych. Czasami są nazywane czujnikami wirtualnymi lub czujnikami syntetycznymi. Czujnik przyspieszenia liniowego oraz czujnik grawitacji to przykłady czujników programowych. W tabeli 1 zestawiamy czujniki obsługiwane przez platformę Androida.
Niewiele urządzeń z Androidem ma czujniki każdego typu. Na przykład większość telefonów i tabletów ma akcelerometr i magnetometr, ale mało urządzeń posiada barometry lub termometry. Ponadto urządzenie może mieć więcej niż 1 czujnik danego typu. Na przykład urządzenie może mieć dwa czujniki grawitacji, z których każdy ma inny zasięg.
Czujnik | Typ | Opis | Częste zastosowania |
---|---|---|---|
TYPE_ACCELEROMETER |
Sprzęt | Mierzy siłę przyspieszenia w m/s2 działającą na urządzenie na wszystkich 3 osiach fizycznych (x, y i z), w tym siłę grawitacji. | Wykrywanie ruchu (potrząsanie, pochylanie itp.). |
TYPE_AMBIENT_TEMPERATURE |
Sprzęt | Mierzy temperaturę otoczenia w stopniach Celsjusza (°C). Zobacz uwagę poniżej. | Monitorowanie temperatury powietrza. |
TYPE_GRAVITY |
Oprogramowanie lub sprzęt | Mierzy siłę grawitacji w m/s2 działającą na urządzenie na wszystkich 3 osiach fizycznych (x, y, z). | Wykrywanie ruchu (potrząsanie, pochylanie itp.). |
TYPE_GYROSCOPE |
Sprzęt | Mierzy prędkość obrotu urządzenia w rad/s wokół każdej z 3 osi fizycznych (x, y i z). | Wykrywanie obrotów (obrót, skręt itp.). |
TYPE_LIGHT |
Sprzęt | Mierzy poziom jasności otoczenia (oświetlenie) w lx. | Sterowanie jasnością ekranu. |
TYPE_LINEAR_ACCELERATION |
Oprogramowanie lub sprzęt | Mierzy siłę przyspieszenia w m/s2, która działa 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 | Mierzy pole magnetyczne otoczenia dla wszystkich 3 osi fizycznych (x, y, z) w μT. | Tworzę kompas. |
TYPE_ORIENTATION |
Oprogramowanie | Mierzy stopnie obrotu wykonywane przez urządzenie wokół wszystkich 3 osi fizycznych (x, y, z).
Od poziomu API 3 możesz uzyskać macierz nachylenia i obrotu urządzenia, używając czujnika grawitacji i czujnika pola geomagnetycznego w połączeniu z metodą getRotationMatrix() . |
Ustalam pozycję urządzenia. |
TYPE_PRESSURE |
Sprzęt | Mierzy ciśnienie powietrza otoczenia w hPa lub mbar. | Monitorowanie zmian ciśnienia. |
TYPE_PROXIMITY |
Sprzęt | Mierzy odległość obiektu w centymetrach względem ekranu widoku urządzenia. Ten czujnik zwykle służy do określania, czy urządzenie jest przyłożone do ucha użytkownika. | Pozycja telefonu podczas połączenia. |
TYPE_RELATIVE_HUMIDITY |
Sprzęt | Mierzy względną wilgotność otoczenia wyrażoną w procentach (%). | Monitorowanie punktu rosy oraz wilgotności bezwzględnej i względnej. |
TYPE_ROTATION_VECTOR |
Oprogramowanie lub sprzęt | Mierzy orientację urządzenia, dostarczając 3 elementy wektora obrotu urządzenia. | Wykrywanie ruchu i wykrywanie obrotów. |
TYPE_TEMPERATURE |
Sprzęt | Mierzy temperaturę urządzenia w stopniach Celsjusza (°C). Ta implementacja czujnika różni się w zależności od urządzenia, dlatego czujnik ten został zastąpiony czujnikiem TYPE_AMBIENT_TEMPERATURE na poziomie interfejsu API 14 |
Monitoruję temperatury. |
Platforma czujników
Możesz uzyskać dostęp do tych czujników i pobrać nieprzetworzone dane z czujników, używając platformy czujników w Androidzie.
Platforma czujników jest częścią pakietu android.hardware
i obejmuje te klasy i interfejsy:
SensorManager
- Możesz użyć tej klasy do utworzenia instancji usługi czujnika. Ta klasa udostępnia różne metody uzyskiwania dostępu do czujników i wyświetlania ich listy, rejestrowania i wyrejestrowania odbiorników zdarzeń z czujników oraz uzyskiwania informacji o orientacji. Ta klasa zawiera też kilka stałych czujników, które służą do zgłaszania dokładności czujnika, ustawiania szybkości zbierania danych i kalibracji czujników.
Sensor
- Możesz użyć tej klasy do utworzenia wystąpienia konkretnego czujnika. Ta klasa udostępnia różne metody, które pozwalają określić możliwości czujnika.
SensorEvent
- System używa tej klasy do utworzenia obiektu zdarzenia z czujnika, który dostarcza informacje o zdarzeniu z czujnika. Obiekt zdarzenia z czujnika zawiera te informacje: nieprzetworzone dane z czujnika, typ czujnika, który wygenerował zdarzenie, dokładność danych i sygnaturę czasową zdarzenia.
SensorEventListener
- Za pomocą tego interfejsu możesz utworzyć 2 metody wywołania zwrotnego, które będą otrzymywać powiadomienia (zdarzenia z czujników) w przypadku zmiany wartości z czujnika lub zmiany dokładności czujnika.
W typowej aplikacji te interfejsy API związane z czujnikami są używane do wykonywania 2 podstawowych zadań:
- Wykrywanie czujników i ich funkcji
Identyfikowanie czujników i czujników w czasie działania jest przydatne, jeśli aplikacja ma funkcje, które wymagają określonych typów czujników lub funkcji. Może być na przykład konieczne zidentyfikowanie wszystkich czujników w urządzeniu i wyłączenie wszystkich funkcji aplikacji korzystających z tych czujników. Możesz też chcieć zidentyfikować wszystkie czujniki danego typu, aby wybrać taką implementację czujnika, która zapewnia optymalną wydajność dla Twojej aplikacji.
- Monitorowanie zdarzeń z czujnika
Monitorowanie zdarzeń z czujników to sposób pozyskiwania nieprzetworzonych danych z czujników. Zdarzenie czujnika ma miejsce za każdym razem, gdy czujnik wykryje zmianę parametrów, które mierzy. Zdarzenie z czujnika dostarcza 4 informacje: nazwę czujnika, który wywołał zdarzenie, sygnaturę czasową zdarzenia, dokładność zdarzenia oraz nieprzetworzone dane z czujnika, które wywołały zdarzenie.
Dostępność czujników
Dostępność czujników jest różna w zależności od urządzenia, ale może też być różna w zależności od wersji Androida. Dzieje się tak, ponieważ czujniki w Androidzie zostały wprowadzone w kilku wersjach platform. Na przykład w Androidzie 1.5 (poziom interfejsu API 3) wprowadzono wiele czujników, ale niektóre nie zostały wdrożone i nie można było ich używać aż do Androida 2.3 (poziom API 9). Podobnie wprowadzono kilka czujników w Androidzie 2.3 (interfejs API na poziomie 9) i Androidzie 4.0 (poziom API 14). Dwa czujniki zostały wycofane i zastąpione nowszymi, lepszymi czujnikami.
W tabeli 2 podsumowano dostępność każdego czujnika z podziałem na platformy. Wymieniamy tylko 4 platformy, ponieważ to na nich zachodzą zmiany w czujnikach. Czujniki wymienione jako wycofane, będą nadal dostępne na kolejnych platformach (pod warunkiem, że czujnik jest zainstalowany w urządzeniu), co jest zgodne z zasadami dotyczącymi zgodności z Androidem.
Czujnik | Android 4.0 (poziom API 14) |
Android 2.3 (poziom API 9) |
Android 2.2 (poziom API 8) |
Android 1.5 (poziom API 3) |
---|---|---|---|---|
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 | nie dotyczy1 | nie dotyczy1 |
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 | nie dotyczy1 | nie dotyczy1 |
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 był on dostępny dopiero w Androidzie 2.3 (poziom API 9).
2 Ten czujnik jest dostępny, ale został wycofany.
Identyfikowanie czujników i funkcji
Struktura czujników w Androidzie udostępnia kilka metod, które ułatwiają określanie w czasie działania, które czujniki są używane na urządzeniu. Interfejs API udostępnia również metody, które umożliwiają określenie możliwości każdego czujnika, na przykład jego maksymalnego zasięgu, rozdzielczości i wymagań dotyczących zasilania.
Aby zidentyfikować czujniki znajdujące się w urządzeniu, musisz najpierw uzyskać odwołanie do usługi czujników. Aby to zrobić, tworzysz instancję klasy SensorManager
, wywołując metodę getSystemService()
i przekazując argument SENSOR_SERVICE
. Na przykład:
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 wyświetlić listę wszystkich czujników w urządzeniu, wywołując metodę getSensorList()
i używając stałej TYPE_ALL
. Na przykład:
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
.
Aby ustalić, czy urządzenie ma określony typ czujnika, możesz też skorzystać z metody getDefaultSensor()
i przekazać stałą typu dla konkretnego czujnika. Jeśli urządzenie ma więcej niż 1 czujnik danego typu, należy ustawić jeden z nich jako czujnik domyślny. Jeśli dla danego typu czujnika nie ma domyślnego czujnika, wywołanie metody zwraca wartość null, co oznacza, że urządzenie nie ma czujnika tego typu. Na przykład ten kod pozwala sprawdzić, czy urządzenie zawiera 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, aby producenci wbudowali w urządzenia z Androidem żadne określone rodzaje czujników, dlatego urządzenia mogą mieć różne konfiguracje czujników.
Oprócz wyświetlania listy czujników znajdujących się w urządzeniu możesz też używać metod publicznych klasy Sensor
do określania możliwości i atrybutów poszczególnych czujników. Jest to przydatne, gdy chcesz, aby aplikacja działała różnie w zależności od dostępnych czujników lub funkcji czujników. Możesz na przykład użyć metod getResolution()
i getMaximumRange()
, aby uzyskać rozdzielczość czujnika i maksymalny zakres pomiaru. Aby poznać wymagania dotyczące zasilania czujnika, możesz też użyć metody getPower()
.
Dwie metody dostępne publicznie są szczególnie przydatne, jeśli chcesz zoptymalizować swoją aplikację pod kątem czujników różnych producentów lub różnych wersji czujników. Jeśli na przykład aplikacja wymaga monitorowania gestów użytkownika, takich jak przechylanie i potrząsanie, możesz utworzyć jeden zestaw reguł filtrowania danych i optymalizacje dla nowszych urządzeń, które mają czujnik grawitacyjny konkretnego dostawcy, oraz inny zestaw reguł filtrowania i optymalizacji dla urządzeń, które nie mają czujnika grawitacji i mają tylko akcelerometr. Poniższy przykładowy kod pokazuje, jak można wykorzystać do tego metody getVendor()
i getVersion()
. W tym przykładzie szukamy czujnika grawitacji, którego dostawcą jest Google LLC i ma numer wersji 3. Jeśli dany czujnik nie jest dostępny, 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 czujnik może używać do wykrywania danych. Każdy czujnik, który zwraca wartość inną niż 0 dla metody getMinDelay()
, jest czujnikiem strumieniowania. Czujniki strumieniowania wykrywają dane w regularnych odstępach czasu i zostały wprowadzone w Androidzie 2.3 (poziom interfejsu API 9). Jeśli czujnik zwraca 0 po wywołaniu metody getMinDelay()
, oznacza to, że czujnik nie jest czujnikiem strumieniowania, ponieważ raportuje dane tylko w przypadku zmiany parametrów, które wykrywa.
Metoda getMinDelay()
jest przydatna, ponieważ pozwala określić maksymalną szybkość, z jaką czujnik może zbierać dane. Jeśli niektóre funkcje aplikacji wymagają wysokich wskaźników pozyskiwania danych lub czujnika strumieniowania, 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ć taka sama jak szybkość, z jaką platforma czujnika dostarcza dane z czujnika do aplikacji. Platforma czujników przekazuje dane za pomocą zdarzeń z czujników, a wiele czynników wpływa na szybkość, z jaką aplikacja odbiera te zdarzenia. Więcej informacji znajdziesz w artykule Monitorowanie zdarzeń z czujników.
Zdarzenia z czujników monitorowania
Aby monitorować nieprzetworzone dane z czujników, należy wdrożyć 2 metody wywołania zwrotnego udostępniane przez interfejs SensorEventListener
: onAccuracyChanged()
i onSensorChanged()
. System Android wywołuje te metody, gdy:
- Zmieni się dokładność czujnika.
W tym przypadku system wywołuje metodę
onAccuracyChanged()
, podając odniesienie do zmienionego obiektuSensor
oraz nowej dokładności czujnika. Dokładność jest odzwierciedlana przez jedną z 4 stałych 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()
, udostępniając obiektSensorEvent
. ObiektSensorEvent
zawiera informacje o nowych danych z czujnika, w tym: dokładność danych, czujnik, który je wygenerował, sygnaturę czasową wygenerowania danych oraz nowe dane zarejestrowane przez czujnik.
Poniższy kod pokazuje, jak używać metody onSensorChanged()
do monitorowania danych z czujnika światła. W tym przykładzie nieprzetworzone dane z czujnika są wyświetlane w elemencie TextView
zdefiniowanym 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 domyślne opóźnienie danych (SENSOR_DELAY_NORMAL
) jest określone przy wywołaniu metody registerListener()
. Opóźnienie danych (czyli częstotliwość próbkowania danych) steruje odstępem, w którym zdarzenia z 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 wykorzystuje opóźnienie wynoszące 200 000 mikrosekund. Możesz określić inne opóźnienia danych, np. SENSOR_DELAY_GAME
(opóźnienie 20 000 mikrosekund), SENSOR_DELAY_UI
(opóźnienie 60 000 mikrosekund) lub SENSOR_DELAY_FASTEST
(opóźnienie 0 mikrosekund). Od Androida 3.0 (poziom interfejsu API 11) możesz też określić opóźnienie w postaci wartości bezwzględnej (w mikrosekundach).
Podane opóźnienie to tylko sugerowane opóźnienie. System Android i inne aplikacje mogą zmienić to opóźnienie. Zalecamy określenie największego opóźnienia, jaki jest możliwe, ponieważ system zwykle używa opóźnienia mniejszego od podanego (czyli należy wybrać najwolniejszą częstotliwość próbkowania, która spełnia potrzeby Twojej aplikacji). Większe opóźnienie zmniejsza obciążenie procesora i tym samym zużywa mniej energii.
Nie ma publicznej metody określania częstotliwości, z jaką platforma czujnika wysyła zdarzenia z czujników do aplikacji. Do obliczania częstotliwości próbkowania w przypadku kilku zdarzeń możesz jednak używać sygnatur czasowych powiązanych z poszczególnymi zdarzeniami z czujnika. Po ustawieniu częstotliwości próbkowania (opóźnienia) nie musisz jej zmieniać. Jeśli z jakiegoś powodu musisz zmienić opóźnienie, musisz wyrejestrować i ponownie zarejestrować odbiornik czujnika.
Warto też pamiętać, że w tym przykładzie do zarejestrowania i wyrejestrowania odbiornika zdarzeń z czujnika użyto metod wywołania zwrotnego onResume()
i onPause()
. Zgodnie ze sprawdzoną metodą wyłączaj czujniki, których nie potrzebujesz, zwłaszcza gdy aktywność jest wstrzymana. W przeciwnym razie bateria może rozładować się w zaledwie kilka godzin, ponieważ niektóre czujniki mają znaczne wymagania w zakresie zasilania i szybko zużywają baterię. System nie wyłączy czujników automatycznie po wyłączeniu ekranu.
Obsługa różnych konfiguracji czujników
Android nie określa standardowej konfiguracji czujników dla urządzeń, co oznacza, że producenci mogą dodawać do swoich urządzeń z Androidem dowolną konfigurację czujników. Dzięki temu urządzenia mogą mieć różne czujniki w wielu różnych konfiguracjach. Jeśli aplikacja korzysta z określonego typu czujnika, upewnij się, że jest on zainstalowany w urządzeniu, tak aby aplikacja mogła działać prawidłowo.
Istnieją 2 możliwości zapewnienia działania danego czujnika na urządzeniu:
- Wykrywaj czujniki w czasie działania i odpowiednio włączaj lub wyłączaj funkcje aplikacji.
- Użyj filtrów Google Play, aby kierować reklamy na urządzenia o określonych konfiguracjach czujników.
Każda opcja została omówiona w kolejnych sekcjach.
Wykrywanie czujników podczas działania
Jeśli Twoja aplikacja używa określonego typu czujnika, ale nie jest od niego uzależniona, możesz wykryć czujnik w czasie działania za pomocą platformy czujnika, a potem odpowiednio wyłączyć lub włączyć funkcje aplikacji. Na przykład aplikacja do nawigacji może korzystać z czujnika temperatury, ciśnienia, GPS-a i czujnika pola geomagnetycznego do wyświetlania temperatury, ciśnienia barometrycznego, lokalizacji i nałożenia kompasu. Jeśli urządzenie nie ma czujnika ciśnienia, możesz użyć platformy czujnika do wykrycia braku 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 urządzenie ma 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 reklam na określone 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 czujnika dla Twojej aplikacji. Element <uses-feature>
ma kilka deskryptorów sprzętowych, które umożliwiają filtrowanie aplikacji na podstawie obecności konkretnych czujników. Dostępne dane to m.in.: akcelerometr, barometr, kompas (pole geomagnetyczne), żyroskop, światło i zbliżeniowo. Poniżej znajdziesz przykładowy wpis w pliku manifestu filtrujący aplikacje bez akcelerometru:
<uses-feature android:name="android.hardware.sensor.accelerometer" android:required="true" />
Jeśli dodasz ten element i deskryptor do pliku manifestu aplikacji, użytkownicy będą ją widzieć w Google Play tylko wtedy, gdy ich urządzenia mają akcelerometr.
Deskryptor należy ustawić na android:required="true"
tylko wtedy, gdy aplikacja w całości opiera się na konkretnym czujniku. Jeśli Twoja aplikacja korzysta z czujnika do obsługi niektórych funkcji, ale nadal działa bez niego, podaj czujnik w elemencie <uses-feature>
, ale ustaw deskryptor na android:required="false"
. Dzięki temu urządzenia będą mogły zainstalować aplikację, nawet jeśli nie mają tego konkretnego czujnika. Jest to też sprawdzona metoda zarządzania projektami, która ułatwia śledzenie funkcji używanych przez aplikację.
Pamiętaj, że jeśli aplikacja korzysta z określonego czujnika, ale nadal działa bez niego, należy wykryć czujnik w czasie działania i odpowiednio wyłączyć lub włączyć funkcje aplikacji.
Układ współrzędnych czujnika
Ogólnie struktura czujników do wyrażania danych wykorzystuje standardowy 3-osiowy układ współrzędnych. W przypadku większości czujników układ współrzędnych jest zdefiniowany względem ekranu urządzenia, gdy jest ono trzymane w domyślnej orientacji (zobacz ilustrację 1). Gdy urządzenie jest trzymane w domyślnej orientacji, oś X jest pozioma i kierowana w prawo, oś Y jest pionowa i kierowana do góry, a oś Z jest skierowana w stronę zewnętrznej strony 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 grawitacji
- Żyroskop
- Czujnik przyspieszenia liniowego
- Czujnik pola geomagnetycznego
Najważniejszym aspektem tego układu współrzędnych jest to, że osie nie są zastępowane, gdy zmienia się orientacja ekranu urządzenia, czyli układ współrzędnych czujnika nie zmienia się wraz z poruszaniem urządzenia. Działa to tak samo jak w przypadku układu współrzędnych OpenGL.
Należy też pamiętać, że aplikacja nie może zakładać, że naturalna (domyślna) orientacja urządzenia jest pionowa. Wiele tabletów ma naturalną orientację poziomą. Układ współrzędnych czujnika zawsze opiera się na naturalnej orientacji urządzenia.
Jeśli aplikacja dopasowuje dane z czujników do wyświetlacza, musisz użyć metody getRotation()
, aby określić obrót ekranu, a potem przypisać współrzędne czujnika do współrzędnych ekranu za pomocą metody remapCoordinateSystem()
. Musisz to zrobić nawet wtedy, gdy w pliku manifestu jest określony
tylko orientacja pionowa.
Uwaga: niektóre czujniki i metody korzystają z układu współrzędnych, który jest względny względem światowego układu odniesienia (a nie układu odniesienia urządzenia). Te czujniki i metody zwracają dane, które przedstawiają ruch urządzenia lub jego pozycję względem Ziemi. Więcej informacji znajdziesz w opisach metod getOrientation()
, getRotationMatrix()
, czujnika orientacji i czujnika wektorów obrotu.
Ograniczenie częstotliwości czujników
Aby chronić potencjalnie poufne informacje o użytkownikach, jeśli Twoja aplikacja jest kierowana na Androida 12 (poziom interfejsu API 31) lub nowszego, system ogranicza częstotliwość odświeżania danych z określonych czujników ruchu i czujników pozycji. Dane te obejmują wartości zarejestrowane przez akcelerometr, żyroskop i czujnik pola geomagnetycznego urządzenia.
Limit częstotliwości odświeżania zależy od tego, jak uzyskujesz dostęp do danych z czujnika:
- Jeśli wywołasz metodę
registerListener()
, aby monitorować zdarzenia z czujnika, częstotliwość próbkowania czujnika będzie ograniczona do 200 Hz. Dotyczy to wszystkich przeciążonych wersji 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 Twoja 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. W przeciwnym razie, jeśli aplikacja będzie próbowała z większą częstotliwością zbierać dane z czujnika ruchu bez deklarowania tych uprawnień, pojawia się SecurityException
.
<manifest ...> <uses-permission android:name="android.permission.HIGH_SAMPLING_RATE_SENSORS"/> <application ...> ... </application> </manifest>
Sprawdzone metody dostępu do czujników i korzystania z nich
Podczas projektowania implementacji czujników pamiętaj o przestrzeganiu wytycznych omówionych w tej sekcji. Te wytyczne stanowią zalecane sprawdzone metody dla każdego, kto korzysta z ramki czujnika do uzyskiwania dostępu do czujników i pozyskiwania danych z czujników.
Zbieraj 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 korzystające z trybu raportowania ciągłego, np. akcelerometry i żyroskopy, nie otrzymują zdarzeń.
- Czujniki, które korzystają z trybów raportowania on-change lub one-shot, nie otrzymują zdarzeń.
Biorąc pod uwagę te ograniczenia, najlepiej jest wykrywać zdarzenia z czujników, gdy aplikacja działa na pierwszym planie lub w ramach usługi na pierwszym planie.
Wyrejestruj detektory czujników
Pamiętaj, aby wyrejestrować odbiornik z czujnika po zakończeniu jego używania lub po wstrzymaniu działania czujnika. Jeśli odbiornik czujnika jest zarejestrowany, a jego działanie jest wstrzymane, czujnik nadal będzie zbierać dane i wykorzystywać zasoby baterii, chyba że go wyrejestrujesz. Poniższy kod pokazuje, jak za pomocą metody onPause()
wyrejestrować 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: unregisterListener(SensorEventListener)
.
Testowanie za pomocą emulatora Androida
Emulator Androida zawiera zestaw wirtualnych elementów sterujących czujnikami, które umożliwiają testowanie czujników, takich jak akcelerometr, temperatura otoczenia, magnetometr, czujnik zbliżeniowy, światło i inne.
Emulator używa 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 na urządzeniu jest zainstalowany Android 4.0, musi być zainstalowana wersja 2.) Aplikacja SdkControllerSensor monitoruje zmiany zachodzące w czujnikach urządzenia i przesyła je do emulatora. Emulator jest następnie przekształcany na podstawie nowych wartości otrzymywanych z czujników urządzenia.
Kod źródłowy aplikacji SdkControllerSensor znajdziesz w tym miejscu:
$ your-android-sdk-directory/tools/apps/SdkController
Aby przenieść dane między urządzeniem a emulatorem, wykonaj te czynności:
- Sprawdź, czy na urządzeniu jest włączone debugowanie USB.
- Podłącz urządzenie do komputera programisty, używając kabla USB.
- Uruchom aplikację SdkControllerSensor na urządzeniu.
- W aplikacji wybierz czujniki, które chcesz emulować.
Uruchom to polecenie
adb
:- Uruchom emulator. Możesz teraz stosować przekształcenia w emulatorze, przesuwając urządzenie.
$ adb forward tcp:1968 tcp:1968
Uwaga: jeśli ruchy wykonywane na urządzeniu fizycznym nie powodują przekształcenia emulatora, spróbuj ponownie uruchomić polecenie adb
z kroku 5.
Więcej informacji znajdziesz w przewodniku po emulatorach Androida.
Nie blokuj metody onSensorChanged()
Dane z czujników mogą się zmieniać bardzo szybko, co oznacza, że system może dość często wywoływać metodę onSensorChanged(SensorEvent)
. Zalecamy, aby w metodzie onSensorChanged(SensorEvent)
robić jak najmniej danych, aby jej nie blokować. Jeśli Twoja aplikacja wymaga filtrowania lub redukcji danych z czujników, wykonaj to działanie inaczej niż w przypadku metody onSensorChanged(SensorEvent)
.
Unikaj używania wycofanych metod lub typów czujników
Niektóre metody i stałe zostały wycofane.
W szczególności wycofaliśmy typ czujnika TYPE_ORIENTATION
. Aby uzyskać dane orientacji, użyj metody getOrientation()
. Podobnie został wycofany typ czujnika TYPE_TEMPERATURE
. Na urządzeniach z Androidem 4.0 zamiast tego należy używać czujnika typu TYPE_AMBIENT_TEMPERATURE
.
Sprawdź czujniki, zanim ich użyjesz
Przed próbą pobrania z niego danych zawsze sprawdzaj, czy na urządzeniu jest czujnik. Nie zakładaj, że czujnik istnieje tylko dlatego, że jest on często używanym czujnikiem. Producenci nie muszą dostarczać żadnych konkretnych czujników.
Rozważnie dobierz opóźnienia czujników
Gdy rejestrujesz czujnik za pomocą metody registerListener()
, pamiętaj, aby wybrać szybkość dostarczania odpowiednią do Twojej aplikacji lub przypadku użycia. Czujniki mogą dostarczać dane z bardzo dużą ilością danych. Zezwolenie systemowi na wysyłanie dodatkowych danych, dzięki którym nie będziesz marnować zasobów systemowych, zużywa energię z baterii.