如何让Xamarin应用在设备重启/关闭或应用关闭后持续运行任务?
跨平台定时任务解决方案(Xamarin Android + iOS)
核心思路
放弃纯.NET的System.Timers.Timer,分别调用Android和iOS的原生后台定时任务API,在共享项目中通过**依赖服务(Dependency Service)**封装统一接口,实现跨平台兼容。
Android 端实现(使用AlarmManager)
Android 推荐用AlarmManager实现系统级定时任务,即使应用被杀死或设备重启后也能触发,步骤如下:
- 创建广播接收器,用于接收定时触发的广播并执行语音播报逻辑:
[BroadcastReceiver(Enabled = true, Exported = false)] [IntentFilter(new[] { "com.yourapp.ACTION_SCHEDULE_TASK" })] public class ScheduleTaskReceiver : BroadcastReceiver { public override void OnReceive(Context context, Intent intent) { // 执行语音播报逻辑 var speechManager = DependencyService.Get<ISpeechService>(); speechManager.Speak("定时播报内容"); } }
- 创建依赖服务实现类,封装
AlarmManager调度逻辑,同时处理设备重启后的任务恢复:
[assembly: Dependency(typeof(AndroidScheduleService))] namespace YourApp.Droid.Services { public class AndroidScheduleService : IScheduleService { public void ScheduleTask(DateTime triggerTime) { var alarmManager = (AlarmManager)Android.App.Application.Context.GetSystemService(Context.AlarmService); var intent = new Intent("com.yourapp.ACTION_SCHEDULE_TASK"); var pendingIntent = PendingIntent.GetBroadcast(Android.App.Application.Context, 0, intent, PendingIntentFlags.UpdateCurrent | PendingIntentFlags.Immutable); // 转换为毫秒级时间戳 var triggerAtMillis = (long)(triggerTime - new DateTime(1970, 1, 1)).TotalMilliseconds; // 使用精确闹钟(Android 12+ 需要SCHEDULE_EXACT_ALARM权限) alarmManager.SetExactAndAllowWhileIdle(AlarmType.RtcWakeup, triggerAtMillis, pendingIntent); } public void RegisterBootReceiver() { // 注册开机广播接收器,重启后恢复任务 var filter = new IntentFilter(Intent.ActionBootCompleted); Android.App.Application.Context.RegisterReceiver(new BootCompletedReceiver(), filter); } } [BroadcastReceiver(Enabled = true, Exported = true)] [IntentFilter(new[] { Intent.ActionBootCompleted })] public class BootCompletedReceiver : BroadcastReceiver { public override void OnReceive(Context context, Intent intent) { // 从本地存储读取保存的定时任务,重新调度 var savedTasks = LoadSavedTasks(); foreach(var task in savedTasks) { var scheduleService = DependencyService.Get<IScheduleService>(); scheduleService.ScheduleTask(task.TriggerTime); } } } }
- 配置权限:在
AndroidManifest.xml中添加必要权限:
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" /> <uses-permission android:name="android.permission.SCHEDULE_EXACT_ALARM" />
注意:Android 12及以上版本需要动态申请
SCHEDULE_EXACT_ALARM权限,用户拒绝后只能使用非精确闹钟。
iOS 端实现(使用UNUserNotificationCenter + Background Tasks)
iOS 后台限制严格,需结合本地通知和后台任务实现,步骤如下:
- 创建依赖服务实现类,使用
UNUserNotificationCenter调度本地通知:
[assembly: Dependency(typeof(IosScheduleService))] namespace YourApp.iOS.Services { public class IosScheduleService : IScheduleService { public void ScheduleTask(DateTime triggerTime) { var content = new UNMutableNotificationContent(); content.Title = "定时播报"; content.Body = "即将开始语音播报"; content.Sound = UNNotificationSound.Default; // 添加自定义标识区分任务 content.UserInfo = new NSDictionary("taskId", "voice_schedule_task"); // 转换为NSDate var triggerDate = (NSDate)triggerTime; var trigger = UNCalendarNotificationTrigger.CreateTrigger( new NSDateComponents { Year = triggerDate.Year, Month = triggerDate.Month, Day = triggerDate.Day, Hour = triggerDate.Hour, Minute = triggerDate.Minute, Second = triggerDate.Second }, repeats: false); var request = UNNotificationRequest.FromIdentifier("voice_schedule_request", content, trigger); UNUserNotificationCenter.Current.AddNotificationRequest(request, (error) => { if (error != null) { // 处理调度错误 } }); } // 在AppDelegate中注册通知回调 public static void SetupNotificationHandler() { UNUserNotificationCenter.Current.Delegate = new NotificationDelegate(); } } public class NotificationDelegate : UNUserNotificationCenterDelegate { public override void DidReceiveNotificationResponse(UNUserNotificationCenter center, UNNotificationResponse response, Action completionHandler) { if (response.Notification.Request.Content.UserInfo["taskId"].ToString() == "voice_schedule_task") { // 执行语音播报逻辑 var speechManager = DependencyService.Get<ISpeechService>(); speechManager.Speak("定时播报内容"); } completionHandler(); } // 前台收到通知时执行播报 public override void WillPresentNotification(UNUserNotificationCenter center, UNNotification notification, Action<UNNotificationPresentationOptions> completionHandler) { if (notification.Request.Content.UserInfo["taskId"].ToString() == "voice_schedule_task") { var speechManager = DependencyService.Get<ISpeechService>(); speechManager.Speak("定时播报内容"); } completionHandler(UNNotificationPresentationOptions.Sound | UNNotificationPresentationOptions.Alert); } } }
- 配置后台权限:在
Info.plist中添加后台模式和通知权限:
<key>UIBackgroundModes</key> <array> <string>remote-notification</string> <string>audio</string> <!-- 需要后台播放音频时添加 --> </array> <key>NSUserNotificationUsageDescription</key> <string>需要使用通知触发定时语音播报</string>
注意:iOS 若应用被用户强制关闭,本地通知仍会触发,但播报可能受限于后台音频权限,需确保权限已申请。
共享项目封装统一接口
在共享项目中定义接口,通过依赖服务调用各平台实现:
public interface IScheduleService { void ScheduleTask(DateTime triggerTime); } // 调用示例 var scheduleService = DependencyService.Get<IScheduleService>(); scheduleService.ScheduleTask(DateTime.Now.AddMinutes(30));
同时需将定时任务信息保存到本地存储(如SQLite、Preferences),设备重启或应用重启后恢复任务。
内容的提问来源于stack exchange,提问作者Noé Sánchez
相关产品推荐
相关产品推荐

