传感器概述

大多数 Android 设备都内置了传感器,可以测量运动、方向和各种环境条件。这些传感器能够以高精度和准确性提供原始数据,如果您想监控三维设备运动或定位,或者您想监控设备附近环境的变化,它们非常有用。例如,游戏可能会跟踪来自设备重力传感器的读数,以推断复杂的用户手势和运动,例如倾斜、摇晃、旋转或摆动。同样,天气应用程序可能会使用设备的温度传感器和湿度传感器来计算和报告露点,或者旅行应用程序可能会使用地磁场传感器和加速度计来报告罗盘方位。

Android 平台支持三种主要类型的传感器

  • 运动传感器

    这些传感器测量沿三个轴的加速度力和旋转力。此类别包括加速度计、重力传感器、陀螺仪和旋转矢量传感器。

  • 环境传感器

    这些传感器测量各种环境参数,例如环境气温、气压、照度和湿度。此类别包括气压计、光度计和温度计。

  • 位置传感器

    这些传感器测量设备的物理位置。此类别包括方向传感器和磁力计。

您可以使用 Android 传感器框架访问设备上可用的传感器并获取原始传感器数据。传感器框架提供了一些类和接口,可帮助您执行各种传感器相关任务。例如,您可以使用传感器框架执行以下操作

  • 确定设备上可用的传感器。
  • 确定单个传感器的功能,例如其最大范围、制造商、功耗和分辨率。
  • 获取原始传感器数据并定义获取传感器数据的最低速率。
  • 注册和注销监视传感器变化的传感器事件监听器。

本主题概述了 Android 平台上可用的传感器。它还介绍了传感器框架。

传感器简介

Android 传感器框架允许您访问多种类型的传感器。其中一些传感器是基于硬件的,而另一些是基于软件的。基于硬件的传感器是内置于手机或平板电脑设备的物理组件。它们通过直接测量特定环境属性(例如加速度、地磁场强度或角度变化)来获取数据。基于软件的传感器不是物理设备,但它们模拟了基于硬件的传感器。基于软件的传感器从一个或多个基于硬件的传感器中获取数据,有时被称为虚拟传感器或合成传感器。线性加速度传感器和重力传感器是基于软件的传感器的示例。表 1 总结了 Android 平台支持的传感器。

很少有 Android 设备拥有所有类型的传感器。例如,大多数手机和平板电脑都配备了加速度计和磁力计,但配备气压计或温度计的设备较少。此外,设备可以拥有一个类型中不止一个传感器。例如,设备可以拥有两个重力传感器,每个传感器具有不同的范围。

表 1. Android 平台支持的传感器类型。

