Omówienie czujników

Większość urządzeń z Androidem ma wbudowane czujniki, które mierzą ruch, orientację i różne warunki środowiskowe. Te czujniki mogą dostarczać nieprzetworzone dane z wysoką precyzją i dokładnością. Są przydatne, jeśli chcesz monitorować ruch lub pozycjonowanie urządzenia w 3 wymiarach albo zmiany w otoczeniu urządzenia. Gra może na przykład śledzić odczyty z czujnika grawitacji urządzenia, aby wykrywać złożone gesty użytkownika i ruchy, takie jak przechylanie, potrząsanie, obracanie czy kołysanie. Podobnie aplikacja pogodowa może używać czujnika temperatury i wilgotności urządzenia do obliczania i podawania punktu rosy, a aplikacja turystyczna może używać czujnika pola geomagnetycznego i akcelerometru do podawania namiaru kompasu.

Platforma Androida obsługuje 3 główne kategorie czujników:

  • Czujniki ruchu

    Te czujniki mierzą siły przyspieszenia i siły obrotowe w 3 osiach. Ta kategoria obejmuje akcelerometry, czujniki grawitacji, żyroskopy i czujniki wektora obrotu.

  • Czujniki środowiskowe

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

  • Czujniki położenia

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

Możesz uzyskać dostęp do czujników dostępnych na urządzeniu i pobrać nieprzetworzone dane z czujników za pomocą platformy czujników Androida. Platforma czujników udostępnia kilka klas i interfejsów, które pomagają wykonywać różne zadania związane z czujnikami. Możesz na przykład użyć platformy czujników, aby:

  • określanie, które czujniki są dostępne na urządzeniu;
  • Określanie możliwości poszczególnych czujników, takich jak maksymalny zasięg, producent, wymagania dotyczące zasilania i rozdzielczość.
  • uzyskiwać nieprzetworzone dane z czujników i określać minimalną częstotliwość ich pozyskiwania;
  • rejestrować i wyrejestrowywać detektory zdarzeń czujników, które monitorują zmiany czujników;

W tym temacie znajdziesz omówienie czujników dostępnych na platformie Androida. Zawiera też wprowadzenie do platformy czujników.

Wprowadzenie do czujników

Platforma czujników Androida umożliwia dostęp do wielu typów czujników. Niektóre z tych czujników są sprzętowe, a inne programowe. Czujniki sprzętowe to fizyczne komponenty wbudowane w telefon lub tablet. Uzyskują dane, bezpośrednio mierząc określone właściwości środowiskowe, takie jak przyspieszenie, natężenie pola geomagnetycznego czy zmiana kąta. Czujniki oparte na oprogramowaniu nie są urządzeniami fizycznymi, chociaż naśladują czujniki sprzętowe. Czujniki programowe pobierają dane z co najmniej 1 czujnika sprzętowego i czasami są nazywane czujnikami wirtualnymi lub syntetycznymi. Czujnik przyspieszenia liniowego i czujnik grawitacji to przykłady czujników opartych na oprogramowaniu. Tabela 1 zawiera podsumowanie czujników obsługiwanych przez platformę Androida.

