如何在Kotlin中用协程依次读取手机传感器数据并规整写入文件?
解决多传感器数据规整写入问题的Kotlin实现方案
核心问题分析
你当前的代码存在两个关键问题:
- 传感器触发速率不一致,每次触发就拼接字符串,导致数据顺序混乱、内容不完整
- 依赖光感触发写入,此时加速度/陀螺仪数据可能还未更新,造成每行数据缺失
下面提供两种基于Kotlin协程的解决方案,确保所有传感器数据就绪后再统一写入文件。
方案一:定时检查+缓存最新数据
这种方式通过缓存每个传感器的最新值,定时检查所有数据是否齐全,齐全后统一写入。
1. 定义数据结构与缓存变量
// 封装一组完整的传感器数据 data class SensorData( val timestamp: String, val safetyStatus: String, val accelX: Double, val accelY: Double, val accelZ: Double, val gyroX: Double, val gyroY: Double, val gyroZ: Double, val lightLevel: Double ) // 缓存各传感器最新数据 private var latestAccel: Triple<Double, Double, Double>? = null private var latestGyro: Triple<Double, Double, Double>? = null private var latestLight: Double? = null private val timeFormatter = SimpleDateFormat("yyyy-MM-dd HH:mm:ss", Locale.getDefault()) private val coroutineScope = CoroutineScope(Dispatchers.IO + Job())
2. 修改传感器监听逻辑(只更新缓存,不拼接文本)
@SuppressLint("SetTextI18n") override fun onSensorChanged(event: SensorEvent?) { event ?: return when (event.sensor.type) { Sensor.TYPE_ACCELEROMETER -> { val x = event.values[0].toDouble() val y = event.values[1].toDouble() val z = event.values[2].toDouble() latestAccel = Triple(x, y, z) accelerometerTextView.text = "Accelerometer X: %.4f Y: %.4f Z: %.4f".format(x, y, z) } Sensor.TYPE_GYROSCOPE -> { val x = event.values[0].toDouble() val y = event.values[1].toDouble() val z = event.values[2].toDouble() latestGyro = Triple(x, y, z) gyroscopeTextView.text = "Gyroscope X: %.4f Y: %.4f Z: %.4f".format(x, y, z) } Sensor.TYPE_LIGHT -> { val level = event.values[0].toDouble() latestLight = level lightTextView.text = "Light Level: %.4f".format(level) } } }
3. 启动协程定时检查并写入数据
// 注册传感器后调用此方法启动数据收集 private fun startDataCollection() { coroutineScope.launch { while (isActive) { // 检查所有传感器数据是否齐全 val accel = latestAccel val gyro = latestGyro val light = latestLight if (accel != null && gyro != null && light != null) { val timestamp = timeFormatter.format(Date()) val safetyStatus = if (safetyType == 0) "Safe" else "Unsafe" val sensorData = SensorData( timestamp = timestamp, safetyStatus = safetyStatus, accelX = accel.first, accelY = accel.second, accelZ = accel.third, gyroX = gyro.first, gyroY = gyro.second, gyroZ = gyro.third, lightLevel = light ) writeDataToFile(sensorData) // 重置缓存,避免重复写入同一组数据 latestAccel = null latestGyro = null latestLight = null } delay(1000) // 每隔1秒检查一次,可根据需求调整频率 } } } // 统一写入文件逻辑 private fun writeDataToFile(data: SensorData) { val externalDir = getExternalFilesDir(null) ?: return val folder = File(externalDir, "AppData") if (!folder.exists()) folder.mkdirs() val file = File(folder, "AppData.txt") val line = buildString { append("${data.timestamp}, ") append("${data.safetyStatus}, ") append("Accel X: %.4f, Y: %.4f, Z: %.4f, ".format(data.accelX, data.accelY, data.accelZ)) append("Gyro X: %.4f, Y: %.4f, Z: %.4f, ".format(data.gyroX, data.gyroY, data.gyroZ)) append("Light Level: %.4f\n\n".format(data.lightLevel)) } file.appendText(line) }
4. 资源清理(避免内存泄漏)
override fun onDestroy() { super.onDestroy() sensorManager.unregisterListener(this) coroutineScope.cancel() // 取消协程任务 }
方案二:Flow响应式合并(更优雅的异步处理)
使用Kotlin Flow将每个传感器数据转为数据流,通过combine操作符等待三个数据流都有新数据时,自动触发写入。
1. 定义传感器数据流
private val accelFlow = callbackFlow<Triple<Double, Double, Double>> { val listener = object : SensorEventListener { override fun onSensorChanged(event: SensorEvent?) { event ?: return if (event.sensor.type == Sensor.TYPE_ACCELEROMETER) { trySend(Triple(event.values[0].toDouble(), event.values[1].toDouble(), event.values[2].toDouble())) } } override fun onAccuracyChanged(sensor: Sensor?, accuracy: Int) {} } sensorManager.registerListener(listener, accelSensor, readSpeed) awaitClose { sensorManager.unregisterListener(listener) } }.buffer(Channel.CONFLATED) // 只保留最新数据 private val gyroFlow = callbackFlow<Triple<Double, Double, Double>> { val listener = object : SensorEventListener { override fun onSensorChanged(event: SensorEvent?) { event ?: return if (event.sensor.type == Sensor.TYPE_GYROSCOPE) { trySend(Triple(event.values[0].toDouble(), event.values[1].toDouble(), event.values[2].toDouble())) } } override fun onAccuracyChanged(sensor: Sensor?, accuracy: Int) {} } sensorManager.registerListener(listener, gyroSensor, readSpeed) awaitClose { sensorManager.unregisterListener(listener) } }.buffer(Channel.CONFLATED) private val lightFlow = callbackFlow<Double> { val listener = object : SensorEventListener { override fun onSensorChanged(event: SensorEvent?) { event ?: return if (event.sensor.type == Sensor.TYPE_LIGHT) { trySend(event.values[0].toDouble()) } } override fun onAccuracyChanged(sensor: Sensor?, accuracy: Int) {} } sensorManager.registerListener(listener, lightSensor, readSpeed) awaitClose { sensorManager.unregisterListener(listener) } }.buffer(Channel.CONFLATED)
2. 合并数据流并写入数据
private fun startDataCollectionWithFlow() { coroutineScope.launch(Dispatchers.IO) { combine(accelFlow, gyroFlow, lightFlow) { accel, gyro, light -> Triple(accel, gyro, light) }.collect { (accel, gyro, light) -> val timestamp = timeFormatter.format(Date()) val safetyStatus = if (safetyType == 0) "Safe" else "Unsafe" val line = buildString { append("${timestamp}, ") append("${safetyStatus}, ") append("Accel X: %.4f, Y: %.4f, Z: %.4f, ".format(accel.first, accel.second, accel.third)) append("Gyro X: %.4f, Y: %.4f, Z: %.4f, ".format(gyro.first, gyro.second, gyro.third)) append("Light Level: %.4f\n\n".format(light)) } val externalDir = getExternalFilesDir(null) ?: return@collect val folder = File(externalDir, "AppData") if (!folder.exists()) folder.mkdirs() val file = File(folder, "AppData.txt") file.appendText(line) } } }
关键优势
- 保证每行数据包含完整的一组传感器信息,格式统一
- 协程在IO线程处理文件写入,不会阻塞主线程
- 两种方案可根据需求选择:定时检查适合固定频率采集,Flow合并适合实时响应式采集
内容的提问来源于stack exchange,提问作者kiselovv
相关产品推荐
相关产品推荐

