Android 传感器系统是一套设计精良的硬件和软件集合,用于测量运动、屏幕方向和各种环境条件。这些传感器能够提供高精确度和高准确度的原始数据,旨在为移动设备提供丰富的环境感知能力。
Android 系统分为三大类传感器:
您可以使用 Android 传感器框架访问这些传感器并获取原始传感器数据。传感器框架是 android.hardware
软件包的一部分,包含以下类和接口:
此类用于创建特定传感器的实例,并且提供了各种方法,用于确定传感器的特性。
此类用于创建传感器服务的实例。提供了用于访问传感器、注册和取消注册传感器事件监听器以及获取屏幕方向信息的各种方法。此类还提供了几个传感器常量,用于报告传感器精确度、设置数据采集速率和校准传感器。
系统使用此类来创建传感器事件对象,该对象提供有关传感器事件的信息。传感器事件对象包含以下信息:原始传感器数据、生成事件的传感器类型、数据的准确性以及事件的时间戳。
该接口提供了两个回调方法,在传感器值或传感器精度发生变化时接收通知(传感器事件)。
在了解完关于 Android 系统中设备传感器的基础知识后,下面将通过案例教你如何以正确的方式将传感器集成到现有架构中,拓展你的应用程序。
首先引入需要的依赖,在模块级别的 build.gradle 文件中添加。
plugins {
...
id 'kotlin-kapt'
id 'dagger.hilt.android.plugin'
}
...
dependencies {
// ViewModel
implementation "androidx.lifecycle:lifecycle-viewmodel-compose:2.6.2"
//Dagger - Hilt
implementation "androidx.hilt:hilt-lifecycle-viewmodel:1.0.0-alpha03"
kapt "androidx.hilt:hilt-compiler:1.0.0"
implementation 'com.google.dagger:hilt-android:2.38.1'
kapt 'com.google.dagger:hilt-compiler:2.38.1'
}
在项目级别的 build.gradle 中也添加依赖注入(DI)的插件
buildscript {
dependencies {
classpath "com.google.dagger:hilt-android-gradle-plugin:2.38.1"
}
}
为了更好地对传感器进行替换或测试,我们需要对它进行抽象化。创建一个名为 MeasurableSensor 抽象类,定义传感器的通用范式,提供给子类使用,后面也将在 ViewModel 中使用该类型。
abstract class MeasurableSensor(
protected val sensorType:Int// 传感器类型
) {
//检测手机是否具有该传感器的功能
abstract val hasSensorFeature: Boolean
//开始监听
abstract fun startListening()
//停止监听
abstract fun stopListening()
//用于接收传感器更改时的值,类型为带参的函数
protected var onSensorValueChanged: ((List<Float>) -> Unit)? = null
//设置传感器更改监听事件
fun setOnSensorValueChangedListening(listener: (List<Float>) -> Unit) {
onSensorValueChanged = listener
}
}
接下来创建 Android 特定的传感器抽象类,继承自 MeasurableSensor 中通用的传感器范式,以及实现 SensorEventListener 接口。在该抽象类中,主要实现通用的操作逻辑。
abstract class AndroidSensor(
private val context: Context,
private val sensorFeature: String,
sensorType: Int
): MeasurableSensor(sensorType), SensorEventListener {
// 根据外部传入的类型,检查该传感器功能是否可用
override val hasSensorFeature: Boolean
get() = context.packageManager.hasSystemFeature(sensorFeature)
// 创建系统的传感器管理器
private lateinit var sensorManager: SensorManager
// 系统的传感器
private var sensor: Sensor? = null
// 开始监听
override fun startListening() {
if (!hasSensorFeature) {
return
}
if (!::sensorManager.isInitialized && sensor == null) {
sensorManager =
context.getSystemService(SensorManager::class.java) as SensorManager
sensor = sensorManager.getDefaultSensor(sensorType)
}
sensor?.let { sensor ->
//注册监听
sensorManager.registerListener(this, sensor, SensorManager.SENSOR_DELAY_NORMAL)
}
}
// 停止监听
override fun stopListening() {
if (!hasSensorFeature && !::sensorManager.isInitialized) {
return
}
sensor?.let {
//移除监听
sensorManager.unregisterListener(this)
}
}
// 传感器更改时执行
override fun onSensorChanged(event: SensorEvent?) {
if (!hasSensorFeature) {
return
}
if (event?.sensor?.type == sensorType) {
onSensorValueChanged?.invoke(event.values.toList())
}
}
// 关于精度的改变,这里不需要
override fun onAccuracyChanged(sensor: Sensor?, accuracy: Int) = Unit
}
创建 Sensors.kt 文件,内部存放具体实现的传感器。这里以光线传感器为例,继承 AndroidSensor 中通用的传感器操作逻辑,具体的只需要传入类型参数即可。
如果将来需要添加新的传感器类型,可以通过创建新的子类,例如,只需要创建其他具体子类传感器继承自 MeasurableSensor 即可完成功能的扩展,十分方便。
/** 光线传感器 **/
class LightSensor(
context: Context
) : AndroidSensor(
context = context,
sensorFeature = PackageManager.FEATURE_SENSOR_LIGHT,
sensorType = Sensor.TYPE_LIGHT
)
/** ...传感器 **/
/** ...传感器 **/
使用依赖注入为应用程序提供传感器类,注意这里的返回类型为顶级的父类 MeasurableSensor,这样做的好处是提高系统灵活性和可扩展性,让开发者轻松地替换子类,可以方便的进行使用测试。
@Module
@InstallIn(SingletonComponent::class)
object SensorModule {
@Provides
@Singleton
fun provideLightSensor(app: Application): MeasurableSensor {
return LightSensor(app)
}
}
创建全局的应用程序类,在清单文件中声明。
@HiltAndroidApp
class MyApp:Application()
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools">
<application
android:name=".MyApp"
...
>
...
</application>
</manifest>
创建 MainViewModel,声明传感器类,在初始化的时候设置传感器监听事件用于接收系统发送的数据,当环境光线低于设置的值60时,更改 isDark 状态,驱动UI刷新。
@HiltViewModel
class MainViewModel @Inject constructor(
// Dagger&hilt 会自动注入对象
private val lightSensor: MeasurableSensor
) : ViewModel() {
var isDark by mutableStateOf(false)
private set
init {
lightSensor.startListening()
lightSensor.setOnSensorValueChangedListening { values ->
// lux:代表着照明单位
val lux = values[0]
isDark = lux < 60f
}
}
}
MainActivty 只有一个简单的文本以及背景用于显示当前环境的状态,光线低于设定值时会显示黑色状态,否则处于白色状态。
@AndroidEntryPoint
class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
SensorGuideTheme {
val viewModel = viewModel<MainViewModel>()
val isDark = viewModel.isDark
Box(
modifier = Modifier
.fillMaxSize()
.background(if (isDark) Color.DarkGray else Color.White),
contentAlignment = Alignment.Center
) {
Text(
text = if (isDark) "当前环境光线:Dark" else "当前环境光线:Light",
color = if (isDark) Color.White else Color.DarkGray
)
}
}
}
}
}
运行,当我们使用卡片遮传感器的光线时,背景也变成了灰色,说明传感器正常运作。
https://github.com/AAnthonyyyy/AndroidSensorGuide
更多技术文章,请关注一下本人的公众号,会不定期发布一些关于 Android、Kotlin、Jetpack Compose相关的学习笔记和知识,感谢支持。