Niewiele urządzeń z Androidem ma wszystkie typy czujników. Na przykład większość telefonów i tabletów ma akcelerometr i magnetometr, ale mniej urządzeń ma barometr lub termometr. Urządzenie może też mieć więcej niż 1 czujnik danego typu. Na przykład urządzenie może mieć 2 czujniki grawitacji, każdy o innym zakresie.

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, która jest stosowana do urządzenia na wszystkich 3 osiach fizycznych (x, y i z), w tym siłę grawitacji. wykrywanie ruchu (potrząsanie, przechylanie itp.);
TYPE_AMBIENT_TEMPERATURE Sprzęt Mierzy temperaturę otoczenia w pomieszczeniu w stopniach Celsjusza (°C). Zobacz uwagę poniżej. Monitorowanie 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ąsanie, przechylanie 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 obrotu (obracanie, skręcanie itp.);
TYPE_LIGHT Sprzęt Mierzy poziom jasności otoczenia (natężenie oświetlenia) w luksach. sterowanie jasnością ekranu,
TYPE_LINEAR_ACCELERATION Oprogramowanie lub sprzęt Mierzy siłę przyspieszenia w m/s2, która jest przykładana 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 natężenie pola geomagnetycznego otoczenia we wszystkich 3 osiach fizycznych (x, y, z) w μT. Tworzenie kompasu.
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 nachylenia i macierz obrotu urządzenia, używając czujnika grawitacji i czujnika pola geomagnetycznego w połączeniu z metodą getRotationMatrix(). Określam pozycję urządzenia.
TYPE_PRESSURE Sprzęt Mierzy ciśnienie powietrza w otoczeniu w hPa lub mbar. Monitorowanie zmian ciśnienia powietrza.
TYPE_PROXIMITY Sprzęt Mierzy bliskość obiektu w cm względem ekranu urządzenia. Ten czujnik jest zwykle używany do określania, czy telefon jest trzymany przy uchu. Pozycja telefonu podczas rozmowy.
TYPE_RELATIVE_HUMIDITY Sprzęt Mierzy wilgotność względną otoczenia w procentach (%). Monitorowanie punktu rosy, wilgotności bezwzględnej i względnej.
TYPE_ROTATION_VECTOR Oprogramowanie lub sprzęt Mierzy orientację urządzenia, podając 3 elementy wektora obrotu urządzenia. wykrywanie ruchu i obrotu,
TYPE_TEMPERATURE Sprzęt Mierzy temperaturę urządzenia w stopniach Celsjusza (°C). Implementacja tego czujnika różni się w zależności od urządzenia. Został on zastąpiony czujnikiem TYPE_AMBIENT_TEMPERATURE na poziomie interfejsu API 14. Monitorowanie temperatury.

Platforma czujników

Dostęp do tych czujników i surowych danych z czujnika można uzyskać za pomocą platformy czujników Androida. Platforma czujników jest częścią pakietu android.hardware i zawiera te klasy i interfejsy:

SensorManager
Możesz użyć tej klasy, aby utworzyć instancję usługi czujnika. Ta klasa udostępnia różne metody uzyskiwania dostępu do czujników i ich wyświetlania, rejestrowania i wyrejestrowywania detektorów zdarzeń czujników oraz uzyskiwania informacji o orientacji. Ta klasa zawiera też kilka stałych czujnika, które służą do raportowania dokładności czujnika, ustawiania częstotliwości pozyskiwania danych i kalibrowania czujników.
Sensor
Możesz użyć tej klasy, aby utworzyć instancję 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 czujnika, który dostarcza informacji o zdarzeniu czujnika. Obiekt zdarzenia 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 czujnika) po zmianie wartości lub dokładności czujnika.

W typowej aplikacji używasz tych interfejsów API związanych z czujnikami do wykonywania 2 podstawowych zadań:

  • Identyfikowanie czujników i ich możliwości

    Identyfikowanie czujników i ich możliwości w czasie działania jest przydatne, jeśli aplikacja ma funkcje, które opierają się na określonych typach lub możliwościach czujników. Możesz na przykład zidentyfikować wszystkie czujniki obecne na urządzeniu i wyłączyć funkcje aplikacji, które korzystają z czujników, których nie ma. Możesz też zidentyfikować wszystkie czujniki danego typu, aby wybrać implementację czujnika, która zapewnia optymalną wydajność aplikacji.

  • Monitorowanie zdarzeń czujnika

    Monitorowanie zdarzeń czujników to sposób na uzyskiwanie nieprzetworzonych danych z czujników. Zdarzenie czujnika występuje za każdym razem, gdy czujnik wykryje zmianę mierzonych parametrów. Zdarzenie czujnika dostarcza 4 informacje: nazwę czujnika, który wywołał zdarzenie, sygnaturę czasową zdarzenia, dokładność zdarzenia i nieprzetworzone dane z czujnika, które wywołały zdarzenie.

Dostępność czujnika

