26、结合Notification:当磁场强度超过阈值时发送通知提醒
好,咱们继续往下走。
前面几章我们一直在讲怎么读霍尔传感器的数据,怎么算磁场强度。但说实话,光在Logcat里看数字,那只是给自己看的。真正做产品,你得让用户知道——比如手机壳合上了、磁吸支架吸住了、或者某个磁场异常了。
这时候就需要通知(Notification)登场了。
我个人习惯把这种场景叫做「阈值触发+用户提醒」。说白了就是:后台一直盯着磁场数据,一旦超过你设定的门槛,就弹个通知出来。嗯,这里要注意,不是每次超过都弹,得加防抖逻辑,不然用户手机会被通知刷爆。
26.1 核心思路:监听 + 判断 + 通知
整个流程其实就三步:
- 监听霍尔传感器,拿到实时磁场值
- 判断是否超过阈值,并且满足防抖条件
- 发送系统通知,提醒用户
我在项目中遇到过一种情况:测试时发现通知总是重复弹出。后来排查发现,是因为传感器更新频率太高,每次变化都触发了判断。所以后来我加了个「上次通知时间」的标记,至少间隔3秒才允许再次弹通知。
关键点:通知的触发条件一定要加防抖。否则用户会骂你。
26.2 代码实现:从传感器到通知的完整链路
下面这段代码,我直接贴一个完整的Service实现。你可以在后台跑这个Service,它负责监听霍尔传感器,并在磁场强度超过阈值时弹出通知。
public class HallSensorService extends Service implements SensorEventListener {
private SensorManager sensorManager;
private Sensor hallSensor;
private static final float THRESHOLD = 50.0f; // 阈值,单位微特斯拉
private long lastNotifyTime = 0;
private static final long NOTIFY_INTERVAL = 3000; // 防抖间隔3秒
@Override
public void onCreate() {
super.onCreate();
sensorManager = (SensorManager) getSystemService(SENSOR_SERVICE);
if (sensorManager != null) {
hallSensor = sensorManager.getDefaultSensor(Sensor.TYPE_MAGNETIC_FIELD);
}
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
if (hallSensor != null) {
sensorManager.registerListener(this, hallSensor, SensorManager.SENSOR_DELAY_NORMAL);
}
return START_STICKY;
}
@Override
public void onSensorChanged(SensorEvent event) {
if (event.sensor.getType() == Sensor.TYPE_MAGNETIC_FIELD) {
float x = event.values[0];
float y = event.values[1];
float z = event.values[2];
float magnitude = (float) Math.sqrt(x * x + y * y + z * z);
// 判断是否超过阈值
if (magnitude > THRESHOLD) {
long now = System.currentTimeMillis();
if (now - lastNotifyTime > NOTIFY_INTERVAL) {
lastNotifyTime = now;
sendNotification("磁场强度超标", "当前值:" + String.format("%.1f", magnitude) + " μT");
}
}
}
}
private void sendNotification(String title, String content) {
NotificationManager manager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
NotificationChannel channel = new NotificationChannel(
"hall_sensor_channel",
"霍尔传感器提醒",
NotificationManager.IMPORTANCE_HIGH
);
manager.createNotificationChannel(channel);
}
Notification notification = new NotificationCompat.Builder(this, "hall_sensor_channel")
.setSmallIcon(android.R.drawable.ic_dialog_alert)
.setContentTitle(title)
.setContentText(content)
.setAutoCancel(true)
.setPriority(NotificationCompat.PRIORITY_HIGH)
.build();
manager.notify(1001, notification);
}
@Override
public void onAccuracyChanged(Sensor sensor, int accuracy) { }
@Override
public void onDestroy() {
super.onDestroy();
if (sensorManager != null) {
sensorManager.unregisterListener(this);
}
}
@Nullable
@Override
public IBinder onBind(Intent intent) {
return null;
}
}
小提示:别忘了在AndroidManifest.xml里声明Service,并且如果是Android 8.0以上,启动Service要用startForegroundService(),否则会被系统杀掉。
26.3 阈值怎么定?
你可能会问:阈值设多少合适?
嗯,这个问题其实没有标准答案。我做过几个项目,总结了一些经验值:
| 场景 | 建议阈值(μT) | 说明 |
|---|---|---|
| 普通环境磁场 | 30 ~ 60 | 日常环境,比如靠近音箱、显示器 |
| 磁吸支架/磁吸配件 | 100 ~ 200 | 磁铁靠近时,磁场会明显升高 |
| 强磁铁靠近 | 300 以上 | 比如钕磁铁,或者磁吸充电座 |
我个人习惯是先跑一遍日志,看看正常环境下的磁场基线是多少。比如你办公室可能平均40μT,那阈值设在80μT就比较合理。别拍脑袋定个1000,那可能永远触发不了。
26.4 避坑指南:我曾经踩过的坑
这里分享几个我实际遇到过的问题:
- 通知不显示? 检查一下通知渠道(NotificationChannel)是否创建了。Android 8.0以后,没有渠道的通知是发不出来的。
- 通知重复弹出? 就是前面说的防抖问题。加个时间戳判断,别每次传感器变化都弹。
- Service被系统杀掉? 尤其是国产手机,后台限制很严。建议用前台Service,或者绑定一个通知栏常驻通知。
- 传感器数据跳变? 霍尔传感器偶尔会有毛刺。我一般会加一个简单的滑动平均滤波,取最近3~5个值的平均再做判断。
注意:不要在主线程里做耗时操作。onSensorChanged是跑在传感器线程的,但通知发送本身很快,所以没问题。如果你要写文件或者网络请求,记得切线程。
26.5 知识体系梳理
下面这张图,我把本章的核心逻辑画出来了。你看一眼就明白整个流程了。
你看,整个流程其实不复杂。关键就两个地方:阈值设得合理,防抖做得到位。这两点做好了,通知功能基本就稳了。
扩展思路:你还可以把通知改成震动、响铃,或者结合前台Service做一个常驻通知栏显示实时磁场值。这些我在后面的章节会提到,但你现在就可以自己试试。
公众号:蓝海资料掘金营,微信deep3321