Omówienie czujników

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ą. Przydają się, gdy chcesz monitorować trójwymiarowy ruch lub położenie urządzenia albo śledzić zmiany w otoczeniu otoczenia w pobliżu urządzenia. Na przykład gra może śledzić odczyty z czujnika grawitacji urządzenia, aby wywnioskować złożone gesty i ruchy użytkownika, takie jak przechylanie, potrząśnięcie, obrót lub zamach. Podobnie aplikacja pogodowa może używać czujnika temperatury i wilgotności na urządzeniu do obliczania i zgłaszania punktu rosy, a aplikacja w podróży – do zgłaszania obecności kompasu za pomocą czujnika pola geomagnetycznego i akcelerometru.

Platforma Android obsługuje trzy ogólne kategorie czujników:

  • Czujniki ruchu

    Czujniki te mierzą siły przyspieszenia i siły obrotowe wzdłuż 3 osi. Ta kategoria obejmuje akcelerometry, czujniki grawitacji, żyroskopy i czujniki wektorów obrotowych.

  • Czujniki środowiskowe

    Czujniki mierzą różne parametry środowiskowe, takie jak temperatura otoczenia i ciśnienie, oświetlenie i wilgotność. Ta kategoria obejmuje barometry, fotometry i termometry.

  • Czujniki pozycji

    Te czujniki mierzą fizyczne położenie urządzenia. Ta kategoria obejmuje czujniki orientacji i magnetometry.

Można uzyskać dostęp do czujników w urządzeniu i pobierać nieprzetworzone dane z czujników za pomocą platformy czujników w systemie Android. Struktura czujnika udostępnia kilka klas i interfejsów, które pomagają wykonywać szeroką gamę zadań związanych z czujnikiem. Za pomocą konstrukcji czujnika można na przykład:

  • Ustal, które czujniki są dostępne w urządzeniu.
  • Określ możliwości konkretnego czujnika, takie jak jego maksymalny zasięg, producenta, wymagania dotyczące zasilania i rozdzielczość.
  • Uzyskiwanie nieprzetworzonych danych z czujnika i określanie minimalnej częstotliwości ich pozyskiwania.
  • Rejestrowanie i wyrejestrowywanie detektorów zdarzeń czujnika, które monitorują zmiany czujników.

W tej części opisujemy czujniki dostępne na platformie Android. Przedstawiamy w nim też konstrukcję czujnika.

Wprowadzenie do czujników

Platforma czujników w Androidzie zapewnia dostęp do wielu typów czujników. Niektóre z nich są oparte na sprzęcie, a inne programowe. Czujniki sprzętowe to fizyczne komponenty wbudowane w telefony lub tablety. Dane czerpią bezpośrednio z określonych właściwości środowiskowych, takich jak przyspieszenie, siła pola geomagnetycznego czy zmiana kątowa. Czujniki programowe nie są urządzeniami fizycznymi, chociaż imitują czujniki sprzętowe. Czujniki programowe wykorzystują dane z jednego lub kilku czujników sprzętowych. Czasem są nazywane czujnikami wirtualnymi lub czujnikami syntetycznymi. Przykładami czujników programowych są czujniki przyspieszenia liniowego i czujnik grawitacji. W tabeli 1 znajdziesz podsumowanie czujników obsługiwanych przez platformę Android.

Niewiele urządzeń z Androidem ma czujniki każdego typu. Na przykład większość telefonów i tabletów jest wyposażonych w akcelerometr i magnesometr, ale mniej urządzeń ma barometry i termometry. Oprócz tego urządzenie może mieć więcej niż 1 czujnik danego typu. Na przykład urządzenie może mieć 2 czujniki grawitacji, z których każdy ma inny zakres.

Tabela 1. Typy czujników obsługiwane przez platformę Androida.