传感器 类型 描述 常用用途
TYPE_ACCELEROMETER 硬件 测量施加到设备所有三个物理轴(x、y 和 z)上的加速度力,包括重力。以 m/s2 为单位。 运动检测(摇晃、倾斜等)。
TYPE_AMBIENT_TEMPERATURE 硬件 测量环境室温。以摄氏度 (°C) 为单位。见下文备注。 监控气温。
TYPE_GRAVITY 软件或硬件 测量施加到设备所有三个物理轴(x、y 和 z)上的重力。以 m/s2 为单位。 运动检测(摇晃、倾斜等)。
TYPE_GYROSCOPE 硬件 测量设备绕三个物理轴(x、y 和 z)中的每一个旋转的速度。以 rad/s 为单位。 旋转检测(旋转、转动等)。
TYPE_LIGHT 硬件 测量环境光线水平(照度)。以 lx 为单位。 控制屏幕亮度。
TYPE_LINEAR_ACCELERATION 软件或硬件 测量施加到设备所有三个物理轴(x、y 和 z)上的加速度力,不包括重力。以 m/s2 为单位。 监控沿单个轴的加速度。
TYPE_MAGNETIC_FIELD 硬件 测量所有三个物理轴(x、y 和 z)上的环境地磁场。以 μT 为单位。 创建指南针。
TYPE_ORIENTATION 软件 测量设备绕三个物理轴(x、y 和 z)中的每一个旋转的度数。从 API 级别 3 开始,您可以使用重力传感器和地磁场传感器结合 getRotationMatrix() 方法来获取设备的倾斜矩阵和旋转矩阵。 确定设备位置。
TYPE_PRESSURE 硬件 测量环境气压,单位为 hPa 或 mbar。 监测气压变化。
TYPE_PROXIMITY 硬件 测量物体相对于设备屏幕的距离,单位为厘米。此传感器通常用于确定手机是否被举到用户的耳边。 通话期间的手机位置。
TYPE_RELATIVE_HUMIDITY 硬件 测量环境相对湿度,单位为百分比 (%)。 监测露点、绝对湿度和相对湿度。
TYPE_ROTATION_VECTOR 软件或硬件 通过提供设备旋转向量的三个元素来测量设备的方向。 运动检测和旋转检测。
TYPE_TEMPERATURE 硬件 测量设备温度,单位为摄氏度 (°C)。此传感器实现方式因设备而异,此传感器在 API 级别 14 中被 TYPE_AMBIENT_TEMPERATURE 传感器取代。 监测温度。

传感器框架

您可以使用 Android 传感器框架访问这些传感器并获取原始传感器数据。传感器框架是 android.hardware 包的一部分,包括以下类和接口

SensorManager
您可以使用此类创建传感器服务的实例。此类提供了多种方法,用于访问和列出传感器、注册和取消注册传感器事件监听器以及获取方向信息。此类还提供了一些传感器常量,用于报告传感器精度、设置数据采集速率和校准传感器。
传感器
您可以使用此类创建特定传感器的实例。此类提供了多种方法,可以让您确定传感器的功能。
SensorEvent
系统使用此类创建传感器事件对象,该对象提供有关传感器事件的信息。传感器事件对象包括以下信息:原始传感器数据、生成事件的传感器类型、数据的精度以及事件的时间戳。
SensorEventListener
您可以使用此接口创建两个回调方法,当传感器值发生变化或传感器精度发生变化时,这些方法会接收通知(传感器事件)。

在一个典型的应用程序中,您使用这些与传感器相关的 API 来执行两个基本任务

  • 识别传感器和传感器功能

    在运行时识别传感器和传感器功能非常有用,尤其是在您的应用程序中存在依赖于特定传感器类型或功能的特性时。例如,您可能想要识别设备上存在的所有传感器,并禁用依赖于不存在的传感器的应用程序特性。同样,您可能想要识别所有特定类型的传感器,以便您可以选择最适合您的应用程序的传感器实现。

  • 监控传感器事件

    监控传感器事件是您获取原始传感器数据的途径。每次传感器检测到其正在测量的参数发生变化时,就会发生传感器事件。传感器事件会向您提供四条信息:触发事件的传感器的名称、事件的时间戳、事件的精度以及触发事件的原始传感器数据。

传感器可用性

虽然传感器的可用性因设备而异,但它也会在不同的 Android 版本之间有所不同。这是因为 Android 传感器是在多个平台版本中引入的。例如,许多传感器是在 Android 1.5(API 级别 3)中引入的,但有些传感器尚未实现,直到 Android 2.3(API 级别 9)才可供使用。同样,在 Android 2.3(API 级别 9)和 Android 4.0(API 级别 14)中也引入了多个传感器。两个传感器已被弃用,并被更新、更好的传感器所取代。

表 2 汇总了每个传感器在不同平台上的可用性。仅列出了四个平台,因为这些平台涉及传感器更改。列为已弃用的传感器在后续平台上仍然可用(前提是该传感器存在于设备上),这符合 Android 的向前兼容性策略。

表 2. 按平台划分的传感器可用性。

