You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.30 08:00:36