KMP 结构适配鸿蒙:传感器与设备信息理论知识
本文介绍了在Kotlin Multiplatform(KMP)框架下实现跨平台传感器数据采集和设备信息获取的方案。通过抽象统一的SensorManager和DeviceInfoManager接口,屏蔽了Android、iOS等平台的API差异。核心内容包括:1)定义传感器类型、数据结构和监听机制;2)封装设备静态信息和运行时状态;3)提供Android平台的具体实现。该方案可广泛应用于运动健康、游
项目概述
传感器与设备信息是现代应用获取设备状态和用户交互的重要方式。在 KMP 框架下,我们需要实现跨平台的传感器数据采集、设备信息获取和系统状态监测。本文详细介绍了如何在 Kotlin Multiplatform 中实现高效的传感器系统,包括加速度计、陀螺仪、磁力计和设备信息获取。
这类能力在以下场景尤其常见:
- 运动/健康 App:通过加速度计、陀螺仪判断步数、姿态、运动强度;
- 游戏/交互应用:利用重力感应、旋转等实现体感操作;
- 系统监控与运营:采集设备型号、系统版本、电池状态等,用于统计和问题排查。
本篇的目标是:
- 抽象统一的
SensorManager、DeviceInfoManager接口,屏蔽不同平台的传感器/系统 API 差异; - 在 Android、iOS、鸿蒙及 Web 等平台下实现相同能力;
- 为后续的传感器数据融合、设备状态监控等高级功能提供基础。
第一部分:传感器核心概念
传感器管理架构
// commonMain/kotlin/com/example/sensor/SensorManager.kt
expect class SensorManager {
suspend fun startListening(sensorType: SensorType): Boolean
suspend fun stopListening(sensorType: SensorType): Boolean
suspend fun getSensorData(sensorType: SensorType): SensorData?
suspend fun getAvailableSensors(): List<SensorInfo>
fun addListener(listener: SensorListener)
}
enum class SensorType {
ACCELEROMETER, GYROSCOPE, MAGNETOMETER, LIGHT, PROXIMITY, PRESSURE, TEMPERATURE, HUMIDITY
}
data class SensorData(
val sensorType: SensorType,
val values: FloatArray,
val accuracy: Int,
val timestamp: Long
)
data class SensorInfo(
val name: String,
val type: SensorType,
val vendor: String,
val version: Int,
val power: Float,
val maxRange: Float,
val resolution: Float,
val minDelay: Int,
val maxDelay: Int
)
interface SensorListener {
fun onSensorDataChanged(sensorData: SensorData)
fun onAccuracyChanged(sensorType: SensorType, accuracy: Int)
fun onError(error: String)
}
这一部分定义了跨平台统一的传感器访问模型:
SensorManager提供开始/停止监听、拉取当前数据、查询可用传感器等能力;SensorType统一列出了常见的硬件传感器类型,便于在不同平台上映射;SensorData和SensorInfo分别代表一次传感器读数以及传感器本身的能力描述;SensorListener则负责把底层事件统一回调到业务层,方便做实时 UI 更新或数据采集。
通过这层抽象,你可以在 common 层编写,例如“摇一摇事件监听”“运动轨迹记录”“姿态检测”等逻辑,而不必直接和 Android/iOS/鸿蒙各自的传感器 API 打交道。
设备信息架构
// commonMain/kotlin/com/example/device/DeviceInfoManager.kt
expect class DeviceInfoManager {
suspend fun getDeviceInfo(): DeviceInfo
suspend fun getSystemInfo(): SystemInfo
suspend fun getBatteryInfo(): BatteryInfo
suspend fun getNetworkInfo(): NetworkInfo
suspend fun getStorageInfo(): StorageInfo
fun addListener(listener: DeviceInfoListener)
}
data class DeviceInfo(
val manufacturer: String,
val model: String,
val brand: String,
val device: String,
val product: String,
val hardware: String,
val fingerprint: String,
val buildId: String,
val buildVersion: String,
val sdkVersion: Int,
val releaseVersion: String
)
data class SystemInfo(
val osName: String,
val osVersion: String,
val cpuCores: Int,
val totalMemory: Long,
val availableMemory: Long,
val uptime: Long
)
data class BatteryInfo(
val level: Int,
val temperature: Int,
val voltage: Int,
val health: String,
val status: String,
val isCharging: Boolean,
val chargingTime: Long
)
interface DeviceInfoListener {
fun onBatteryChanged(batteryInfo: BatteryInfo)
fun onNetworkChanged(networkInfo: NetworkInfo)
fun onStorageChanged(storageInfo: StorageInfo)
}
设备信息相关接口关注的是“设备是什么 + 当前状态如何”:
DeviceInfo聚合了厂商、型号、硬件指纹、系统版本等静态信息,可用于日志、AB 实验、灰度配置等;SystemInfo、BatteryInfo等则关注运行时状态(CPU 核心数、内存、电量、温度等),适合健康检查和性能调优;DeviceInfoListener把电池、网络、存储变化统一暴露给业务层,用于触发降级策略或告警提示。
在 KMP 结构中,通过统一的 DeviceInfoManager,可以构建跨平台的“设备/运行环境探测模块”,为埋点、运营配置和稳定性监控提供基础数据。
第二部分:平台特定实现
Android 传感器实现
// androidMain/kotlin/com/example/sensor/SensorManager.kt
import android.hardware.SensorManager as AndroidSensorManager
import android.hardware.SensorEvent
import android.hardware.SensorEventListener
import android.content.Context
actual class SensorManager(private val context: Context) {
private val sensorManager = context.getSystemService(AndroidSensorManager::class.java)
private val listeners = mutableListOf<SensorListener>()
private val sensorListeners = mutableMapOf<SensorType, SensorEventListener>()
actual suspend fun startListening(sensorType: SensorType): Boolean {
return try {
val androidSensorType = when (sensorType) {
SensorType.ACCELEROMETER -> android.hardware.Sensor.TYPE_ACCELEROMETER
SensorType.GYROSCOPE -> android.hardware.Sensor.TYPE_GYROSCOPE
SensorType.MAGNETOMETER -> android.hardware.Sensor.TYPE_MAGNETIC_FIELD
SensorType.LIGHT -> android.hardware.Sensor.TYPE_LIGHT
SensorType.PROXIMITY -> android.hardware.Sensor.TYPE_PROXIMITY
SensorType.PRESSURE -> android.hardware.Sensor.TYPE_PRESSURE
SensorType.TEMPERATURE -> android.hardware.Sensor.TYPE_AMBIENT_TEMPERATURE
SensorType.HUMIDITY -> android.hardware.Sensor.TYPE_RELATIVE_HUMIDITY
}
val sensor = sensorManager?.getDefaultSensor(androidSensorType)
val eventListener = object : SensorEventListener {
override fun onSensorChanged(event: SensorEvent) {
val sensorData = SensorData(
sensorType = sensorType,
values = event.values,
accuracy = event.accuracy,
timestamp = event.timestamp
)
listeners.forEach { it.onSensorDataChanged(sensorData) }
}
override fun onAccuracyChanged(sensor: android.hardware.Sensor?, accuracy: Int) {
listeners.forEach { it.onAccuracyChanged(sensorType, accuracy) }
}
}
sensorListeners[sensorType] = eventListener
sensorManager?.registerListener(eventListener, sensor, AndroidSensorManager.SENSOR_DELAY_NORMAL)
true
} catch (e: Exception) {
listeners.forEach { it.onError(e.message ?: "Failed to start listening") }
false
}
}
actual suspend fun stopListening(sensorType: SensorType): Boolean {
return try {
sensorListeners[sensorType]?.let { listener ->
sensorManager?.unregisterListener(listener)
sensorListeners.remove(sensorType)
}
true
} catch (e: Exception) {
false
}
}
actual suspend fun getAvailableSensors(): List<SensorInfo> {
return try {
sensorManager?.sensorList?.map { sensor ->
SensorInfo(
name = sensor.name,
type = when (sensor.type) {
android.hardware.Sensor.TYPE_ACCELEROMETER -> SensorType.ACCELEROMETER
android.hardware.Sensor.TYPE_GYROSCOPE -> SensorType.GYROSCOPE
else -> SensorType.ACCELEROMETER
},
vendor = sensor.vendor,
version = sensor.version,
power = sensor.power,
maxRange = sensor.maxRange,
resolution = sensor.resolution,
minDelay = sensor.minDelay,
maxDelay = sensor.maxDelay
)
} ?: emptyList()
} catch (e: Exception) {
emptyList()
}
}
actual fun addListener(listener: SensorListener) {
listeners.add(listener)
}
}
// Android 设备信息
actual class DeviceInfoManager {
actual suspend fun getDeviceInfo(): DeviceInfo {
return DeviceInfo(
manufacturer = android.os.Build.MANUFACTURER,
model = android.os.Build.MODEL,
brand = android.os.Build.BRAND,
device = android.os.Build.DEVICE,
product = android.os.Build.PRODUCT,
hardware = android.os.Build.HARDWARE,
fingerprint = android.os.Build.FINGERPRINT,
buildId = android.os.Build.ID,
buildVersion = android.os.Build.VERSION.RELEASE,
sdkVersion = android.os.Build.VERSION.SDK_INT,
releaseVersion = android.os.Build.VERSION.RELEASE
)
}
actual suspend fun getSystemInfo(): SystemInfo {
val runtime = Runtime.getRuntime()
return SystemInfo(
osName = "Android",
osVersion = android.os.Build.VERSION.RELEASE,
cpuCores = Runtime.getRuntime().availableProcessors(),
totalMemory = runtime.totalMemory(),
availableMemory = runtime.freeMemory(),
uptime = android.os.SystemClock.uptimeMillis()
)
}
actual suspend fun getBatteryInfo(): BatteryInfo {
// Implementation for battery info
return BatteryInfo(
level = 0,
temperature = 0,
voltage = 0,
health = "GOOD",
status = "CHARGING",
isCharging = false,
chargingTime = 0L
)
}
}
Android 端传感器与设备信息实现的要点:
- 传感器部分使用
AndroidSensorManager+SensorEventListener,根据SensorType映射到具体的传感器常量; - 在
onSensorChanged中将SensorEvent转换为公共的SensorData,并通过SensorListener广播给上层; - 设备信息利用
Build、SystemClock等系统 API 获取设备型号、系统版本、内存等基础信息,并封装为DeviceInfo、SystemInfo等。
在实际项目中可以进一步扩展:
- 根据不同业务场景调整采样频率(
SENSOR_DELAY_NORMAL/FASTEST),平衡功耗和精度; - 将电池、网络等信息和埋点/日志系统结合,便于线上问题定位;
- 对部分敏感信息进行脱敏或聚合处理,符合隐私和合规要求。
iOS 传感器实现
// iosMain/kotlin/com/example/sensor/SensorManager.kt
import platform.CoreMotion.*
import platform.Foundation.*
actual class SensorManager {
private val motionManager = CMMotionManager()
private val listeners = mutableListOf<SensorListener>()
actual suspend fun startListening(sensorType: SensorType): Boolean {
return try {
when (sensorType) {
SensorType.ACCELEROMETER -> {
motionManager.startAccelerometerUpdatesToQueue(
NSOperationQueue.mainQueue()
) { data, error ->
data?.acceleration?.let { acceleration ->
val sensorData = SensorData(
sensorType = sensorType,
values = floatArrayOf(
acceleration.x.toFloat(),
acceleration.y.toFloat(),
acceleration.z.toFloat()
),
accuracy = 0,
timestamp = System.currentTimeMillis()
)
listeners.forEach { it.onSensorDataChanged(sensorData) }
}
}
}
SensorType.GYROSCOPE -> {
motionManager.startGyroUpdatesToQueue(
NSOperationQueue.mainQueue()
) { data, error ->
data?.rotationRate?.let { rotationRate ->
val sensorData = SensorData(
sensorType = sensorType,
values = floatArrayOf(
rotationRate.x.toFloat(),
rotationRate.y.toFloat(),
rotationRate.z.toFloat()
),
accuracy = 0,
timestamp = System.currentTimeMillis()
)
listeners.forEach { it.onSensorDataChanged(sensorData) }
}
}
}
else -> return false
}
true
} catch (e: Exception) {
false
}
}
actual suspend fun stopListening(sensorType: SensorType): Boolean {
return try {
when (sensorType) {
SensorType.ACCELEROMETER -> motionManager.stopAccelerometerUpdates()
SensorType.GYROSCOPE -> motionManager.stopGyroUpdates()
else -> return false
}
true
} catch (e: Exception) {
false
}
}
}
iOS 端示例主要演示了如何使用 CoreMotion:
- 通过
CMMotionManager分别开启加速度计和陀螺仪数据流,将回调结果转换为公共的SensorData; - 根据不同
SensorType选择开启/停止对应的数据流; - 在 KMP 中将这部分实现放在
iosMain,对上层完全透明。
在生产环境中,还可以配合 UIDevice、ProcessInfo 等 API 获取更多设备与系统状态信息,并统一封装到 DeviceInfoManager 中。
第三部分:编译后的代码形式
JavaScript 编译版本
// 传感器管理器 (JavaScript/Web)
class SensorManager {
constructor() {
this.listeners = [];
this.sensors = {};
}
async startListening(sensorType) {
try {
if (sensorType === 'ACCELEROMETER') {
window.addEventListener('devicemotion', (event) => {
const sensorData = {
sensorType: 'ACCELEROMETER',
values: [
event.acceleration.x,
event.acceleration.y,
event.acceleration.z
],
accuracy: 0,
timestamp: Date.now()
};
this.listeners.forEach(l => l.onSensorDataChanged(sensorData));
});
}
return true;
} catch (error) {
return false;
}
}
async stopListening(sensorType) {
// Remove event listener
return true;
}
}
// 设备信息管理器
class DeviceInfoManager {
async getDeviceInfo() {
return {
manufacturer: navigator.vendor,
model: navigator.userAgent,
brand: 'Web',
device: 'Browser',
product: navigator.platform,
hardware: navigator.hardwareConcurrency,
fingerprint: '',
buildId: '',
buildVersion: navigator.appVersion,
sdkVersion: 0,
releaseVersion: navigator.appVersion
};
}
async getSystemInfo() {
return {
osName: navigator.platform,
osVersion: navigator.appVersion,
cpuCores: navigator.hardwareConcurrency,
totalMemory: performance.memory?.jsHeapSizeLimit || 0,
availableMemory: performance.memory?.jsHeapSizeLimit - performance.memory?.usedJSHeapSize || 0,
uptime: performance.now()
};
}
}
Web 端的实现示例展示了:
- 如何通过浏览器提供的
devicemotion事件模拟加速度计数据; - 使用
navigator、performance等对象获取设备与运行环境信息(如内核数、内存上限等); - 在没有真实硬件传感器的环境中,尽可能提供与 KMP 抽象兼容的数据结构,便于复用业务逻辑。
这对于构建 Web 调试面板、数据可视化后台等场景尤其有用,可以用同一套 KMP 代码来消费这些“伪传感器数据”。
第四部分:鸿蒙系统实际应用
鸿蒙传感器实现
// ohos/kotlin/com/example/sensor/SensorManager.kt
import ohos.sensor.agent.SensorManager as OhosSensorManager
import ohos.sensor.data.SensorEvent
import ohos.sensor.listener.SensorEventCallback
import ohos.app.Context
actual class SensorManager(private val context: Context) {
private val sensorManager = OhosSensorManager.getInstance()
private val listeners = mutableListOf<SensorListener>()
actual suspend fun startListening(sensorType: SensorType): Boolean {
return try {
val ohosType = when (sensorType) {
SensorType.ACCELEROMETER -> OhosSensorManager.SENSOR_TYPE_ACCELEROMETER
SensorType.GYROSCOPE -> OhosSensorManager.SENSOR_TYPE_GYROSCOPE
SensorType.MAGNETOMETER -> OhosSensorManager.SENSOR_TYPE_MAGNETIC_FIELD
else -> return false
}
sensorManager.createSensorEventListener(object : SensorEventCallback {
override fun onSensorEvent(sensorEvent: SensorEvent) {
val sensorData = SensorData(
sensorType = sensorType,
values = sensorEvent.values,
accuracy = sensorEvent.accuracy,
timestamp = sensorEvent.timestamp
)
listeners.forEach { it.onSensorDataChanged(sensorData) }
}
override fun onAccuracyChanged(sensorId: Int, accuracy: Int) {
listeners.forEach { it.onAccuracyChanged(sensorType, accuracy) }
}
}, ohosType)
true
} catch (e: Exception) {
false
}
}
}
// 鸿蒙设备信息
actual class DeviceInfoManager {
actual suspend fun getDeviceInfo(): DeviceInfo {
return DeviceInfo(
manufacturer = ohos.system.SystemProperties.get("ro.product.manufacturer", ""),
model = ohos.system.SystemProperties.get("ro.product.model", ""),
brand = ohos.system.SystemProperties.get("ro.product.brand", ""),
device = ohos.system.SystemProperties.get("ro.product.device", ""),
product = ohos.system.SystemProperties.get("ro.product.name", ""),
hardware = ohos.system.SystemProperties.get("ro.hardware", ""),
fingerprint = ohos.system.SystemProperties.get("ro.build.fingerprint", ""),
buildId = ohos.system.SystemProperties.get("ro.build.id", ""),
buildVersion = ohos.system.SystemProperties.get("ro.build.version.release", ""),
sdkVersion = ohos.system.SystemProperties.getInt("ro.build.version.sdk", 0),
releaseVersion = ohos.system.SystemProperties.get("ro.build.version.release", "")
)
}
}
第五部分:高级功能
传感器数据融合
// commonMain/kotlin/com/example/sensor/SensorFusion.kt
class SensorFusion(private val sensorManager: SensorManager) {
private val accelerometerData = FloatArray(3)
private val gyroscopeData = FloatArray(3)
private val magnetometerData = FloatArray(3)
suspend fun getOrientation(): FloatArray {
// Implement sensor fusion algorithm (e.g., Madgwick filter)
val orientation = FloatArray(3)
// Calculate orientation from sensor data
return orientation
}
suspend fun getDeviceMotion(): DeviceMotion {
return DeviceMotion(
acceleration = accelerometerData.copyOf(),
rotation = gyroscopeData.copyOf(),
magneticField = magnetometerData.copyOf()
)
}
}
data class DeviceMotion(
val acceleration: FloatArray,
val rotation: FloatArray,
val magneticField: FloatArray
)
// 设备状态监测
class DeviceStateMonitor(private val deviceInfoManager: DeviceInfoManager) {
private val listeners = mutableListOf<DeviceStateListener>()
suspend fun startMonitoring() {
// Periodically check device state
}
suspend fun stopMonitoring() {
// Stop monitoring
}
}
interface DeviceStateListener {
fun onBatteryLow()
fun onStorageLow()
fun onMemoryLow()
fun onTemperatureHigh()
}

总结
传感器与设备信息是现代应用获取设备状态和用户交互的重要方式。通过 KMP 框架,我们可以在共享代码中定义统一的传感器和设备信息接口,同时在各平台上利用原生 API 实现高效的数据采集。鸿蒙系统提供了完整的传感器 API,使得跨平台实现相对平顺。关键是要合理处理传感器精度、功耗和数据融合。
在工程实践中,你可以结合本篇思考:
- 当前业务实际需要哪些传感器,采样频率和精度要求如何?
- 是否已经有统一的“设备画像/运行环境”模块,方便埋点与调优?
- 在多端场景下,如何通过适配层最大程度复用与传感器相关的业务逻辑?
建议的实战路径:
- 先做一个简单 demo:展示实时加速度曲线 + 设备基础信息;
- 再引入传感器融合和设备状态监控,对比不同算法和策略对体验与功耗的影响;
- 最终将通用能力沉淀为工具模块,在多个业务中复用。
更多推荐



所有评论(0)