讲师中心 微信公众号
AI工具推荐 视频效率加速

如何在 Android 中确保 SMS 仅发送一次(避免重复触发)

轻婷酱_5672

轻婷酱_5672

发布时间:2026-02-28 11:34:07

|

333人浏览过

|

来源于php中文网

原创

如何在 Android 中确保 SMS 仅发送一次(避免重复触发)

本文讲解如何通过状态标记机制防止 android 应用中 sms 被重复发送,重点解决因 handler 消息循环或 ui 逻辑缺陷导致的「短信持续发送」问题,并提供可直接复用的线程安全解决方案。

本文讲解如何通过状态标记机制防止 android 应用中 sms 被重复发送,重点解决因 handler 消息循环或 ui 逻辑缺陷导致的「短信持续发送」问题,并提供可直接复用的线程安全解决方案。

在 Android 开发中,SmsManager.sendTextMessage() 本身是无状态、无去重能力的纯发送接口。若调用逻辑嵌套在周期性回调(如 Handler 的 handleMessage)、定时器(Timer)、传感器监听或 Dialog 关闭事件中,极易因条件判断缺失或状态未持久化,造成短信被反复触发——正如问题中描述的“必须强制关闭 App 才能停止发送”。

根本原因在于:当前代码中 sendSMS() 被置于 handler 的 else 分支内,而该分支可能在 timer.cancel() 前被多次执行(例如 msg.arg1

Android Remote Control
Android Remote Control

远程控制 Android 设备,支持截图、自动化操作手机 App 等功能。本应用使用 uiautomator2 作为底层框架,通过 ADB 连接设备,用户无需 Root(1)查看手机屏幕;(2)自动化操作手机 App;(3)远程安装/卸载应用……

下载

✅ 正确解法:引入原子性发送标志位(flag),配合恰当的作用域与重置时机。

✅ 推荐实现方案(线程安全 & 易维护)

// 在 Activity / Fragment / Service 类成员变量区声明
private boolean hasSentSMS = false;
private final Object smsLock = new Object(); // 可选:提升多线程场景下的安全性

// 修改 sendSMS 方法(增强健壮性)
private void sendSMS(String address, String time) {
    // 防御性检查
    if (address == null || time == null || 
        sharedPreferences == null || 
        context == null) {
        Toast.makeText(context, "Missing required data", Toast.LENGTH_SHORT).show();
        return;
    }

    String name = sharedPreferences.getString("pre_key_name", "");
    String phoneNum = sharedPreferences.getString("pre_key_phone", "");

    // 空号/无效号码保护
    if (phoneNum == null || phoneNum.trim().isEmpty()) {
        Toast.makeText(context, "Phone number not configured", Toast.LENGTH_SHORT).show();
        return;
    }

    String smsContent = String.format("%s %s in %s falls!", time, name, address);
    SmsManager smsManager = SmsManager.getDefault();

    try {
        smsManager.sendTextMessage(phoneNum, null, smsContent, null, null);
        Toast.makeText(context, "Message sent successfully", Toast.LENGTH_SHORT).show();
    } catch (Exception e) {
        Toast.makeText(context, "SMS failed: " + e.getMessage(), Toast.LENGTH_LONG).show();
        Log.e("SMS", "Send failed", e);
    }
}

// 在 Handler 中安全调用
public Handler handler = new Handler(Looper.getMainLooper()) {
    @Override
    public void handleMessage(Message msg) {
        super.handleMessage(msg);
        if (msg.arg1 > 0) {
            countingView.setText("                         " + msg.arg1 + context.getString(R.string.fall_contacts));
        } else {
            if (dialog != null) {
                dialog.dismiss();
                if (isVibrate) stopVibrate();
                stopAlarm();

                // ? 关键:加锁 + 状态检查,确保仅执行一次
                synchronized (smsLock) {
                    if (!hasSentSMS) {
                        sendSMS(locationAddress, locationTime);
                        hasSentSMS = true;
                    }
                }
                return;
            }
            timer.cancel();
        }
    }
};

⚠️ 注意事项与最佳实践

  • 作用域一致性:hasSentSMS 必须声明为类成员变量(而非局部变量),否则每次进入 handleMessage 都会重置为 false;
  • 重置时机明确:若业务需要支持「多次跌倒报警」,应在合适时机(如用户手动重启监测、Activity 重建后、或新事件触发前)重置 hasSentSMS = false;
  • 权限与兼容性:Android 6.0+ 需动态申请 SEND_SMS 权限;Android 12(API 31)起,非默认短信应用需使用 RoleManager 申请短信角色,否则 sendTextMessage 将静默失败;
  • 替代方案建议:对关键告警类短信,推荐改用 PendingIntent + BroadcastReceiver 的异步结果回调(sentIntent/deliveryIntent),可精确感知发送成败,避免“假成功”;
  • 测试验证:可在 sendSMS() 开头添加 Log.d("SMS", "Sending... [" + System.currentTimeMillis() + "]");,结合 Logcat 观察是否真只打印一次。

✅ 总结

防止重复发送 SMS 的核心不是“阻止方法调用”,而是控制业务逻辑的执行频次。一个轻量、可靠、易理解的布尔标志位(配合必要同步)即可彻底解决该类问题。比起复杂的状态机或 RxJava 流控,此方案更符合中小型告警类 App 的工程实际,兼顾可读性与稳定性。

热门AI工具

更多
Lovart
Lovart Hot

一款面向视觉设计创作的AI设计平台,可通过智能体和画布工作流辅助制作海报、Logo、网页、PPT及其他视觉内容。

豆包大模型

豆包大模型是一款由字节跳动推出的企业级大语言模型服务平台。

PixTV
PixTV Hot

PixTV是一款面向AIGC内容创作的AI视频生成工具。

DeepSeek

DeepSeek是一款面向对话、写作、编程和推理场景的AI大模型工具。

Laper
Laper Hot

Laper是专为编剧、导演和制片人推出的 AI 原生剧本创作工具。

WorkBuddy

一款AI办公效率工具,主要用于腾讯云推出的AI原生桌面智能体工作台,适合需要提升相关任务效率的用户。

VibeKnow
VibeKnow Hot

一款AI视频创作工具,主要用于全球首个AI知识视频创作平台,文档、文章、网页,一键生成视频,适合需要提升相关任务效率的用户。

音述AI
音述AI Hot

一款AI音频处理工具,主要用于音述AI是一个以“用声音述说故事”为核心的 AI 音乐创作与声音分享社区,适合需要提升相关任务效率的用户。

咔片AIPPT

一款在线AI演示文稿制作工具,可根据主题和内容需求辅助生成PPT结构与页面,提高演示材料制作效率。

相关专题

更多
硬盘接口类型介绍
硬盘接口类型介绍

硬盘接口类型有IDE、SATA、SCSI、Fibre Channel、USB、eSATA、mSATA、PCIe等等。详细介绍:1、IDE接口是一种并行接口,主要用于连接硬盘和光驱等设备,它主要有两种类型:ATA和ATAPI,IDE接口已经逐渐被SATA接口;2、SATA接口是一种串行接口,相较于IDE接口,它具有更高的传输速度、更低的功耗和更小的体积;3、SCSI接口等等。

3188

2023.10.19

PHP接口编写教程
PHP接口编写教程

本专题整合了PHP接口编写教程,阅读专题下面的文章了解更多详细内容。

5049

2025.10.17

php8.4实现接口限流的教程
php8.4实现接口限流的教程

PHP8.4本身不内置限流功能,需借助Redis(令牌桶)或Swoole(漏桶)实现;文件锁因I/O瓶颈、无跨机共享、秒级精度等缺陷不适用高并发场景。本专题为大家提供相关的文章、下载、课程内容,供大家免费下载体验。

3829

2025.12.29

java接口相关教程
java接口相关教程

本专题整合了java接口相关内容,阅读专题下面的文章了解更多详细内容。

466

2026.01.19

线程和进程的区别
线程和进程的区别

线程和进程的区别:线程是进程的一部分,用于实现并发和并行操作,而线程共享进程的资源,通信更方便快捷,切换开销较小。本专题为大家提供线程和进程区别相关的各种文章、以及下载和课程。

4398

2023.08.10

android开发三大框架
android开发三大框架

android开发三大框架是XUtil框架、volley框架、ImageLoader框架。本专题为大家提供android开发三大框架相关的各种文章、以及下载和课程。

902

2023.08.14

android是什么系统
android是什么系统

Android是一种功能强大、灵活可定制、应用丰富、多任务处理能力强、兼容性好、网络连接能力强的操作系统。本专题为大家提供android相关的文章、下载、课程内容,供大家免费下载体验。

2707

2023.08.22

android权限限制怎么解开
android权限限制怎么解开

android权限限制可以使用Root权限、第三方权限管理应用程序、ADB命令和Xposed框架解开。详细介绍:1、Root权限,通过获取Root权限,用户可以解锁所有权限,并对系统进行自定义和修改;2、第三方权限管理应用程序,用户可以轻松地控制和管理应用程序的权限;3、ADB命令,用户可以在设备上执行各种操作,包括解锁权限;4、Xposed框架,用户可以在不修改系统文件的情况下修改应用程序的行为和权限。

5297

2023.09.19

Gradle插件开发入门教程
Gradle插件开发入门教程

本专题介绍Gradle插件开发的基本流程,涵盖插件类编写、自定义任务注册、扩展参数设计和插件应用方法,帮助你理解插件如何组织构建逻辑,减少多个项目之间的重复配置。

100

2026.10.22

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
Excel 教程
Excel 教程

共162课时 | 44.7万人学习

Java 教程
Java 教程

共578课时 | 193.9万人学习

Uniapp从零开始实现新闻资讯应用
Uniapp从零开始实现新闻资讯应用

共64课时 | 9.6万人学习

关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2026 https://www.php.cn/ All Rights Reserved | php.cn