
本文详解如何通过状态标记机制避免 smsmanager.sendtextmessage() 在 handler 或定时逻辑中被重复调用,确保仅发送一次短信,并附带完整代码示例与关键注意事项。
本文详解如何通过状态标记机制避免 smsmanager.sendtextmessage() 在 handler 或定时逻辑中被重复调用,确保仅发送一次短信,并附带完整代码示例与关键注意事项。
在 Android 开发中,当使用 Handler 配合倒计时、传感器触发或对话框关闭等异步流程调用 sendSMS() 时,若缺乏发送状态控制,极易导致短信被反复发送——如用户未及时退出 Activity、Dialog 被多次 dismiss、或 handleMessage() 被意外重入,都可能使 sendSMS() 被连续执行,甚至引发系统短信限制或用户投诉。
根本原因在于:原代码中 sendSMS() 被直接置于 handler 的 handleMessage() 分支内(例如 dialog.dismiss() 后立即调用),且未做任何防重入校验。即使添加了 return,也无法阻止后续消息再次触发该逻辑。
✅ 正确解法是引入线程安全的发送状态标识。推荐使用 volatile boolean(适用于单线程 Handler 场景)或更稳妥的 AtomicBoolean,在类成员域声明,并在真正发送前校验+置位:
// 声明于 Activity / Fragment 类顶部(非方法内)
private volatile boolean hasSentSMS = false;
// 在 handler 的 handleMessage 中调用处修改为:
if (dialog != null) {
dialog.dismiss();
if (isVibrate) {
stopVibrate();
}
stopAlarm();
// ✅ 关键防护:仅当尚未发送时才执行
if (!hasSentSMS) {
sendSMS(locationAddress, locationTime);
hasSentSMS = true; // 置位,锁定后续调用
}
return;
}同时,建议对 sendSMS() 方法本身进行增强,增加空值与权限校验,提升健壮性:
private void sendSMS(String address, String time) {
// 1. 基础校验
if (address == null || time == null ||
TextUtils.isEmpty(sharedPreferences.getString("pre_key_phone", ""))) {
Toast.makeText(context, "缺少必要信息,无法发送短信", Toast.LENGTH_SHORT).show();
return;
}
// 2. 检查 SMS 权限(Android 6.0+ 运行时权限)
if (ContextCompat.checkSelfPermission(context, Manifest.permission.SEND_SMS)
!= PackageManager.PERMISSION_GRANTED) {
Toast.makeText(context, "未授予短信发送权限", Toast.LENGTH_SHORT).show();
return;
}
// 3. 发送逻辑
SmsManager smsManager = SmsManager.getDefault();
String name = sharedPreferences.getString("pre_key_name", "未知人员");
String phoneNum = sharedPreferences.getString("pre_key_phone", "");
String smsContent = String.format("%s %s在%s发生跌倒!", time, name, address);
try {
smsManager.sendTextMessage(phoneNum, null, smsContent, null, null);
Toast.makeText(context, "短信已发送", Toast.LENGTH_SHORT).show();
} catch (Exception e) {
Toast.makeText(context, "发送失败:" + e.getMessage(), Toast.LENGTH_SHORT).show();
Log.e("SMS", "Send failed", e);
}
}⚠️ 重要注意事项:
- hasSentSMS 是一次性标志,若业务需支持“多次触发、每次只发一次”,应在合适时机(如 Activity 重建、新检测事件开始前)重置为 false;
- 若涉及多线程(如后台 Service 触发),请改用 AtomicBoolean 并配合 compareAndSet(false, true) 保证原子性;
- Toast 仅作调试提示,生产环境建议结合 Notification 或日志埋点确认发送结果;
- 自 Android 10(API 29)起,非默认短信应用需申请 ROLE_SMS,否则 sendTextMessage() 将静默失败——务必在 AndroidManifest.xml 中声明并引导用户授权。
总结:防止重复发送短信的核心不是“加 return”,而是建立可验证、可维护的状态契约。通过布尔标记 + 权限/参数校验 + 异常捕获三重保障,即可稳定实现“一次触发、一次送达”的预期行为。










