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
相关产品推荐
相关产品推荐

