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ą. 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.

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.

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

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.

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 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:

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:

Rysunek 1. System współrzędnych (w stosunku 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 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 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.

AndroidManifest.xml

<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:

  1. Sprawdź, czy na urządzeniu jest włączone debugowanie USB.
  2. Podłącz urządzenie do komputera programisty, używając kabla USB.
  3. Uruchom aplikację SdkControllerSensor na urządzeniu.
  4. W aplikacji wybierz czujniki, które chcesz emulować.
  5. Uruchom to polecenie adb:

  6. $ adb forward tcp:1968 tcp:1968
    
  7. Uruchom emulator. Możesz teraz stosować przekształcenia w emulatorze, przesuwając urządzenie.

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.