Xamarin.Forms通过BroadcastReceiver从SQLite取数设置闹钟仅触发一次问题
问题根因及修复方案
1. 修复async void OnReceive的执行上下文问题
BroadcastReceiver的OnReceive默认执行在主线程,系统判定OnReceive方法返回后广播就处理完成。如果使用async void,await操作执行时方法会提前返回,系统可能会销毁广播对应的进程,导致await之后的设置下一次闹钟的代码无法执行。
修复方式:使用WakeLock持有锁,保证异步操作完成前进程不被系统回收
public override async void OnReceive(Context context, Intent intent) { // 最多持有1分钟唤醒锁,避免长时间持有导致耗电 var wakeLock = PowerManager.FromContext(context).NewWakeLock(WakeLockFlags.Partial, "SalahAlarm:WakeLock"); wakeLock.Acquire(TimeSpan.FromMinutes(1)); try { var sound = Android.Net.Uri.Parse(ContentResolver.SchemeAndroidResource + "://" + context.PackageName + "/" + Resource.Raw.azan); Ringtone r = RingtoneManager.GetRingtone(context, sound); r.Play(); await TextToSpeech.SpeakAsync("It's Time for salah"); // 此处替换为你从SQLite读取下一次闹钟时间的逻辑即可 Xamarin.Forms.DependencyService.Get<ILocalNotificationService>().LocalNotification("Local Notification", "sdfasdf", 0, System.DateTime.Now); } finally { wakeLock.Release(); } }
需要在AndroidManifest.xml中添加唤醒锁权限:
<uses-permission android:name="android.permission.WAKE_LOCK" />
2. 修复闹钟逻辑与时间计算错误
你当前实现存在两个明显逻辑问题:
- 每次用随机数作为PendingIntent的请求码,会导致后台堆积大量无效PendingIntent,甚至出现资源占用异常
ElapsedRealtimeWakeup是基于系统启动时间的闹钟类型,很容易和业务里的自然时间计算逻辑冲突,建议换成基于标准UTC时间的RtcWakeup类型,逻辑更清晰
修复后的LocalNotification代码如下:
public void LocalNotification(string title, string body, int id, DateTime notifyTime) { var intent = CreateIntent(id); // 用闹钟id作为固定请求码,方便后续更新/取消闹钟 var pendingIntent = PendingIntent.GetBroadcast(Application.Context, id, intent, PendingIntentFlags.Immutable | PendingIntentFlags.UpdateCurrent); var alarmManager = GetAlarmManager(); // 计算下一次闹钟的绝对时间戳 var nextSalahTime = new NextSalahTimeRemaining().getNextSalahTime(notifyTime).SalahTime; var utcOffset = nextSalahTime.ToUniversalTime() - new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc); var triggerAtMillis = (long)utcOffset.TotalMilliseconds; // 适配不同Android版本的闹钟接口,保证息屏也能正常触发 if (Build.VERSION.SdkInt >= BuildVersionCodes.M) { alarmManager.SetExactAndAllowWhileIdle(AlarmType.RtcWakeup, triggerAtMillis, pendingIntent); } else if (Build.VERSION.SdkInt >= BuildVersionCodes.Kitkat) { alarmManager.SetExact(AlarmType.RtcWakeup, triggerAtMillis, pendingIntent); } else { alarmManager.Set(AlarmType.RtcWakeup, triggerAtMillis, pendingIntent); } }
注意:Android 12(API 31)及以上版本需要在
AndroidManifest.xml中添加精确闹钟权限:<uses-permission android:name="android.permission.SCHEDULE_EXACT_ALARM" />
3. 确认BroadcastReceiver注册配置
如果使用静态注册BroadcastReceiver,要保证AndroidManifest.xml的配置没有错误限制,示例配置如下:
<receiver android:name=".你的Receiver类名" android:exported="true"> <intent-filter> <action android:name="你自定义的广播Action字符串" /> </intent-filter> </receiver>
静态注册的Receiver适配范围更广,不需要应用存活也能接收到广播触发回调。
内容的提问来源于stack exchange,提问作者peter john
相关产品推荐
相关产品推荐

