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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.01 05:15:03