传感器 Android 4.0
(API 级别 14)
Android 2.3
(API 级别 9)
Android 2.2
(API 级别 8)
Android 1.5
(API 级别 3)
TYPE_ACCELEROMETER
TYPE_AMBIENT_TEMPERATURE n/a n/a n/a
TYPE_GRAVITY n/a n/a
TYPE_GYROSCOPE n/a1 n/a1
TYPE_LIGHT
TYPE_LINEAR_ACCELERATION n/a n/a
TYPE_MAGNETIC_FIELD
TYPE_ORIENTATION 2 2 2
TYPE_PRESSURE n/a1 n/a1
TYPE_PROXIMITY
TYPE_RELATIVE_HUMIDITY n/a n/a n/a
TYPE_ROTATION_VECTOR n/a n/a
TYPE_TEMPERATURE 2

1 此传感器类型是在 Android 1.5(API 级别 3)中添加的,但直到 Android 2.3(API 级别 9)才可供使用。

2 此传感器可用,但已弃用。

识别传感器和传感器功能

Android 传感器框架提供了一些方法,可以让您轻松地在运行时确定设备上存在哪些传感器。该 API 还提供了一些方法,可以让您确定每个传感器的功能,例如其最大范围、分辨率和功耗。

要识别设备上存在的传感器,您首先需要获取对传感器服务的引用。为此,您通过调用 getSystemService() 方法并传入 SENSOR_SERVICE 参数,来创建 SensorManager 类的实例。例如

Kotlin

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

Java

private SensorManager sensorManager;
...
sensorManager = (SensorManager) getSystemService(Context.SENSOR_SERVICE);

接下来,您可以通过调用 getSensorList() 方法并使用 TYPE_ALL 常量,获取设备上所有传感器的列表。例如

Kotlin

val deviceSensors: List<Sensor> = sensorManager.getSensorList(Sensor.TYPE_ALL)

Java

List<Sensor> deviceSensors = sensorManager.getSensorList(Sensor.TYPE_ALL);

如果您想要列出所有特定类型的传感器,可以使用另一个常量代替 TYPE_ALL,例如 TYPE_GYROSCOPETYPE_LINEAR_ACCELERATIONTYPE_GRAVITY

您还可以使用 getDefaultSensor() 方法并传入特定传感器的类型常量,来确定设备上是否存在特定类型的传感器。如果设备上存在多个特定类型的传感器,则其中一个传感器必须被指定为默认传感器。如果在给定类型的传感器中不存在默认传感器,则方法调用将返回 null,这意味着设备上不存在该类型的传感器。例如,以下代码检查设备上是否存在磁力计

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

注意: Android 不要求设备制造商在其 Android 设备上内置任何特定类型的传感器,因此设备可以具有各种传感器配置。

除了列出设备上存在的传感器之外,您还可以使用 Sensor 类的公用方法来确定单个传感器的功能和属性。如果您希望您的应用程序根据设备上存在的传感器或传感器功能的不同而表现不同,这将非常有用。例如,您可以使用 getResolution()getMaximumRange() 方法获取传感器的分辨率和最大测量范围。您还可以使用 getPower() 方法获取传感器的功耗。

如果您希望针对不同的制造商的传感器或不同版本的传感器优化您的应用程序,有两个公用方法尤其有用。例如,如果您的应用程序需要监控用户的动作(如倾斜和摇晃),您可以为具有特定供应商的重力传感器的较新设备创建一组数据过滤规则和优化,而为不具有重力传感器,而只具有加速度计的设备创建另一组数据过滤规则和优化。以下代码示例展示了如何使用 getVendor()getVersion() 方法来实现这一点。在此示例中,我们正在寻找一个以 Google LLC 为供应商且版本号为 3 的重力传感器。如果该特定传感器不在设备上,我们将尝试使用加速度计。

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

