You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在Kotlin中用协程依次读取手机传感器数据并规整写入文件?

解决多传感器数据规整写入问题的Kotlin实现方案

核心问题分析

你当前的代码存在两个关键问题:

  1. 传感器触发速率不一致,每次触发就拼接字符串,导致数据顺序混乱、内容不完整
  2. 依赖光感触发写入,此时加速度/陀螺仪数据可能还未更新,造成每行数据缺失

下面提供两种基于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)
        }
    }
}

关键优势

  1. 保证每行数据包含完整的一组传感器信息,格式统一
  2. 协程在IO线程处理文件写入,不会阻塞主线程
  3. 两种方案可根据需求选择:定时检查适合固定频率采集,Flow合并适合实时响应式采集

内容的提问来源于stack exchange,提问作者kiselovv

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.30 06:00:25