Dostępność czujników zależy od urządzenia, ale może się też różnić w zależności od wersji Androida. Dzieje się tak dlatego, że czujniki Androida były wprowadzane w ciągu kilku wersji platformy. Na przykład wiele czujników zostało wprowadzonych w Androidzie 1.5 (poziom interfejsu API 3), ale niektóre z nich nie zostały zaimplementowane i były dostępne dopiero w Androidzie 2.3 (poziom interfejsu API 9). Podobnie kilka czujników zostało wprowadzonych w Androidzie 2.3 (poziom interfejsu API 9) i Androidzie 4.0 (poziom interfejsu API 14). Dwa czujniki zostały wycofane i zastąpione nowszymi, lepszymi.

Tabela 2 zawiera podsumowanie dostępności poszczególnych czujników na różnych platformach. Na liście znajdują się tylko 4 platformy, ponieważ tylko na nich wprowadzono zmiany dotyczące czujników. Czujniki oznaczone jako wycofane są nadal dostępne na kolejnych platformach (pod warunkiem, że czujnik jest obecny na urządzeniu), co jest zgodne z zasadami zgodności Androida z przyszłymi wersjami.

Tabela 2. Dostępność czujników na różnych platformach.

Czujnik Android 4.0
(poziom API 14)
Android 2.3
(poziom interfejsu API 9)
Android 2.2
(poziom API 8)
Android 1.5
(poziom interfejsu 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 –1 –1
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 –1 –1
TYPE_PROXIMITY Tak Tak Tak Tak
TYPE_RELATIVE_HUMIDITY Tak nie dotyczy nie dotyczy nie dotyczy
TYPE_ROTATION_VECTOR Tak Tak nie dotyczy nie dotyczy
TYPE_TEMPERATURE Tak2 Tak Tak Tak

1 Ten typ czujnika został dodany w Androidzie 1.5 (poziom interfejsu API 3), ale nie był dostępny do użytku aż do Androida 2.3 (poziom interfejsu API 9).

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

Identyfikowanie czujników i ich możliwości

Platforma czujników Androida udostępnia kilka metod, które ułatwiają określenie w czasie działania, które czujniki znajdują się na urządzeniu. Interfejs API udostępnia też metody, które pozwalają określić możliwości każdego czujnika, takie jak maksymalny zasięg, rozdzielczość i wymagania dotyczące zasilania.

Aby zidentyfikować czujniki na urządzeniu, musisz najpierw uzyskać odwołanie do usługi czujnika. W tym celu utwórz instancję klasy SensorManager, wywołując metodę getSystemService() i przekazując argument SENSOR_SERVICE. 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 uzyskać listę wszystkich czujników na urządzeniu, wywołując metodę getSensorList() i używając stałej TYPE_ALL. 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.

Możesz też określić, czy na urządzeniu znajduje się określony typ czujnika, używając metody getDefaultSensor() i przekazując stałą typu dla konkretnego czujnika. Jeśli urządzenie ma więcej niż 1 czujnik danego typu, jeden z nich musi być oznaczony 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 czujnika tego typu. Na przykład ten kod sprawdza, czy na urządzeniu jest magnetometr:

Kotlin

private lateinit var sensorManager: SensorManager
...
sensorManager = getSystemService(Context.SENSOR_SERVICE) as SensorManager
if (sensorManager.getDefaultSensor(Sensor.TYPE_MAGNETIC_FIELD) != null) {
    // Success! There's a magnetometer.
} else {
    // Failure! No magnetometer.
}

Java

private SensorManager sensorManager;
...
sensorManager = (SensorManager) getSystemService(Context.SENSOR_SERVICE);
if (sensorManager.getDefaultSensor(Sensor.TYPE_MAGNETIC_FIELD) != null){
    // Success! There's a magnetometer.
} else {
    // Failure! No magnetometer.
}

Uwaga: Android nie wymaga od producentów urządzeń, aby wbudowywali w urządzenia z Androidem określone typy czujników, dlatego urządzenia mogą mieć różne konfiguracje czujników.

Oprócz wyświetlania listy czujników na urządzeniu możesz używać metod publicznych klasy Sensor, aby określać możliwości i atrybuty poszczególnych czujników. Jest to przydatne, jeśli chcesz, aby aplikacja zachowywała się inaczej w zależności od tego, jakie czujniki lub funkcje czujników są dostępne na urządzeniu. Możesz na przykład użyć metod getResolution() i getMaximumRange(), aby uzyskać rozdzielczość czujnika i maksymalny zakres pomiaru. Możesz też użyć metody getPower(), aby uzyskać informacje o wymaganiach dotyczących zasilania czujnika.

Dwie z metod publicznych 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 aplikacja musi monitorować gesty użytkownika, takie jak przechylanie i potrząsanie, możesz utworzyć jeden zestaw reguł filtrowania danych i optymalizacji dla nowszych urządzeń, które mają czujnik grawitacyjny określonego dostawcy, oraz inny zestaw reguł filtrowania danych i optymalizacji dla urządzeń, które nie mają czujnika grawitacyjnego, a jedynie akcelerometr. Poniższy przykładowy kod pokazuje, jak możesz to zrobić za pomocą metod getVendor() i getVersion(). W tym przykładzie szukamy czujnika grawitacyjnego, którego dostawcą jest Google LLC, a numer wersji to 3. Jeśli urządzenie nie ma takiego czujnika, 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 interwał czasowy (w mikrosekundach), jaki czujnik może wykorzystać do wykrywania danych. Każdy czujnik, który zwraca wartość niezerową dla metody getMinDelay(), jest czujnikiem strumieniowym. Czujniki strumieniowe wykrywają dane w regularnych odstępach czasu i zostały wprowadzone w Androidzie 2.3 (poziom interfejsu API 9). Jeśli czujnik zwraca wartość 0, gdy wywołujesz metodę getMinDelay(), oznacza to, że nie jest to czujnik strumieniowy, ponieważ raportuje dane tylko wtedy, gdy nastąpi zmiana parametrów, które wykrywa.

Metoda getMinDelay() jest przydatna, ponieważ pozwala określić maksymalną częstotliwość, z jaką czujnik może pozyskiwać dane. Jeśli niektóre funkcje aplikacji wymagają wysokiej częstotliwości pozyskiwania danych lub czujnika strumieniowego, możesz użyć tej metody, aby określić, czy czujnik spełnia te wymagania, a następnie odpowiednio włączyć lub wyłączyć odpowiednie funkcje w aplikacji.

Uwaga: maksymalna szybkość pozyskiwania danych przez czujnik nie musi być szybkością, z jaką platforma czujników dostarcza dane z czujnika do aplikacji. Platforma czujników raportuje dane za pomocą zdarzeń czujników, a na częstotliwość, z jaką aplikacja otrzymuje zdarzenia czujników, wpływa kilka czynników. Więcej informacji znajdziesz w artykule Monitorowanie zdarzeń czujnika.

Monitorowanie zdarzeń czujnika

Aby monitorować nieprzetworzone dane z czujnika, musisz zaimplementować 2 metody wywołania zwrotnego, które są 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, który jest zdefiniowany 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ślane podczas wywoływania metody registerListener(). Opóźnienie danych (lub częstotliwość próbkowania) określa odstępy czasu, w jakich zdarzenia czujnika są wysyłane do aplikacji za pomocą metody wywołania zwrotnego onSensorChanged(). Domyślne opóźnienie danych jest odpowiednie do monitorowania typowych zmian orientacji ekranu i wynosi 200 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 jako wartość bezwzględną (w mikrosekundach).

Określone przez Ciebie opóźnienie jest tylko sugerowane. System Android i inne aplikacje mogą zmienić to opóźnienie. Zalecaną sprawdzoną metodą jest określenie największego możliwego opóźnienia, ponieważ system zwykle używa mniejszego opóźnienia niż określone (czyli należy wybrać najniższą częstotliwość próbkowania, która nadal spełnia wymagania aplikacji). Użycie większego opóźnienia powoduje mniejsze obciążenie procesora, a tym samym mniejsze zużycie energii.

Nie ma publicznej metody określania częstotliwości, z jaką platforma czujników wysyła zdarzenia czujników do 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 trzeba jej zmieniać. Jeśli z jakiegoś powodu musisz zmienić opóźnienie, musisz wyrejestrować i ponownie zarejestrować detektor czujnika.

Warto też zauważyć, że w tym przykładzie użyto metod wywołania zwrotnego onResume() i onPause() do rejestrowania i wyrejestrowywania detektora zdarzeń czujnika. Zalecamy wyłączanie niepotrzebnych czujników, zwłaszcza gdy aktywność jest wstrzymana. Jeśli tego nie zrobisz, bateria może się rozładować w ciągu kilku godzin, ponieważ niektóre czujniki mają duże wymagania dotyczące zasilania i mogą szybko zużywać 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 na urządzeniach, co oznacza, że producenci urządzeń mogą stosować dowolną konfigurację czujników na urządzeniach z Androidem. W rezultacie urządzenia mogą zawierać różne czujniki w szerokim zakresie konfiguracji. Jeśli aplikacja korzysta z określonego typu czujnika, musisz upewnić się, że jest on obecny na urządzeniu, aby aplikacja mogła działać prawidłowo.

Aby upewnić się, że dany czujnik jest obecny na urządzeniu, możesz skorzystać z 2 opcji:

  • Wykrywaj czujniki w czasie działania i odpowiednio włączaj lub wyłączaj funkcje aplikacji.
  • Używaj filtrów Google Play, aby kierować aplikacje na urządzenia docelowe z określonymi konfiguracjami czujników.

Każda opcja jest omówiona w sekcjach poniżej.

Wykrywanie czujników w czasie działania

Jeśli aplikacja używa określonego typu czujnika, ale nie jest od niego zależna, możesz użyć platformy czujników, aby wykryć czujnik w czasie działania, a następnie odpowiednio wyłączyć lub włączyć funkcje aplikacji. Na przykład aplikacja do nawigacji może używać czujnika temperatury, czujnika ciśnienia, czujnika GPS i czujnika pola geomagnetycznego do wyświetlania temperatury, ciśnienia barometrycznego, lokalizacji i kierunku kompasu. Jeśli urządzenie nie ma czujnika ciśnienia, możesz użyć platformy czujników, aby wykryć brak czujnika ciśnienia w czasie działania, a następnie wyłączyć część interfejsu aplikacji, która wyświetla ciśnienie. Na przykład ten kod sprawdza, czy na urządzeniu jest czujnik ciśnienia:

Kotlin

private lateinit var sensorManager: SensorManager
...
sensorManager = getSystemService(Context.SENSOR_SERVICE) as SensorManager

if (sensorManager.getDefaultSensor(Sensor.TYPE_PRESSURE) != null) {
    // Success! There's a pressure sensor.
} else {
    // Failure! No pressure sensor.
}

Java

private SensorManager sensorManager;
...
sensorManager = (SensorManager) getSystemService(Context.SENSOR_SERVICE);
if (sensorManager.getDefaultSensor(Sensor.TYPE_PRESSURE) != null){
    // Success! There's a pressure sensor.
} else {
    // Failure! No pressure sensor.
}

Używanie filtrów Google Play do kierowania na konkretne konfiguracje czujników

Jeśli publikujesz aplikację w Google Play, możesz użyć elementu <uses-feature> w pliku manifestu, aby odfiltrować urządzenia, które nie mają odpowiedniej konfiguracji czujników. Element <uses-feature> ma kilka deskryptorów sprzętowych, które umożliwiają filtrowanie aplikacji na podstawie obecności określonych czujników. Możesz wymienić te czujniki: akcelerometr, barometr, kompas (pole geomagnetyczne), żyroskop, czujnik światła i czujnik zbliżeniowy. Oto przykładowy wpis w pliku manifestu, który odfiltrowuje 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 zobaczą Twoją aplikację w Google Play tylko wtedy, gdy ich urządzenie będzie wyposażone w akcelerometr.

Deskryptor należy ustawić na android:required="true" tylko wtedy, gdy aplikacja całkowicie opiera się na konkretnym czujniku. Jeśli aplikacja używa czujnika do niektórych funkcji, ale działa bez niego, należy podać czujnik w elemencie <uses-feature>, ale ustawić deskryptor na android:required="false". Dzięki temu użytkownicy będą mogli zainstalować Twoją aplikację nawet wtedy, gdy ich urządzenia nie będą miały danego czujnika. Jest to też sprawdzona metoda zarządzania projektami, która pomaga śledzić funkcje używane przez aplikację. Pamiętaj, że jeśli aplikacja używa określonego czujnika, ale może działać bez niego, musisz wykryć czujnik w czasie działania i odpowiednio wyłączyć lub włączyć funkcje aplikacji.

Układ współrzędnych czujnika

Ogólnie rzecz biorąc, platforma czujników używa standardowego 3-osiowego układu współrzędnych do wyrażania wartości danych. W przypadku większości czujników układ współrzędnych jest definiowany względem ekranu urządzenia, gdy jest ono trzymane w orientacji domyślnej (patrz rysunek 1). Gdy urządzenie jest trzymane w orientacji domyślnej, oś X jest pozioma i skierowana w prawo, oś Y jest pionowa i skierowana w górę, a oś Z jest skierowana na zewnątrz ekranu. W tym systemie współrzędne za ekranem mają ujemne wartości Z. Ten układ współrzędnych jest używany przez te czujniki:

Rysunek 1. Układ współrzędnych (względem urządzenia) używany przez interfejs Sensor API.

Najważniejszą rzeczą, którą należy zrozumieć w tym układzie współrzędnych, jest to, że osie nie są zamieniane, gdy zmienia się orientacja ekranu urządzenia – czyli układ współrzędnych czujnika nigdy się nie zmienia, gdy urządzenie się porusza. Działa to tak samo jak układ współrzędnych OpenGL.

Kolejną kwestią, którą należy zrozumieć, jest to, że aplikacja nie może zakładać, że naturalna (domyślna) orientacja urządzenia jest pionowa. W przypadku wielu tabletów naturalna jest orientacja pozioma. Układ współrzędnych czujnika jest zawsze oparty na naturalnej orientacji urządzenia.

Jeśli aplikacja dopasowuje dane z czujnika do wyświetlacza, musisz użyć metody getRotation(), aby określić obrót ekranu, a następnie metody remapCoordinateSystem(), aby zmapować współrzędne czujnika na współrzędne ekranu. Musisz to zrobić, nawet jeśli w pliku manifestu określono, że aplikacja ma być wyświetlana tylko w orientacji pionowej.

Uwaga: niektóre czujniki i metody używają układu współrzędnych, który jest powiązany z globalnym układem odniesienia (w przeciwieństwie do układu odniesienia urządzenia). Te czujniki i metody zwracają dane, które reprezentują ruch urządzenia lub jego pozycję względem Ziemi. Więcej informacji znajdziesz w opisach metod getOrientation() i getRotationMatrix() oraz w artykułach o czujniku orientacji i czujniku wektora obrotu.

Ograniczanie liczby żądań czujnika

Aby chronić potencjalnie poufne informacje o użytkownikach, jeśli aplikacja jest kierowana na Androida 12 (poziom interfejsu API 31) lub nowszego, system ogranicza częstotliwość odświeżania danych z niektórych czujników ruchu i pozycji. Te dane obejmują wartości zarejestrowane przez akcelerometr, żyroskop i czujnik pola geomagnetycznego urządzenia.

Limit częstotliwości odświeżania zależy od sposobu uzyskiwania dostępu do danych z czujnika:

Jeśli aplikacja musi zbierać dane z czujnika ruchu z większą częstotliwością, musisz zadeklarować uprawnienie HIGH_SAMPLING_RATE_SENSORS, jak pokazano w tym fragmencie kodu. Jeśli aplikacja spróbuje zbierać dane z czujnika ruchu z większą częstotliwością bez zadeklarowania tego uprawnienia, wystąpi błąd SecurityException.

AndroidManifest.xml

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

Sprawdzone metody uzyskiwania dostępu do czujników i korzystania z nich

Podczas projektowania implementacji czujnika postępuj zgodnie ze wskazówkami podanymi w tej sekcji. Te wytyczne to zalecane sprawdzone metody dla każdego, kto używa platformy czujników do uzyskiwania dostępu do czujników i pozyskiwania danych z czujników.

Zbieranie danych z czujników tylko na pierwszym planie

Na urządzeniach z Androidem 9 (poziom API 28) lub nowszym aplikacje działające w tle mają te ograniczenia:

  • Czujniki, które używają trybu raportowania ciągłego, takie jak akcelerometry i żyroskopy, nie otrzymują zdarzeń.
  • Czujniki, które używają trybów raportowania on-change lub one-shot, nie otrzymują zdarzeń.

Ze względu na te ograniczenia najlepiej jest wykrywać zdarzenia czujnika, gdy aplikacja działa na pierwszym planie lub jako część usługi na pierwszym planie.

Wyrejestrowywanie detektorów czujników

Pamiętaj, aby wyrejestrować detektor czujnika, gdy skończysz korzystać z czujnika lub gdy jego aktywność zostanie wstrzymana. Jeśli detektor czujnika jest zarejestrowany, a jego aktywność jest wstrzymana, czujnik będzie nadal zbierać dane i zużywać baterię, dopóki nie wyrejestrujesz czujnika. Poniższy kod pokazuje, jak użyć metody onPause(), aby 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 znajdziesz tutaj: unregisterListener(SensorEventListener).

Testowanie za pomocą narzędzia Android Emulator

Android Emulator zawiera zestaw elementów sterujących czujnikami wirtualnymi, które umożliwiają testowanie czujników takich jak akcelerometr, czujnik temperatury otoczenia, magnetometr, czujnik zbliżeniowy, czujnik światła i inne.

Emulator korzysta z połączenia z urządzeniem z Androidem, na którym działa aplikacja SdkControllerSensor. Pamiętaj, że ta aplikacja jest dostępna tylko na urządzeniach z Androidem 4.0 (poziom interfejsu API 14) lub nowszym. (Jeśli urządzenie ma Androida 4.0, musi być zainstalowana wersja 2). Aplikacja SdkControllerSensor monitoruje zmiany w czujnikach na urządzeniu i przesyła je do emulatora. Emulator jest następnie przekształcany na podstawie nowych wartości, które otrzymuje z czujników na urządzeniu.

Kod źródłowy aplikacji SdkControllerSensor znajdziesz tutaj:

$ 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 używanego do programowania za pomocą 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. Teraz możesz stosować przekształcenia w emulatorze, poruszając urządzeniem.

Uwaga: jeśli ruchy, które wykonujesz na urządzeniu fizycznym, nie powodują zmian w emulatorze, spróbuj ponownie uruchomić polecenie adb z kroku 5.

Więcej informacji znajdziesz w przewodniku po emulatorze Androida.

Nie blokuj metody onSensorChanged()

Dane z czujnika mogą się szybko zmieniać, co oznacza, że system może dość często wywoływać metodę onSensorChanged(SensorEvent). Zgodnie ze sprawdzonymi metodami w metodzie onSensorChanged(SensorEvent) należy wykonywać jak najmniej działań, aby jej nie blokować. Jeśli aplikacja wymaga filtrowania lub redukcji danych z czujnika, należy 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 wycofany został typ czujnika TYPE_ORIENTATION. Aby uzyskać dane o orientacji, użyj metody getOrientation(). Podobnie wycofany został typ czujnika TYPE_TEMPERATURE. Na urządzeniach z Androidem 4.0 należy używać czujnika typu TYPE_AMBIENT_TEMPERATURE.

Sprawdzanie czujników przed ich użyciem

Zanim spróbujesz pobrać dane z czujnika, zawsze sprawdź, czy jest on dostępny na urządzeniu. Nie zakładaj, że czujnik istnieje, tylko dlatego, że jest często używany. Producenci urządzeń nie muszą wyposażać ich w żadne konkretne czujniki.

Starannie wybierz opóźnienia czujników

Podczas rejestrowania czujnika za pomocą metody registerListener() upewnij się, że wybierasz częstotliwość dostarczania, która jest odpowiednia dla Twojej aplikacji lub przypadku użycia. Czujniki mogą dostarczać dane z bardzo dużą częstotliwością. Zezwolenie systemowi na wysyłanie dodatkowych danych, których nie potrzebujesz, marnuje zasoby systemowe i zużywa baterię.