另一个有用的方法是 getMinDelay() 方法,它返回传感器用于感知数据的最小时间间隔(以微秒为单位)。任何返回 getMinDelay() 方法的非零值的传感器都是流式传感器。流式传感器会定期感知数据,并在 Android 2.3(API 级别 9)中引入。如果传感器在您调用 getMinDelay() 方法时返回零,则表示该传感器不是流式传感器,因为它只在感知到的参数发生变化时才报告数据。

getMinDelay() 方法非常有用,因为它可以让您确定传感器获取数据的最大速率。如果您的应用程序中的某些特性需要高数据采集速率或流式传感器,您可以使用此方法确定传感器是否满足这些要求,然后相应地启用或禁用您的应用程序中的相关特性。

注意: 传感器的最大数据采集速率不一定与传感器框架向您的应用程序传递传感器数据的速率相同。传感器框架通过传感器事件报告数据,并且有多个因素会影响您的应用程序接收传感器事件的速率。有关详细信息,请参阅 监控传感器事件

监控传感器事件

要监控原始传感器数据,您需要实现 SensorEventListener 接口公开的两个回调方法:onAccuracyChanged()onSensorChanged()。每当发生以下情况时,Android 系统就会调用这些方法

以下代码展示了如何使用 onSensorChanged() 方法监控光线传感器的。此示例将原始传感器数据显示在 TextView 中,该 TextView 在 main.xml 文件中定义为 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);
    }
}

在此示例中,当调用 registerListener() 方法时,会指定默认数据延迟(SENSOR_DELAY_NORMAL)。数据延迟(或采样率)控制通过 onSensorChanged() 回调方法将传感器事件发送到您的应用程序的时间间隔。默认数据延迟适用于监控典型的屏幕方向更改,并使用 200,000 微秒的延迟。您可以指定其他数据延迟,例如 SENSOR_DELAY_GAME(20,000 微秒延迟)、SENSOR_DELAY_UI(60,000 微秒延迟)或 SENSOR_DELAY_FASTEST(0 微秒延迟)。从 Android 3.0(API 级别 11)开始,您还可以将延迟指定为绝对值(以微秒为单位)。

您指定的延迟只是一个建议延迟。Android 系统和其他应用程序可以更改此延迟。作为最佳实践,您应该指定您能使用的最大延迟,因为系统通常使用比您指定的延迟更小的延迟(也就是说,您应该选择仍然满足应用程序需求的最慢采样率)。使用更大的延迟会减轻处理器负载,因此会更省电。

没有公开方法可以确定传感器框架将传感器事件发送到您的应用程序的速率;但是,您可以使用与每个传感器事件相关联的时间戳来计算多个事件的采样率。您无需在设置采样率(延迟)后进行更改。如果由于某种原因您确实需要更改延迟,则必须注销并重新注册传感器侦听器。

还需要注意的是,此示例使用 onResume()onPause() 回调方法来注册和注销传感器事件侦听器。作为最佳实践,您应该始终禁用不需要的传感器,尤其是在您的活动暂停时。如果不这样做,可能会在短短几个小时内耗尽电池电量,因为某些传感器具有相当大的功耗,并且可以快速耗尽电池电量。系统不会在屏幕关闭时自动禁用传感器。

处理不同的传感器配置

Android 没有为设备指定标准的传感器配置,这意味着设备制造商可以在其 Android 设备中加入他们想要的任何传感器配置。因此,设备可以包含各种传感器,以多种配置形式存在。如果您的应用程序依赖于特定类型的传感器,您必须确保该传感器存在于设备上,以便您的应用程序能够成功运行。

您有两个选项可以确保特定传感器存在于设备上

  • 在运行时检测传感器,并根据需要启用或禁用应用程序功能。
  • 使用 Google Play 过滤器来针对具有特定传感器配置的设备。

以下各节将讨论每个选项。

在运行时检测传感器

