Android前台服务存储传感器数据时出现时间跳变问题排查
问题:传感器数据存储出现时间跳变现象
我正在开发一款应用,使用前台服务监控传感器数据并将其存储到Realm数据库中,每秒存储一次包含所有传感器值的对象。通过初始化SensorEventListener监听传感器事件,采用Channel架构以流的方式收发传感器数据。目前遇到的问题是,数据库存储中会出现时间跳变现象,尽管已关闭应用的电池优化,但问题仍存在,跳变间隔约15-20分钟,有时分布更随机。
相关代码片段
accelometerSensorEventListener = object : SensorEventListener { override fun onSensorChanged(event: SensorEvent?) { if (true) { for (i in 0 until event!!.values.size) { accelerometerValues[i] = event.values[i] Log.d("DATA", "accelerometer value : ${event.values[i]}") } //keep the time stamp for each event if (!acceleratorTimeStampSet) { acceleratorTimeStampSet = true initialAcceleratorTimeStamp = System.currentTimeMillis() } //set the timestamps ... allTimeStamps.add(dateTime.toString()) } var sensors = Sensors() var accelX = String.format("%.4f", event!!.values.get(0)).replace(",", ".") var accelY = String.format("%.4f", event!!.values.get(1)).replace(",", ".") var accelZ = String.format("%.4f", event!!.values.get(2)).replace(",", ".") sensors.accelerationXAxisValue = accelX sensors.accelerationYAxisValue = accelY sensors.accelerationZAxisValue = accelZ sensors.timeStamp = allTimeStampsAccel.get(allTimeStampsAccel.size -1) writeStringToFile("accel : $accelX\n") data.trySend(sensors) } fun collectSensorDataFromChannelAndStoreToRealmDB() { val scope = CoroutineScope(Dispatchers.Default) var sensorFecthingComplete : Boolean = false var sensors = Sensors() scope.launch { while (true) { var flowedSensors = data.receive() if (flowedSensors.accelerationXAxisValue != null) { sensors.accelerationXAxisValue = flowedSensors.accelerationXAxisValue } if (flowedSensors.accelerationYAxisValue != null) { sensors.accelerationYAxisValue = flowedSensors.accelerationYAxisValue } ... if ((sensors.accelerationXAxisValue != null) && (sensors.accelerationYAxisValue != null) && (sensors.accelerationZAxisValue != null) && (sensors.gyroscopeXAxis != null) && (sensors.gyroscopeYAxis != null) && (sensors.gyroscopeZAxis != null) && (sensors.linAcceXAxiValue != null) && (sensors.linAccelYAxisValue != null) && (sensors.linAccelZAxisValue != null) && (sensors.rotationXAxis != null) && (sensors.rotationYAxis != null) && (sensors.rotationZAxis != null) && (sensors.scalarRotation != null) && (sensors.pitch != null) && (sensors.roll != null) && (sensors.azimuth != null)) sensorFecthingComplete = true //store the sensor data to realm database if (sensorFecthingComplete) { realmDB = Realm.getDefaultInstance() val num : Number? = realmDB.where(Sensors::class.java).max("id") Log.d("STORE", "new ID num : ${num}") var nextID : Int = 0 nextID = if (num == null) { 1 } else { num.toInt() + 1 } if (nextID >= 2) { val currentSensor : Sensors = realmDB.where(Sensors::class.java).equalTo("id", nextID -1).findFirst()!! val currentTimeStamp = currentSensor.timeStamp var previousHour = LocalDateTime.parse(currentTimeStamp, DateTimeFormatter.ofPattern("yyyy-MM-dd hh:mm:ss a")) // var previousHour = SimpleDateFormat("YYYY-MM-d hh:mm:ss a").parse(currentTimeStamp) var currentHour = LocalDateTime.parse(sensors.timeStamp, DateTimeFormatter.ofPattern("yyyy-MM-dd hh:mm:ss a")) // var currentHour = SimpleDateFormat("YYYY-MM-d hh:mm:ss a").parse(sensors.timeStamp) if (currentHour.isAfter(previousHour)) { try { realmDB.executeTransaction { sensors.id = nextID if (realmDB.where(Sensors::class.java).equalTo("id", sensors.id).findFirst() == null) { Log.d("DATA", "sensors storing with id : ${sensors.id}") writeStringToFile("storing sensor id : " + sensors.id+"\n") realmDB.insert(sensors) } } } catch(e : Exception) { Log.d("EXCEPTION", e.message.toString()) } } } else { var dummySensors = Sensors() dummySensors.id = 1 var now = LocalDateTime.now() var currentTimeStamp = DateTimeFormatter.ofPattern("YYYY-MM-dd hh:mm:ss a") val currentDate = now.format(currentTimeStamp) // var time = SimpleDateFormat("YYYY-MM-d hh:mm:ss a").parse(Calendar.getInstance().time.toString()).toString() dummySensors.timeStamp = currentDate.toString() dummySensors.accelerationXAxisValue = null dummySensors.accelerationYAxisValue = null ... realmDB.executeTransaction { it.insert(dummySensors) } } sensorFecthingComplete = false sensors.timeStamp = null sensors.accelerationXAxisValue = null sensors.accelerationYAxisValue = null sensors.accelerationZAxisValue = null ... } } } }
时间跳变的可能原因
- 传感器采样率不稳定:
SensorEventListener的回调频率并非严格固定为每秒一次,系统会根据硬件负载、进程优先级动态调整传感器采样率。即使是前台服务,当系统资源紧张时,传感器回调可能被延迟,导致批量数据集中发送到Channel,最终存储时时间戳出现跳变。 - Channel数据积压与消费延迟:如果
collectSensorDataFromChannelAndStoreToRealmDB中的协程在处理/存储数据时耗时(比如Realm事务阻塞),Channel会积压传感器事件。当积压的数据被批量处理时,时间戳就会出现连续跳变。 - 时间戳生成逻辑不一致:当前代码中传感器数据的时间戳依赖
allTimeStampsAccel列表的最后一个元素,若传感器事件回调延迟,这个列表的时间戳可能不是实时生成,而是在回调触发时批量补充,导致时间戳不连续。另外,dummy数据使用LocalDateTime.now()生成时间戳,和传感器事件的时间戳来源不一致,也可能引入跳变。 - Realm事务的性能瓶颈:每次存储都调用
Realm.getDefaultInstance()获取实例,且事务中存在重复查询操作(equalTo("id", sensors.id)),如果事务执行缓慢,会导致后续数据处理延迟,进而让时间戳看起来跳变。 - 厂商系统深层限制:即使关闭了应用级电池优化,部分厂商的系统仍有全局后台限制策略(如待机模式、应用休眠),会在后台静默限制传感器采样或进程调度,导致数据采集中断后恢复时出现时间跳变。
内容的提问来源于stack exchange,提问作者Lampros Marantos
相关产品推荐
相关产品推荐

