Handler.postDelayed失效:如何每隔X秒向数据库写入加速度计数据?
问题排查与解决方案
你的问题核心在于**onSensorChanged会被传感器频繁触发**(SENSOR_DELAY_NORMAL模式下大概每200ms就会调用一次),而你每次触发都调用handler.postDelayed,这会导致大量重复的延迟任务被添加到消息队列中,最终逻辑完全混乱,根本不是你预期的“每隔5秒执行一次”。另外,sensorEvent是传感器框架复用的对象,延迟后访问它可能拿到过期数据,这也是潜在的坑。
下面是修复后的完整代码,我会标注关键修改点:
public class MainActivity extends AppCompatActivity implements SensorEventListener{ private Button receiveButton; private ListView listView; private DatabaseReference database; private ArrayAdapter<String> adapter; private List<String> items; private SensorManager sensorManager; private Sensor senAccelerometer; private Handler handler = new Handler(); private final int delay = 5000; //in milliseconds private Accelerometer accelerometer; private float xValue; private float yValue; private float zValue; // 新增:标记是否已经启动了循环任务,避免重复添加 private boolean isTaskRunning = false; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); sensorManager = (SensorManager) getSystemService(SENSOR_SERVICE); senAccelerometer = sensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER); sensorManager.registerListener(this, senAccelerometer, SensorManager.SENSOR_DELAY_NORMAL); receiveButton = findViewById(R.id.receiveButton); listView = findViewById(R.id.listView); database = FirebaseDatabase.getInstance().getReference("Accelerometer"); items = new ArrayList<>(); items.add("X Value: " + Float.toString(xValue)); items.add("Y Value: " + Float.toString(yValue)); items.add("Z Value: " + Float.toString(zValue)); updateUI(); receiveButton.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { database.addValueEventListener(new ValueEventListener() { @Override public void onDataChange(DataSnapshot dataSnapshot) { Accelerometer accelerometer = dataSnapshot.getValue(Accelerometer.class); if(accelerometer != null){ // 增加空判断,避免空指针崩溃 xValue = accelerometer.xValue; yValue = accelerometer.yValue; zValue = accelerometer.zValue; } items.clear(); // 用clear替代迭代器删除,代码更简洁 items.add("X Value: " + Float.toString(xValue)); items.add("Y Value: " + Float.toString(yValue)); items.add("Z Value: " + Float.toString(zValue)); updateUI(); // 把updateUI移到这里,确保数据更新后立即刷新列表 } @Override public void onCancelled(DatabaseError databaseError) { // 建议在这里添加错误日志,方便排查Firebase相关问题 Log.e("FirebaseError", "数据库操作失败: " + databaseError.getMessage()); } }); } }); // 启动循环写入数据库的任务(如果需要一开始就执行,否则可以放在按钮点击事件里) startDatabaseWriteTask(); } private void updateUI(){ if(adapter == null){ adapter = new ArrayAdapter<String>(this, android.R.layout.simple_list_item_1, items); listView.setAdapter(adapter); } else{ adapter.notifyDataSetChanged(); } } @Override public void onSensorChanged(SensorEvent sensorEvent) { // 这里只做一件事:实时更新最新的加速度计数值,不处理任何延迟逻辑 if(sensorEvent.sensor.getType() == Sensor.TYPE_ACCELEROMETER) { xValue = sensorEvent.values[0]; yValue = sensorEvent.values[1]; zValue = sensorEvent.values[2]; } } @Override public void onAccuracyChanged(Sensor sensor, int i) { } // 新增:封装循环写入数据库的任务逻辑 private void startDatabaseWriteTask(){ if(isTaskRunning) return; // 防止重复启动多个任务 isTaskRunning = true; handler.postDelayed(new Runnable() { @Override public void run() { // 使用实时更新的x/y/z值创建对象 accelerometer = new Accelerometer(xValue, yValue, zValue); database.setValue(accelerometer); // 继续循环,实现每隔delay秒执行一次 handler.postDelayed(this, delay); } }, delay); } // 生命周期管理:避免后台继续运行任务,防止内存泄漏 @Override protected void onPause() { super.onPause(); handler.removeCallbacksAndMessages(null); // 移除所有Handler的待执行任务 isTaskRunning = false; } @Override protected void onResume() { super.onResume(); startDatabaseWriteTask(); // 恢复循环任务 } @Override protected void onDestroy() { super.onDestroy(); sensorManager.unregisterListener(this); // 注销传感器监听,避免内存泄漏 handler.removeCallbacksAndMessages(null); } }
关键修改说明:
- 分离核心逻辑:把传感器数据更新和数据库写入的逻辑彻底分开,
onSensorChanged只负责实时同步最新数值,不再触发延迟任务,从根源避免任务堆积。 - 单一循环任务:用
startDatabaseWriteTask方法启动唯一的循环任务,通过isTaskRunning标志位防止重复启动,确保只有一个定时任务在运行。 - 生命周期管控:在
onPause时移除所有Handler任务,onResume时恢复,onDestroy时注销传感器监听,避免后台无效运行和内存泄漏。 - 代码健壮性优化:增加Firebase数据空判断、错误日志,用更简洁的
items.clear()替代迭代器删除操作,提升代码稳定性和可读性。
这样修改后,就能稳定地每隔5秒将最新的加速度计数值写入数据库了。
内容的提问来源于stack exchange,提问作者Geordi Rugenbrink
相关产品推荐
相关产品推荐

