27、结合Room数据库:将传感器数据持久化存储、历史数据查询
好,咱们终于聊到数据持久化了。
前面几章,我们一直在实时获取霍尔传感器的数值。但说实话,光看实时数据,很多时候是不够的。你想想看,如果你在做一款智能门锁,或者一个磁场监测设备,你肯定需要知道“过去一小时磁场变化趋势”,或者“昨天这个时候磁场强度是多少”。
这时候,就需要数据库登场了。
在Android里,我个人最常用的本地数据库方案就是Room。它是Google官方推出的ORM框架,说白了就是帮我们把SQLite的操作封装得更加优雅。你不用写一堆繁琐的SQL语句,用注解就能搞定。
为什么选Room?
我记得刚入行那会儿,大家都是直接写SQLiteOpenHelper,建表、升级、CRUD全得手写。那叫一个痛苦。后来Google推出了Room,我第一时间就迁移过去了。原因很简单:
- 编译时校验SQL:写错了直接编译报错,不会等到运行时才崩溃
- 与LiveData/Flow无缝集成:数据一变,UI自动更新
- 代码量减少一半以上:不用写那么多模板代码
霍尔传感器数据模型设计
先定义我们要存什么。霍尔传感器通常返回三个值:x、y、z轴的磁场强度(单位微特斯拉)。另外,我们肯定要记录时间戳。
@Entity(tableName = "hall_sensor_data")
public class HallSensorData {
@PrimaryKey(autoGenerate = true)
private long id;
private float xValue;
private float yValue;
private float zValue;
private long timestamp; // 毫秒级时间戳
// 构造函数、getter、setter 省略
// 实际开发中建议用 Kotlin data class,这里用Java示例
}
嗯,这里要注意:@PrimaryKey(autoGenerate = true) 会让Room自动生成自增ID。我个人习惯把ID设成long类型,因为int在数据量大时可能不够用。
DAO接口:数据访问层
DAO是Room的核心。你只需要定义接口,Room会自动生成实现类。
@Dao
public interface HallSensorDao {
@Insert
void insert(HallSensorData data);
@Query("SELECT * FROM hall_sensor_data ORDER BY timestamp DESC")
LiveData<List<HallSensorData>> getAllData();
@Query("SELECT * FROM hall_sensor_data WHERE timestamp BETWEEN :startTime AND :endTime ORDER BY timestamp ASC")
LiveData<List<HallSensorData>> getDataBetween(long startTime, long endTime);
@Query("DELETE FROM hall_sensor_data WHERE timestamp < :cutoffTime")
void deleteOldData(long cutoffTime);
}
看到没?查询条件直接用SQL写,但Room会在编译时检查语法。我曾经在项目里把表名写错了,编译直接报红,省了我半小时调试时间。
Database类:数据库的入口
定义一个抽象类,继承RoomDatabase。这里要指定实体类和版本号。
@Database(entities = {HallSensorData.class}, version = 1, exportSchema = false)
public abstract class AppDatabase extends RoomDatabase {
public abstract HallSensorDao hallSensorDao();
private static volatile AppDatabase INSTANCE;
public static AppDatabase getInstance(Context context) {
if (INSTANCE == null) {
synchronized (AppDatabase.class) {
if (INSTANCE == null) {
INSTANCE = Room.databaseBuilder(
context.getApplicationContext(),
AppDatabase.class,
"hall_sensor_db"
).build();
}
}
}
return INSTANCE;
}
}
这里用了单例模式。为什么?因为创建数据库实例开销不小,重复创建会浪费资源。我建议所有Room数据库都用单例。
在传感器回调中存储数据
现在,我们把实时数据存进去。注意,数据库操作不能在主线程执行,Room默认也不允许。所以我们要用异步方式。
// 在SensorEventListener的onSensorChanged中
new Thread(() -> {
HallSensorData data = new HallSensorData();
data.setXValue(event.values[0]);
data.setYValue(event.values[1]);
data.setZValue(event.values[2]);
data.setTimestamp(System.currentTimeMillis());
AppDatabase.getInstance(context).hallSensorDao().insert(data);
}).start();
当然,实际项目中我建议用线程池或者协程。这里为了演示,用了最简单的new Thread。
历史数据查询与展示
查询历史数据,用LiveData监听最方便。数据一变,UI自动刷新。
// 在Activity或Fragment中
AppDatabase.getInstance(this).hallSensorDao()
.getDataBetween(startOfDay, endOfDay)
.observe(this, dataList -> {
// 更新图表或列表
updateChart(dataList);
});
这里我用了observe(this, ...),生命周期感知,不会内存泄漏。Room + LiveData 简直是绝配。
数据清理:别让数据库无限膨胀
霍尔传感器数据如果一直存,手机存储迟早会爆。我建议定期清理旧数据。
// 删除7天前的数据
long cutoff = System.currentTimeMillis() - 7 * 24 * 60 * 60 * 1000L;
AppDatabase.getInstance(this).hallSensorDao().deleteOldData(cutoff);
可以在应用启动时执行一次,或者用WorkManager定时清理。
知识体系图
下面这张图,帮你理清Room与霍尔传感器数据持久化的整体流程:
总结一下
Room数据库让霍尔传感器数据的持久化变得非常轻松。你只需要定义好Entity、DAO和Database,剩下的交给Room处理。配合LiveData,UI能自动响应数据变化。
我个人建议:一定要做批量插入和定期清理。这是我在多个项目里踩过坑之后总结出来的经验。否则,数据库写爆、查询卡顿,迟早找上门。
核心要点回顾:
- Entity定义数据模型,注意时间戳字段
- DAO用注解写SQL,编译时校验
- Database用单例模式,避免重复创建
- 批量插入提升性能,定期清理防止膨胀
- LiveData + Room 实现数据驱动UI
好了,这一章就到这里。代码不多,但都是实战中提炼出来的。你拿去用,基本不会出大问题。
公众号:蓝海资料掘金营,微信deep3321