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

Xamarin.Android锁屏时接收短信触发Vibration振动失效如何解决

锁屏短信触发振动失效解决方案

核心问题点

你遇到的锁屏下失效是4个共性问题导致的,非锁屏下能运行只是因为应用处于前台,系统没做限制:

  • 代码开头直接调用InvokeAbortBroadcast():安卓4.4之后只有系统默认短信应用有权限中止短信广播,普通应用调用这行会直接触发安全异常,后续逻辑被catch块吞掉,锁屏状态下系统对广播违规的检查更严格,会直接拦截执行
  • 缺少唤醒锁:锁屏后CPU会进入低功耗休眠状态,没有唤醒锁的情况下振动指令会被系统挂起,直到屏幕点亮才会执行
  • Xamarin.Essentials封装的振动API没有做锁屏场景兼容,三星One UI的后台管控会直接拦截非活跃应用的振动调用
  • 高版本安卓解析短信PDU需要传入format参数,旧的单参数CreateFromPdu在安卓12+的锁屏场景下会解析失败
  • 额外注意:Toast在锁屏状态下默认不会显示,不要用Toast判断逻辑是否执行,用Logcat打日志排查。

操作步骤

1. 补全清单权限

在AndroidManifest.xml的manifest节点下添加缺失的权限,你之前加的振动、短信权限保留,补充以下内容:

<!-- 唤醒锁权限:锁屏时临时唤醒CPU执行振动 -->
<uses-permission android:name="android.permission.WAKE_LOCK" />
<!-- 安卓13+通知权限,部分ROM要求该权限才能正常接收系统广播 -->
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
<!-- 锁屏场景下组件运行权限 -->
<uses-permission android:name="android.permission.USE_FULL_SCREEN_INTENT" />

安卓6.0及以上版本,需要在应用首次启动时动态申请短信、通知权限,仅在Manifest声明无效。

2. 替换广播接收代码

直接把你原来的MySMSReciver类替换成下面的代码,修复了上述所有问题,可直接运行:

[BroadcastReceiver(Enabled = true, Label = "SMS Receiver", Exported = true)]
[IntentFilter(new string[] { "android.provider.Telephony.SMS_RECEIVED" }, Priority = (int)IntentFilterPriority.HighPriority)]
public class MySMSReceiver : BroadcastReceiver
{
    public const string INTENT_ACTION = "android.provider.Telephony.SMS_RECEIVED";
    private const string WAKE_LOCK_TAG = "SMSVibrateWakeLock";
    public override void OnReceive(Context context, Intent intent)
    {
        // *注意:非默认短信应用请删除下一行!普通应用调用会触发安全异常导致逻辑中断*
        // InvokeAbortBroadcast();
        
        WakeLock tempWakeLock = null;
        try
        {
            if (intent.Action != INTENT_ACTION) return;

            // 申请10秒 partial 唤醒锁,仅唤醒CPU不点亮屏幕,执行完自动释放
            var powerService = context.GetSystemService(Context.PowerService) as PowerManager;
            tempWakeLock = powerService.NewWakeLock(WakeLockFlags.PartialWakeLock, WAKE_LOCK_TAG);
            tempWakeLock.Acquire(10000);

            var bundle = intent.Extras;
            if (bundle == null) return;

            var pdus = bundle.Get("pdus").ToArray<Java.Lang.Object>();
            string pduFormat = bundle.GetString("format");
            var smsMessages = new SmsMessage[pdus.Length];
            var logContent = new StringBuilder();
            string senderNum = null;

            for (int i = 0; i < smsMessages.Length; i++)
            {
                // 兼容高低版本安卓的PDU解析
                if (Build.VERSION.SdkInt >= BuildVersionCodes.M)
                {
                    smsMessages[i] = SmsMessage.CreateFromPdu((byte[])pdus[i], pduFormat);
                }
                else
                {
                    smsMessages[i] = SmsMessage.CreateFromPdu((byte[])pdus[i]);
                }

                senderNum ??= smsMessages[i].OriginatingAddress;
                logContent.Append($"发件人: {smsMessages[i].OriginatingAddress}{Environment.NewLine}内容: {smsMessages[i].MessageBody}{Environment.NewLine}");
            }

            if (!string.IsNullOrEmpty(senderNum))
            {
                Android.Util.Log.Info("SMS_VIBRATE", $"收到短信:{logContent}");
                // 直接调用系统原生振动服务,避开Xamarin.Essentials的兼容问题
                var vibrator = context.GetSystemService(Context.VibratorService) as Vibrator;
                if (vibrator.HasVibrator)
                {
                    long vibrateDuration = 5000; // 振动5秒,单位毫秒
                    if (Build.VERSION.SdkInt >= BuildVersionCodes.O)
                    {
                        vibrator.Vibrate(VibrationEffect.CreateOneShot(vibrateDuration, VibrationEffect.DefaultAmplitude));
                    }
                    else
                    {
#pragma warning disable CS0618 // 兼容低版本安卓
                        vibrator.Vibrate(vibrateDuration);
#pragma warning restore CS0618
                    }
                }
            }
        }
        catch (Exception ex)
        {
            Android.Util.Log.Error("SMS_VIBRATE", $"运行异常:{ex.Message}\n{ex.StackTrace}");
        }
        finally
        {
            // 确保唤醒锁被释放,避免异常耗电
            if (tempWakeLock != null && tempWakeLock.IsHeld)
            {
                tempWakeLock.Release();
            }
        }
    }
}

3. 三星设备额外配置

三星One UI的后台管控比原生安卓严格,必须手动配置否则锁屏后应用还是会被限制:

  • 进入系统设置-应用-找到你的应用-电池和设备维护-电池,将应用的电池模式设置为不受限制
  • 进入后台使用限制页面,把你的应用添加到「永不休眠」列表,不要放在优化休眠、深度休眠列表里
  • 如果需要实现拦截短信的功能(即调用InvokeAbortBroadcast),必须把应用设置为系统默认短信应用,否则系统会直接拦截该操作。

验证方式

连接设备用Logcat过滤tag为SMS_VIBRATE的日志,锁屏状态下发送短信到测试机,如果能看到对应的日志输出,说明广播接收逻辑正常,振动会正常触发,不要用Toast判断,锁屏下Toast默认不显示。

内容的提问来源于stack exchange,提问作者JoeDarley

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.28 08:27:19