Czujnik Typ Opis Częste zastosowania
TYPE_ACCELEROMETER Urządzenie Mierzy siłę przyspieszenia w m/s2, która jest stosowana do urządzenia na wszystkich 3 osiach fizycznych (x, y i z), w tym siłę grawitacji. Wykrywanie ruchu (potrząśnięcie, pochylenie itp.).
TYPE_AMBIENT_TEMPERATURE Urządzenie Mierzy temperaturę otoczenia w stopniach Celsjusza (°C). Zobacz uwagę poniżej. Monitoruję temperatury powietrza.
TYPE_GRAVITY Oprogramowanie lub sprzęt Mierzy siłę grawitacji w m/s2, która jest stosowana do urządzenia na wszystkich 3 osiach fizycznych (x, y, z). Wykrywanie ruchu (potrząśnięcie, pochylenie itp.).
TYPE_GYROSCOPE Urządzenie Mierzy szybkość obrotu urządzenia w rad/s wokół każdej z 3 osi fizycznych (x, y i z). Wykrywanie obrotu (obrót, obrót itp.).
TYPE_LIGHT Urządzenie 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 jest stosowana do urządzenia na wszystkich 3 osiach fizycznych (x, y i z), z wyłączeniem siły grawitacji. Monitorowanie przyspieszenia na jednej osi.
TYPE_MAGNETIC_FIELD Urządzenie Mierzy pole geomagnetyczne otoczenia dla wszystkich 3 osi fizycznych (x, y, z) w μT. Tworzę kompas.
TYPE_ORIENTATION Oprogramowanie Mierzy stopnie obrotu urządzenia wokół wszystkich 3 osi fizycznych (x, y, z). Od poziomu interfejsu API 3 możesz uzyskać macierz pochylenia i obrotu urządzenia za pomocą czujnika grawitacji i czujnika pola geomagnetycznego w połączeniu z metodą getRotationMatrix(). Określanie pozycji urządzenia.
TYPE_PRESSURE Urządzenie Mierzy ciśnienie powietrza w otoczeniu w hPa lub mbar. Monitorowanie zmian ciśnienia.
TYPE_PROXIMITY Urządzenie Mierzy odległość od obiektu w centymetrach w stosunku do widoku urządzenia. Ten czujnik zwykle służy do określania, czy użytkownik trzyma słuchawkę do ucha. Pozycja telefonu podczas połączenia.
TYPE_RELATIVE_HUMIDITY Urządzenie Mierzy względną wilgotność otoczenia w procentach (%). Monitoruję punkt rosy oraz wilgotność bezwzględną i względną.
TYPE_ROTATION_VECTOR Oprogramowanie lub sprzęt Mierzy orientację urządzenia przez udostępnianie 3 elementów wektora obrotu urządzenia. Wykrywanie ruchu i obrotu.
TYPE_TEMPERATURE Urządzenie Mierzy temperaturę urządzenia w stopniach Celsjusza (°C). Implementacja czujnika różni się w zależności od urządzenia. W interfejsie API poziomu 14 ten czujnik został zastąpiony czujnikiem TYPE_AMBIENT_TEMPERATURE. Monitoruję temperatury.

Platforma czujników

Możesz uzyskać dostęp do tych czujników i pobierać nieprzetworzone dane z czujników, korzystając z platformy czujników w Androidzie. Platforma czujnika jest częścią pakietu android.hardware i obejmuje następujące 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 wyrejestrowywania detektorów zdarzeń czujnika oraz uzyskiwania informacji o orientacji. Ta klasa udostępnia też kilka stałych czujników, które służą do raportowania dokładności czujnika, ustawiania współczynników pozyskiwania danych i kalibrowania czujników.
Sensor
Za pomocą tej klasy możesz utworzyć instancję konkretnego czujnika. Ta klasa udostępnia różne metody umożliwiające określenie możliwości czujnika.
SensorEvent
System używa tej klasy do utworzenia obiektu zdarzenia czujnika, który dostarcza informacje o takim zdarzeniu. Obiekt zdarzenia czujnika zawiera te informacje: nieprzetworzone dane z czujnika, typ czujnika, który wygenerował zdarzenie, dokładność danych oraz sygnaturę czasową 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 lub dokładności czujnika.

W typowej aplikacji te interfejsy API związane z czujnikami wykonują 2 podstawowe zadania:

  • Identyfikowanie czujników i ich funkcji

    Identyfikowanie czujników i ich funkcji w czasie działania jest przydatne, jeśli aplikacja zawiera funkcje, które wymagają określonych typów lub możliwości czujników. Możesz na przykład zidentyfikować wszystkie czujniki urządzenia i wyłączyć wszystkie funkcje aplikacji, które wykorzystują czujniki, które nie są obecne. Podobnie możesz zechcieć zidentyfikować wszystkie czujniki danego typu, aby wybrać wdrożenie czujnika, które zapewni optymalną wydajność w Twojej aplikacji.

  • Monitorowanie zdarzeń z czujnika

    Monitorowanie zdarzeń z czujników to sposób na pozyskiwanie nieprzetworzonych danych z czujnika. Zdarzenie czujnika ma miejsce za każdym razem, gdy czujnik wykryje zmianę mierzonych parametrów. Zdarzenie z czujnika dostarcza 4 rodzaje informacji: nazwę czujnika, który wywołał zdarzenie, znacznik czasu zdarzenia, dokładność zdarzenia oraz nieprzetworzone dane z czujnika, które wywołało zdarzenie.

