Из этой статьи вы узнаете, как получить доступ к основным аппаратным датчикам Android, а также о типах датчиков, доступных на Android, реальных случаях их использования и многом другом.
Типы распространенных датчиков, доступных в Android:
- акселерометр (accelerometer) — датчик, сообщающий об ускорении устройства по трем осям X, Y, Z; измеряемое ускорение включает как физическое ускорение (изменение скорости), так и силу тяжести;
- гироскоп, или датчик вектора поворота (gyroscope) — датчик, сообщающий о скорости вращения устройства в пространстве относительно трех осей;
- датчик приближенности (proximity) сообщает о расстоянии от датчика до ближайшей видимой поверхности; обычно используется в телефонных приложениях для определения расстояния от динамика до дисплея; его можно автоматически отключить от экрана;
- магнитометр (magnetometer) — датчик магнитного поля, сообщающий текущие показатели магнитного поля окружающей среды, измеренные по трем осям X, Y, Z; обычно используется в приложении «Компас»;
- датчик освещенности (light sensor/environmental sensor) измеряет степень освещенности окружающей среды; обычно используется для динамического изменения яркости экрана.
Примечание: существуют и другие типы датчиков, доступные в мобильных устройствах Android, например датчик температуры окружающей среды (Ambient temperature), датчик барометрического (атмосферного) давления (barometer sensor). Но наличие этих датчиков зависит от конкретного производителя, а следовательно, они менее распространены, чем упомянутые выше датчики: одни телефоны могут быть оснащены такими датчиками, другие нет. Поэтому, если вы полагаетесь на эти датчики в своем приложении, определите во время выполнения их доступность на Android-устройстве и возможности.
Как выяснить доступность датчиков на устройстве?
Чтобы определить датчики, установленные на устройстве, нужно сначала получить ссылку на службу датчиков (sensor service). Для этого следует создать экземпляр класса SensorManager, вызвав метод getSystemService() и передав аргумент SENSOR_SERVICE. Например:
private lateinit var sensorManager: SensorManager
...
sensorManager = getSystemService(Context.SENSOR_SERVICE) as SensorManager
if (sensorManager.getDefaultSensor(Sensor.TYPE_AMBIENT_TEMPERATURE) != null) {
// Успешно! Датчик температуры окружающей среды есть в наличии.
} else {
// Неудачно! Датчика температуры окружающей среды нет.
}
Если метод getDefaultSensor() возвращает null для указанного по умолчанию типа датчика, это означает, что на устройстве нет доступного датчика.
Чтобы перечислить все доступные на устройстве датчики, используйте getSensorList() из класса SensorManager.
val deviceSensors: List<Sensor> = sensorManager.getSensorList(Sensor.TYPE_ALL)
Реализация с использованием фреймворка для Android-датчиков
Класс SensorManager предоставляет различные методы доступа к датчикам и их спискам, регистрации и снятия с регистрации слушателей событий датчиков, а также получения информации об ориентации.
Доступ к датчику освещенности окружающей среды в Android:
class AndroidSensorEventListener(
context: Context
) : SensorEventListener {
private val lightSensorLux = FloatArray(1) // используйте второй индекс для доступа к необработанным данным датчика освещенности
private val sensorManager: SensorManager =
context.getSystemService(Context.SENSOR_SERVICE) as SensorManager
interface LuxValueListener {
fun onAzimuthValueChange(luxValue: Float) // комплексный подход
}
private var luxValueListener: LuxValueListener?= null
override fun onSensorChanged(event: SensorEvent) {
if (event.sensor.type == Sensor.TYPE_LIGHT) {
System.arraycopy(event.values, 0, lightSensorLux, 0, lightSensorLux.size)
}
luxValueListener?.onAzimuthValueChange(lightSensorLux[0])
}
override fun onAccuracyChanged(sensor: Sensor?, accuracy: Int) {}
fun registerSensor() {
sensorManager.getDefaultSensor(Sensor.TYPE_LIGHT)?.also { accelerometer ->
sensorManager.registerListener(
this,
accelerometer,
SensorManager.SENSOR_DELAY_NORMAL,
SensorManager.SENSOR_DELAY_UI
)
}
}
fun unregisterSensorListener() {
sensorManager.unregisterListener(this)
}
fun setLuxListener(listener: LuxValueListener) {
luxValueListener = listener
}
}
Здесь Sensor — конкретная реализация определенного датчика. Можно использовать этот класс для получения конкретных сведений о датчике, включая:
- getMinDelay();
- getVendor();
- isWakeUpSensor() и др.
Здесь также реализован SensorEventListener. С помощью этого интерфейса можно использовать два обратных вызова в качестве событий датчика для получения обновлений датчика, таких как onSensorChanged и onAccuracyChanged.
И, наконец, SensorEvent, который является важным классом, предоставляющим данные датчика, его временную метку и точность.
Датчики положения
Такие датчики измеряют физическое положение устройства. В эту категорию входят датчики ориентации (приближенности) и магнитометры.
Реализация магнитометра:
class AndroidSensorEventListener(
private val context: Context
) : SensorEventListener {
private val accelerometerReading = FloatArray(3)
private val magnetometerReading = FloatArray(3)
private val rotationMatrix = FloatArray(9)
private val adjustedRotationMatrix = FloatArray(9)
private val orientationAngles = FloatArray(3)
private val sensorManager: SensorManager =
context.getSystemService(Context.SENSOR_SERVICE) as SensorManager
interface AzimuthValueListener {
fun onAzimuthValueChange(degree: Float)
}
private var azimuthValueListener: AzimuthValueListener?= null
override fun onSensorChanged(event: SensorEvent) {
if (event.sensor.type == Sensor.TYPE_ACCELEROMETER) {
System.arraycopy(event.values, 0, accelerometerReading, 0, accelerometerReading.size)
} else if (event.sensor.type == Sensor.TYPE_MAGNETIC_FIELD) {
System.arraycopy(event.values, 0, magnetometerReading, 0, magnetometerReading.size)
}
updateOrientationAngles()
}
override fun onAccuracyChanged(sensor: Sensor?, accuracy: Int) {
if (accuracy == SensorManager.SENSOR_STATUS_UNRELIABLE || accuracy == SensorManager.SENSOR_STATUS_ACCURACY_LOW) {
Toast.makeText(context, R.string.calibration_required, Toast.LENGTH_LONG).show()
}
}
private fun updateOrientationAngles() {
val isSuccess: Boolean = SensorManager.getRotationMatrix(
rotationMatrix, null, accelerometerReading, magnetometerReading
)
val rotation = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
context.display?.rotation
} else {
val windowManager = context.getSystemService(Context.WINDOW_SERVICE) as WindowManager
@Suppress("DEPRECATION") windowManager.defaultDisplay.rotation
}
when (rotation) {
// Используется Android-класс Surface для определения углов поворота устройства и соответствующего обновления оси вращения датчика
Surface.ROTATION_0 -> SensorManager.remapCoordinateSystem(
rotationMatrix, SensorManager.AXIS_X, SensorManager.AXIS_Y, adjustedRotationMatrix
)
Surface.ROTATION_90 -> SensorManager.remapCoordinateSystem(
rotationMatrix,
SensorManager.AXIS_Y,
SensorManager.AXIS_MINUS_X,
adjustedRotationMatrix
)
Surface.ROTATION_180 -> SensorManager.remapCoordinateSystem(
rotationMatrix,
SensorManager.AXIS_MINUS_X,
SensorManager.AXIS_MINUS_Y,
adjustedRotationMatrix
)
Surface.ROTATION_270 -> SensorManager.remapCoordinateSystem(
rotationMatrix,
SensorManager.AXIS_MINUS_Y,
SensorManager.AXIS_X,
adjustedRotationMatrix
)
}
if (isSuccess) {
SensorManager.getOrientation(adjustedRotationMatrix, orientationAngles)
val azimuth = orientationAngles[0]
val toDegree = ToDegree.toDegree(azimuth)
azimuthValueListener?.onAzimuthValueChange(toDegree)
}
}
fun registerSensor() {
sensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER)?.also { accelerometer ->
sensorManager.registerListener(
this,
accelerometer,
SensorManager.SENSOR_DELAY_NORMAL,
SensorManager.SENSOR_DELAY_UI
)
}
sensorManager.getDefaultSensor(Sensor.TYPE_MAGNETIC_FIELD)?.also { magneticField ->
sensorManager.registerListener(
this,
magneticField,
SensorManager.SENSOR_DELAY_NORMAL,
SensorManager.SENSOR_DELAY_UI
)
}
}
fun unregisterSensorListener() {
sensorManager.unregisterListener(this)
}
fun setAzimuthListener(listener: AzimuthValueListener) {
azimuthValueListener = listener
}
}
В приведенном выше примере кода объединены значения акселерометра и магнитометра с помощью методов getRotationMatrix() и getOrientation() из SensorManager для отслеживания положения устройства относительно земной системы координат на северном полюсе.
В методе updateOrientationAngles использована функция remapCoordinateSystem() из SensorManager для преобразования значений ориентации в систему отсчета приложения (необходимо для приложения «Компас», которое соответствующим образом поворачивает угол).
Заключение
Чтобы увидеть реальное использование демонстрационного приложения, можете заглянуть на два GitHub-репозитория по реализации датчиков на Android-устройствах.
В одном из этих репозиториев представлено приложение IllumiSur, которое является реализацией датчика освещенности для определения значений в люксах.
Приложение MBCompass из второго репозитория демонстрирует работу магнитометра и представляет собой реальный геомагнитный компас.
Читайте также:
- Автоматизация создания файлов для нового экрана с плагином для Android Studio
- Компонентный подход: организация навигации с помощью библиотеки Decompose. Часть 3
- Как создать анимацию кругового вытеснения в Jetpack Compose
Читайте нас в Telegram, VK и Дзен
Перевод статьи Mubarak Native: A guide to accessing android device sensors in android