如果您的应用程序使用特定类型的传感器,但不依赖于它,您可以使用传感器框架在运行时检测传感器,然后根据需要禁用或启用应用程序功能。例如,导航应用程序可能会使用温度传感器、压力传感器、GPS 传感器和地磁场传感器来显示温度、气压、位置和指南针方位。如果设备没有压力传感器,您可以使用传感器框架在运行时检测压力传感器的缺失,然后禁用应用程序 UI 中显示压力的部分。例如,以下代码检查设备上是否存在压力传感器

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

使用 Google Play 过滤器来针对特定传感器配置

如果您要在 Google Play 上发布您的应用程序,可以使用清单文件中的 <uses-feature> 元素来过滤不具有适用于您的应用程序的适当传感器配置的设备的应用程序。 <uses-feature> 元素具有多个硬件描述符,可让您根据特定传感器的存在来过滤应用程序。您可以列出的传感器包括:加速度计、气压计、罗盘(地磁场)、陀螺仪、光线和接近传感器。以下是一个清单条目示例,该条目会过滤没有加速度计的应用程序

<uses-feature android:name="android.hardware.sensor.accelerometer"
              android:required="true" />

如果您将此元素和描述符添加到应用程序的清单中,用户只有在他们的设备具有加速度计的情况下才会在 Google Play 上看到您的应用程序。

仅当您的应用程序完全依赖于特定传感器时,才应将描述符设置为 android:required="true"。如果您的应用程序使用传感器来执行某些功能,但即使没有传感器也能运行,则应在 <uses-feature> 元素中列出该传感器,但将描述符设置为 android:required="false"。这有助于确保设备即使没有特定传感器也可以安装您的应用程序。这也是一个项目管理最佳实践,可以帮助您跟踪应用程序使用的功能。请记住,如果您的应用程序使用特定传感器,但即使没有传感器也能运行,那么您应该在运行时检测该传感器,并根据需要禁用或启用应用程序功能。

传感器坐标系

通常,传感器框架使用标准的 3 轴坐标系来表示数据值。对于大多数传感器,坐标系是在设备处于默认方向时相对于设备屏幕定义的(参见图 1)。当设备处于默认方向时,X 轴水平指向右侧,Y 轴垂直指向上方,Z 轴指向屏幕表面的外部。在此系统中,屏幕后面的坐标具有负 Z 值。以下传感器使用此坐标系

图 1. 传感器 API 使用的坐标系(相对于设备)。

关于此坐标系,最重要的是要了解轴在设备屏幕方向发生变化时不会交换,也就是说,传感器坐标系在设备移动时永远不会改变。这种行为与 OpenGL 坐标系的行为相同。

另一个需要理解的点是,您的应用程序不能假设设备的自然(默认)方向是纵向。许多平板设备的自然方向是横向。传感器坐标系始终基于设备的自然方向。

最后,如果您的应用程序将传感器数据与屏幕上的显示相匹配,则需要使用 getRotation() 方法来确定屏幕旋转,然后使用 remapCoordinateSystem() 方法将传感器坐标映射到屏幕坐标。即使您的清单指定了仅限纵向显示,您也需要这样做。

注意:某些传感器和方法使用相对于世界参考系(而不是设备参考系)的坐标系。这些传感器和方法返回表示设备运动或设备相对于地球的位置的数据。有关详细信息,请参阅 getOrientation() 方法、 getRotationMatrix() 方法、 方向传感器旋转矢量传感器.

传感器速率限制

为了保护有关用户的潜在敏感信息,如果您的应用程序的目标是 Android 12(API 级别 31)或更高版本,则系统会对来自某些运动传感器和位置传感器的数据的刷新率进行限制。此数据包括设备的 加速度计陀螺仪地磁场传感器 记录的值。

刷新率限制取决于您访问传感器数据的方式

如果您的应用程序需要以更高的速率收集运动传感器数据,则必须声明 HIGH_SAMPLING_RATE_SENSORS 权限,如以下代码片段所示。否则,如果您的应用程序尝试在未声明此权限的情况下以更高的速率收集运动传感器数据,则会发生 SecurityException

