传感器概述

大多数搭载 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 硬件 测量相对于设备屏幕,物体靠近的距离,单位为cm。此传感器通常用于确定手机是否被举到用户的耳边。 通话期间的手机位置。
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,则必须安装修订版 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()方法注册传感器时,请确保您选择的传递速率适合您的应用或用例。传感器可以以非常高的速率提供数据。允许系统发送您不需要的额外数据会浪费系统资源并消耗电池电量。