Dostępność czujników

Dostępność czujników jest różna w zależności od urządzenia, ale może też różnić się w zależności od wersji Androida. Wynika to z tego, że czujniki Androida zostały wprowadzone w kilku wersjach platform. Na przykład w Androidzie 1.5 wprowadziliśmy wiele czujników (poziom interfejsu API 3), ale niektóre nie zostały wdrożone i nie można ich używać aż do wersji Androida 2.3 (poziom interfejsu API 9). Kilka czujników wprowadziliśmy również w Androidzie 2.3 (poziom API 9) i 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 poszczególnych platform. Wyszczególniliśmy tylko 4 platformy, ponieważ to te, które zaangażowały się w zmiany czujników. Czujniki oznaczone jako wycofane są nadal dostępne na kolejnych platformach (pod warunkiem, że znajdują się w urządzeniu), co jest zgodne z zasadami Google dotyczącymi zgodności z przekierowaniem w Androidzie.

Tabela 2. Dostępność czujników według platformy.

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 nie był on dostępny do wersji Androida 2.3 (poziom interfejsu API 9).

2 Ten czujnik jest dostępny, ale został wycofany.

Rozpoznawanie czujników i ich funkcji

Platforma czujników w Androidzie udostępnia kilka metod, które ułatwiają określanie w czasie działania, które czujniki znajdują się na urządzeniu. Interfejs API udostępnia też metody, które umożliwiają określenie możliwości każdego czujnika, np. jego maksymalnego zasięgu, rozdzielczości i wymagań dotyczących mocy.

Aby zidentyfikować czujniki urządzenia, musisz najpierw uzyskać odniesienie do usługi czujnika. Aby to zrobić, utwórz 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 urządzenia, 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, możesz użyć innej stałej zamiast TYPE_ALL takiej jak TYPE_GYROSCOPE, TYPE_LINEAR_ACCELERATION lub TYPE_GRAVITY.

Możesz też określić, czy w urządzeniu istnieje określony typ czujnika, korzystając z metody getDefaultSensor() i przekazując stałą typ dla konkretnego czujnika. Jeśli urządzenie ma więcej niż 1 czujnik danego typu, jeden z czujników musi być ustawiony jako domyślny. Jeśli dla danego typu czujnika nie ma czujnika domyślnego, wywołanie metody zwraca wartość null, co oznacza, że urządzenie nie ma takiego czujnika. Na przykład ten kod sprawdza, czy na urządzeniu znajduje się 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: producenci urządzeń z Androidem nie muszą wbudować w swoje urządzenia z Androidem żadnych konkretnych typów czujników, więc mogą one mieć różne konfiguracje.

Oprócz wyświetlania listy czujników zainstalowanych na urządzeniu możesz użyć publicznych metod klasy Sensor, aby określić możliwości i atrybuty poszczególnych czujników. Jest to przydatne, gdy chcesz, aby aplikacja działała inaczej w zależności od tego, które czujniki lub ich funkcje są dostępne na urządzeniu. Możesz np. 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 publiczne metody są szczególnie przydatne, jeśli chcesz zoptymalizować aplikację pod kątem czujników różnych producentów lub różnych wersji czujnika. Jeśli na przykład Twoja aplikacja musi monitorować gesty użytkowników, takie jak przechylanie i potrząsanie, możesz utworzyć jeden zestaw reguł filtrowania danych i optymalizacji dla nowszych urządzeń z czujnikiem grawitacji konkretnego dostawcy, a drugi zestaw reguł filtrowania danych i optymalizacji dla urządzeń, które nie mają czujnika grawitacji i mają tylko akcelerometr. Poniższy przykładowy kod pokazuje, jak możesz to zrobić za pomocą metod getVendor() i getVersion(). W tym przykładzie szukamy czujnika grawitacji, którego dostawcą jest Google LLC, a jego wersja to 3. Jeśli w urządzeniu nie ma tego konkretnego czujnika, spróbujemy go użyć.

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 metoda getMinDelay(), która zwraca minimalny przedział czasu (w mikrosekundach), którego czujnik może użyć do wykrywania danych. Każdy czujnik, który zwraca wartość inną niż 0 w przypadku metody getMinDelay(), jest czujnikiem strumieniowania. Czujniki strumieniowania wykrywają dane w regularnych odstępach czasu i zostały wprowadzone w Androidzie 2.3 (poziom API 9). Jeśli przy wywołaniu metody getMinDelay() czujnik zwróci wartość 0, oznacza to, że nie jest czujnikiem strumieniowania, ponieważ zgłasza dane tylko w przypadku zmiany wykrywanych parametrów.

