MAUI安卓端编程读取OTP短信 新消息到达触发LogEvent方案
Blazor MAUI 基于LifecycleEvents实现短信OTP自动读取落地方案
核心原理
之前BroadcastReceiver方案效果差,核心原因是Android 8.0+对静态注册的隐式广播做了严格限制,后台场景下大概率被系统拦截。本方案将短信广播的注册/反注册逻辑完全绑定到Android Activity生命周期,通过MAUI LifecycleEvents管理注册时机,避免广播被系统回收、内存泄漏等问题,收到短信时自动触发日志输出和OTP提取。
步骤1:配置基础权限
- 打开
Platforms/Android/AndroidManifest.xml,在<manifest>根节点下添加以下权限声明:
<uses-permission android:name="android.permission.RECEIVE_SMS" /> <uses-permission android:name="android.permission.READ_SMS" />
- Android 6.0+要求动态申请敏感权限,后续会在生命周期回调中加入权限申请逻辑,无需额外手动处理。
步骤2:定义OTP状态共享服务
Blazor页面和Android原生生命周期逻辑运行在不同上下文,需要一个单例服务传递收到的OTP值。在项目根目录新建SmsStateService.cs:
public class SmsStateService { // 收到新OTP时触发的事件 public event Action<string> OnOtpReceived; private string _currentOtp; public string CurrentOtp { get => _currentOtp; set { _currentOtp = value; if (!string.IsNullOrEmpty(value)) { OnOtpReceived?.Invoke(value); } } } }
在MauiProgram.cs的服务注册部分添加这个单例:
builder.Services.AddSingleton<SmsStateService>();
步骤3:修改LifecycleEvents配置,绑定广播注册逻辑
替换原来ConfigureLifecycleEvents块的全部代码,核心逻辑为:
- OnCreate时动态注册短信广播接收器
- OnStop时反注册接收器,避免内存泄漏
- OnStart时检查并申请短信运行时权限
- 接收器收到短信后自动匹配OTP,触发LogEvent并更新状态服务
完整代码如下:
.ConfigureLifecycleEvents(events => { // 定义广播接收器实例、广播过滤器变量,用于后续注册/反注册 SmsBroadcastReceiver _smsReceiver = null; IntentFilter _smsFilter = null; events.AddAndroid(android => android .OnActivityResult((activity, requestCode, resultCode, data) => LogEvent("OnActivityResult", requestCode.ToString())) .OnStart((activity) => { LogEvent("OnStart"); // 检查并申请短信权限 if (activity.CheckSelfPermission(Android.Manifest.Permission.ReceiveSms) != Android.Content.PM.Permission.Granted) { activity.RequestPermissions(new[] { Android.Manifest.Permission.ReceiveSms, Android.Manifest.Permission.ReadSms }, 1001); } }) .OnCreate((activity, bundle) => { LogEvent("OnCreate"); // 初始化广播接收器和过滤器 var smsService = activity.GetService<SmsStateService>(); _smsReceiver = new SmsBroadcastReceiver(smsService); _smsFilter = new IntentFilter(Android.Provider.Telephony.Sms.Intents.SmsReceivedAction); // Android 13+ 注册广播需要指定导出标志 if (Android.OS.Build.VERSION.SdkInt >= Android.OS.BuildVersionCodes.Tiramisu) { activity.RegisterReceiver(_smsReceiver, _smsFilter, Android.Content.ActivityFlags.ReceiverExported); } else { activity.RegisterReceiver(_smsReceiver, _smsFilter); } }) .OnStop((activity) => { LogEvent("OnStop"); // 反注册广播,避免内存泄漏 if (_smsReceiver != null) { activity.UnregisterReceiver(_smsReceiver); _smsReceiver = null; } })); // 自定义短信广播接收器 class SmsBroadcastReceiver : Android.Content.BroadcastReceiver { private readonly SmsStateService _smsStateService; // 匹配6位数字OTP的正则,可根据自身业务调整 private readonly System.Text.RegularExpressions.Regex _otpRegex = new System.Text.RegularExpressions.Regex(@"\b\d{6}\b"); public SmsBroadcastReceiver(SmsStateService smsStateService) { _smsStateService = smsStateService; } public override void OnReceive(Android.Content.Context context, Android.Content.Intent intent) { if (intent.Action != Android.Provider.Telephony.Sms.Intents.SmsReceivedAction) return; LogEvent("SmsReceived", "New message incoming"); var messages = Android.Provider.Telephony.Sms.Intents.GetMessagesFromIntent(intent); foreach (var msg in messages) { var messageBody = msg.MessageBody; LogEvent("SmsContent", messageBody); // 匹配OTP var match = _otpRegex.Match(messageBody); if (match.Success) { var otp = match.Value; LogEvent("OtpParsed", otp); _smsStateService.CurrentOtp = otp; } } } } static void LogEvent(string eventName, string type = null) { System.Diagnostics.Debug.WriteLine($"Lifecycle event: {eventName}{(type == null ? string.Empty : $" ({type})")}"); } });
注意:
activity.GetService<T>()是MAUI内置的扩展方法,需要确保代码引用了Microsoft.Maui.ApplicationModel命名空间,否则会报编译错误。
步骤4:Blazor页面中接收OTP并自动填充
在需要输入验证码的Blazor组件(比如登录页)中,注入SmsStateService,监听OTP接收事件,自动填充到输入框:
@inject SmsStateService SmsService @implements IDisposable <input type="text" @bind="otpCode" placeholder="请输入验证码" /> @code { private string otpCode; protected override void OnInitialized() { SmsService.OnOtpReceived += HandleOtpReceived; } private void HandleOtpReceived(string otp) { otpCode = otp; // 可选:收到OTP后自动触发提交逻辑 InvokeAsync(StateHasChanged); } public void Dispose() { SmsService.OnOtpReceived -= HandleOtpReceived; } }
常见问题说明
- 若收不到短信回调,优先检查是否授予了短信权限,部分国产ROM需要手动在系统设置中开启"短信读取"权限
- OTP匹配正则可根据自身短信模板调整,比如需要匹配4位验证码就将正则改为
\b\d{4}\b,也可以增加关键字校验(比如必须包含"验证码"字样)避免误提取其他短信的数字 - 该方案仅在应用处于前台/可见状态时生效,如果需要后台状态下也能读取短信,需要额外适配后台运行权限,不符合Google Play政策要求,不建议实现
内容的提问来源于stack exchange,提问作者rakshith
相关产品推荐
相关产品推荐