AndroidManifest.xml

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

访问和使用传感器的最佳实践

在设计传感器实现时,请务必遵循本节中讨论的指南。这些指南是使用传感器框架访问传感器和获取传感器数据的任何人的推荐最佳实践。

仅在前景中收集传感器数据

在运行 Android 9(API 级别 28)或更高版本的设备上,在后台运行的应用程序具有以下限制

  • 使用连续报告模式的传感器(例如加速度计和陀螺仪)不会接收事件。
  • 使用更改时单次报告模式的传感器不会接收事件。

鉴于这些限制,最好在您的应用处于前台时或作为前台服务的一部分来检测传感器事件。

取消注册传感器监听器

在您完成使用传感器或传感器活动暂停时,请确保取消注册传感器的监听器。如果注册了传感器监听器并且其活动已暂停,除非您取消注册传感器,否则传感器将继续获取数据并使用电池资源。以下代码展示了如何使用onPause()方法取消注册监听器

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);
}

有关更多信息,请参见unregisterListener(SensorEventListener)

使用 Android 模拟器测试

Android 模拟器包含一组虚拟传感器控件,允许您测试传感器,例如加速度计、环境温度、磁力计、接近传感器、光线传感器等。

模拟器使用与运行SdkControllerSensor应用的 Android 设备的连接。请注意,此应用仅适用于运行 Android 4.0(API 级别 14)或更高版本的设备。(如果设备运行的是 Android 4.0,则必须安装 Revision 2。)SdkControllerSensor 应用会监控设备上传感器的变化并将它们传输到模拟器。然后根据从您设备上的传感器接收到的新值,对模拟器进行转换。

您可以在以下位置查看SdkControllerSensor 应用的源代码

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

要将数据从您的设备传输到模拟器,请执行以下步骤

  1. 检查您的设备上是否启用了 USB 调试
  2. 使用 USB 线将您的设备连接到开发机器。
  3. 在您的设备上启动SdkControllerSensor 应用。
  4. 在应用中,选择您要模拟的传感器。
  5. 运行以下adb 命令

  6. $ adb forward tcp:1968 tcp:1968
    
  7. 启动模拟器。您现在应该能够通过移动设备来对模拟器应用转换。

注意:如果对物理设备所做的移动没有转换模拟器,请尝试再次运行步骤 5 中的adb 命令。

有关更多信息,请参见Android 模拟器指南

不要阻塞 onSensorChanged() 方法

传感器数据可能会以很高的速率变化,这意味着系统可能会经常调用onSensorChanged(SensorEvent) 方法。作为最佳实践,您应该在onSensorChanged(SensorEvent) 方法中尽可能少地执行操作,这样就不会阻塞它。如果您的应用程序需要您对传感器数据进行任何数据过滤或减少,则应在onSensorChanged(SensorEvent) 方法之外执行该操作。

避免使用已弃用的方法或传感器类型

一些方法和常量已被弃用。特别是,TYPE_ORIENTATION 传感器类型已被弃用。要获取方向数据,您应该改为使用getOrientation() 方法。同样,TYPE_TEMPERATURE 传感器类型已被弃用。在运行 Android 4.0 的设备上,您应该改为使用TYPE_AMBIENT_TEMPERATURE 传感器类型。

在使用传感器之前验证传感器

在尝试从传感器获取数据之前,始终验证设备上是否存在传感器。不要假设传感器存在只是因为它是一个经常使用的传感器。设备制造商不需要在其设备中提供任何特定传感器。

谨慎选择传感器延迟

当您使用registerListener() 方法注册传感器时,请确保您选择适合您的应用程序或用例的传递速率。传感器可以以非常高的速率提供数据。允许系统发送您不需要的额外数据会浪费系统资源并消耗电池电量。