Metoda getMinDelay() jest przydatna, ponieważ pozwala określić maksymalną szybkość pobierania danych przez czujnik. Jeśli niektóre funkcje aplikacji wymagają wysokich współczynników pozyskiwania danych lub czujnika strumieniowania, możesz za pomocą tej metody 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 jest zarazem szybkością, z jaką platforma czujnika dostarcza dane do aplikacji. Platforma czujnika raportuje dane za pomocą zdarzeń z czujnika, a kilka czynników wpływa na częstotliwość, z jaką aplikacja odbiera te zdarzenia. Więcej informacji znajdziesz w artykule Monitorowanie zdarzeń z czujnika.

Monitorowanie zdarzeń z czujnika

Aby monitorować nieprzetworzone dane z czujników, musisz wdrożyć 2 metody wywołań zwrotnych udostępnianych przez interfejs SensorEventListener: onAccuracyChanged() i onSensorChanged(). System Android wywołuje te metody, gdy:

Ten kod pokazuje, jak używać metody onSensorChanged() do monitorowania danych z czujnika światła. Ten przykład wyświetla nieprzetworzone dane z czujnika w pliku TextView, który jest w pliku main.xml zdefiniowany 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ślane przy wywoływaniu metody registerListener(). Opóźnienie danych (czyli częstotliwość próbkowania) steruje odstępem czasu, po 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 z opóźnieniem 200 tys. mikrosekund. Możesz określić inne opóźnienia danych, takie jak 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 jako wartość bezwzględną (w mikrosekundach).

Podane przez Ciebie opóźnienie jest tylko sugerowanym opóźnieniem. System Android i inne aplikacje mogą zmieniać to opóźnienie. Zgodnie ze sprawdzoną metodą należy podać największe możliwe opóźnienie, ponieważ system zwykle używa mniejszego opóźnienia niż podane przez Ciebie (czyli wybierz najwolniejszą częstotliwość próbkowania, która nadal spełnia potrzeby Twojej aplikacji). Większe opóźnienie powoduje mniejsze obciążenie procesora i zużywa mniej energii.

Nie ma publicznej metody określania częstotliwości, z jaką platforma czujnika wysyła zdarzenia czujnika do Twojej aplikacji. Możesz jednak użyć sygnatur czasowych powiązanych z każdym zdarzeniem czujnika, aby obliczyć częstotliwość próbkowania w przypadku kilku zdarzeń. 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ć detektor czujnika.

Warto też pamiętać, że w tym przykładzie użyto metod wywołania zwrotnego onResume() i onPause() do rejestrowania i wyrejestrowania detektora zdarzeń czujnika. Sprawdzoną metodą jest wyłączenie czujników, których nie potrzebujesz, zwłaszcza gdy aktywność jest wstrzymana. Jeśli tego nie zrobisz, bateria może wyczerpać się już w ciągu kilku godzin – niektóre czujniki mają wysokie wymagania w zakresie mocy i mogą szybko zużywać 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 urządzeń, co oznacza, że producenci urządzeń mogą stosować w swoich urządzeniach z Androidem dowolną konfigurację czujników. Urządzenia mogą więc zawierać różne czujniki w szerokiej gamie konfiguracji. Jeśli Twoja aplikacja korzysta z określonego typu czujnika, musisz go umieścić w urządzeniu, aby aplikacja mogła działać prawidłowo.

Masz 2 sposoby sprawdzania, czy dany czujnik znajduje się w urządzeniu:

  • Wykrywanie czujników w czasie działania i włączanie lub wyłączanie odpowiednich funkcji aplikacji.
  • Aby kierować reklamy na urządzenia o określonych konfiguracjach czujników, użyj filtrów Google Play.

Każda z tych opcji została omówiona w kolejnych sekcjach.

Wykrywanie czujników w czasie działania

Jeśli Twoja aplikacja używa określonego typu czujnika, ale nie zależy od niego, możesz za pomocą platformy czujników wykrywać czujnik w czasie działania, a następnie odpowiednio wyłączyć lub włączyć funkcje aplikacji. Na przykład aplikacja do nawigacji może korzystać z czujników temperatury, ciśnienia, czujnika GPS i czujnika pola geomagnetycznego do wyświetlania temperatury, ciśnienia barometrycznego, lokalizacji i położenia kompasu. Jeśli urządzenie nie ma czujnika ciśnienia, możesz użyć konstrukcji czujnika, aby wykryć brak czujnika ciśnienia w czasie działania, a następnie wyłączyć część interfejsu aplikacji, która wyświetla nacisk. Na przykład ten kod sprawdza, czy w urządzeniu znajduje się 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ć ją z urządzeń, które nie mają odpowiedniej konfiguracji czujnika dla aplikacji. Element <uses-feature> ma kilka deskryptorów sprzętowych, które umożliwiają filtrowanie aplikacji na podstawie obecności określonych czujników. Czujniki, które można wyświetlić, to: akcelerometr, barometr, kompas (pole geomagnetyczne), żyroskop, światło i bliskość. 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 deskryptor do pliku manifestu aplikacji, użytkownicy będą ją widzieć w Google Play tylko wtedy, gdy ich urządzenie ma akcelerometr.

Deskryptor należy ustawić na android:required="true" tylko wtedy, gdy aplikacja całkowicie opiera się na konkretnym czujniku. Jeśli Twoja aplikacja używa czujnika do niektórych funkcji, ale nadal działa bez czujnika, 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ą danego czujnika. Jest to również sprawdzona metoda na zarządzanie projektami, która pomaga w śledzeniu funkcji używanych w aplikacji. Pamiętaj, że jeśli Twoja aplikacja korzysta z określonego czujnika, ale działa bez niego, należy wykryć czujnik w czasie działania i odpowiednio wyłączyć lub włączyć jego funkcje.

System współrzędnych czujnika

Ogólnie rzecz biorąc, do wyrażenia wartości danych szkielet czujnika wykorzystuje standardowy 3-osiowy układ współrzędnych. W przypadku większości czujników układ współrzędnych jest określany 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 kieruje się w prawo, oś Y jest pionowa i kierowana do góry, a oś Z jest skierowana w stronę zewnętrznej płaszczyzny ekranu. W tym systemie współrzędne za ekranem mają wartość ujemną Z. Z tego układu współrzędnych korzystają następujące czujniki:

Rysunek 1. System współrzędnych (w odniesieniu do urządzenia) używany przez interfejs Sensor API.

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 nigdy się nie zmienia, gdy urządzenie się porusza. Jest to takie samo jak działanie układu współrzędnych OpenGL.

Aplikacja nie może też zakładać, że naturalna (domyślna) orientacja urządzenia jest pionowa. Na wielu tabletach naturalna orientacja to pozioma. A układ współrzędnych czujnika zawsze opiera się na naturalnej orientacji urządzenia.

Na koniec, jeśli aplikacja dopasowuje dane z czujnika do wyświetlacza na ekranie, musisz użyć metody getRotation(), aby określić obrót ekranu, a potem użyć metody remapCoordinateSystem(), aby zmapować współrzędne czujnika na współrzędne ekranu. Jest to konieczne, nawet jeśli w pliku manifestu wskazano wyświetlacz w orientacji pionowej.

Uwaga: niektóre czujniki i metody korzystają z układu współrzędnych zależnego od światowego układu odniesienia (a nie do układu odniesienia urządzenia). Te czujniki i metody zwracają dane reprezentujące ruch urządzenia lub jego pozycję względem Ziemi. Więcej informacji zawierają metody getOrientation(), getRotationMatrix(), Czujnik orientacji i Czujnik wektora obrotu.

Ograniczenie szybkości czujnika

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 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 sposobu uzyskania dostępu do danych z czujnika:

Jeśli Twoja aplikacja musi zbierać dane z czujnika ruchu z większą szybkoś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 szybciej zbierać dane z czujnika ruchu bez deklarowania tego uprawnienia, wystąpi SecurityException.

AndroidManifest.xml

<manifest ...>
    <uses-permission android:name="android.permission.HIGH_SAMPLING_RATE_SENSORS"/>
    <application ...>
        ...
    </application>
</manifest>

Sprawdzone metody dostępu do czujników i ich używania

Podczas projektowania implementacji czujnika pamiętaj o przestrzeganiu wytycznych opisanych w tej sekcji. Te wytyczne są zalecanymi sprawdzonymi metodami dla każdego, kto korzysta z platformy czujnika do uzyskiwania dostępu do czujników i pozyskiwania z nich danych.

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, które używają trybu raportowania ciągłego, takie jak akcelerometry i żyroskopy, nie odbierają zdarzeń.
  • Czujniki, które używają trybów raportowania w zmianie lub jednego ujęcia, nie odbierają zdarzeń.

Ze względu na te ograniczenia najlepiej wykrywać zdarzenia z czujnika, gdy aplikacja działa na pierwszym planie lub w ramach usługi na pierwszym planie.

Wyrejestruj detektory czujników

Pamiętaj, aby wyrejestrować detektor czujnika, gdy przestaniesz go używać lub gdy jego aktywność zostanie wstrzymana. Jeśli odbiornik czujnika jest zarejestrowany, a jego aktywność zostanie wstrzymana, czujnik będzie nadal zbierać dane i korzystać z zasobów baterii, chyba że wyrejestrujesz czujnik. Poniższy kod pokazuje, jak za pomocą metody onPause() wyrejestrować detektor:

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 elementów sterujących czujnikami wirtualnymi, 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 aplikacja jest dostępna tylko na urządzeniach z Androidem 4.0 (poziom interfejsu API 14) lub nowszym. (Jeśli na urządzeniu jest Android 4.0, musi być na nim zainstalowana wersja 2). Aplikacja SdkControllerSensor monitoruje zmiany w czujnikach urządzenia i przesyła je do emulatora. Emulator jest następnie przekształcany na podstawie nowych wartości, które otrzymuje z czujników urządzenia.

Kod źródłowy aplikacji SdkControllerSensor możesz wyświetlić w tej lokalizacji:

$ your-android-sdk-directory/tools/apps/SdkController

Aby przenieść dane między urządzeniem a emulatorem, wykonaj te czynności:

  1. Sprawdź, czy na urządzeniu jest włączone debugowanie USB.
  2. Podłącz urządzenie do maszyny wirtualnej za pomocą kabla USB.
  3. Na urządzeniu uruchom aplikację SdkControllerSensor.
  4. W aplikacji wybierz czujniki, które chcesz emulować.
  5. Uruchom to polecenie adb:

  6. $ adb forward tcp:1968 tcp:1968
    
  7. Uruchom emulator. Przekształcenia można teraz zastosować w emulatorze, przenosząc urządzenie.

Uwaga: jeśli ruchy urządzenia fizycznego nie wpływają na emulator, spróbuj ponownie uruchomić polecenie adb z kroku 5.

Więcej informacji znajdziesz w przewodniku po emulatorze Androida.

Nie blokuj metody onSensorChanged()

Dane z czujników mogą zmieniać się z dużą szybkością, co oznacza, że system może dość często wywoływać metodę onSensorChanged(SensorEvent). Zalecamy, aby w ramach metody onSensorChanged(SensorEvent) robić jak najmniejszą ilość treści, aby nie blokować tej metody. Jeśli Twoja aplikacja wymaga filtrowania lub redukcji danych z czujników, musisz wykonać te czynności poza metodą onSensorChanged(SensorEvent).

Unikaj używania wycofanych metod lub typów czujników

Kilka metod i stałych zostało wycofanych. W szczególności wycofaliśmy typ czujnika TYPE_ORIENTATION. Aby uzyskać dane orientacji, użyj metody getOrientation(). Ten typ czujnika (TYPE_TEMPERATURE) również został wycofany. W przypadku urządzeń z Androidem 4.0 użyj czujnika typu TYPE_AMBIENT_TEMPERATURE.

Sprawdź czujniki, zanim ich użyjesz

Przed podjęciem próby pozyskania danych z urządzenia zawsze sprawdź, czy znajduje się w nim czujnik. Nie zakładaj, że czujnik istnieje tylko dlatego, że jest często używany. Producenci nie muszą udostępniać w swoich urządzeniach żadnych konkretnych czujników.

Starannie wybieraj opóźnienia z czujnika

Gdy rejestrujesz czujnik za pomocą metody registerListener(), wybierz częstotliwość przesyłania, która pasuje do Twojej aplikacji lub zastosowania. Czujniki dostarczają dane w bardzo dużych ilościach. Jeśli umożliwisz systemowi wysyłanie dodatkowych danych, które nie są potrzebne, niepotrzebnie zużywają zasoby systemowe i zużywają baterię.