如何将Xamarin.Forms函数改为后台运行(支持重启后执行)
解决方案实现
1. 定义任务模型与本地存储(SQLite)
先安装NuGet包:sqlite-net-pcl
任务实体类
using SQLite; namespace YourAppName.Models { [Table("ScheduledTasks")] public class ScheduledTask { [PrimaryKey, AutoIncrement] public int Id { get; set; } public string TaskName { get; set; } public DateTime ScheduleTime { get; set; } public string Message { get; set; } // 要播报的内容 public bool IsActive { get; set; } = true; } }
任务数据访问层(CRUD)
using SQLite; using System.Collections.Generic; using System.Threading.Tasks; using YourAppName.Models; namespace YourAppName.Data { public class TaskDatabase { readonly SQLiteAsyncConnection _database; public TaskDatabase(string dbPath) { _database = new SQLiteAsyncConnection(dbPath); _database.CreateTableAsync<ScheduledTask>().Wait(); } // 获取所有活跃任务 public Task<List<ScheduledTask>> GetTasksAsync() { return _database.Table<ScheduledTask>().Where(t => t.IsActive).ToListAsync(); } // 获取单个任务 public Task<ScheduledTask> GetTaskAsync(int id) { return _database.Table<ScheduledTask>().Where(t => t.Id == id).FirstOrDefaultAsync(); } // 添加/更新任务 public Task<int> SaveTaskAsync(ScheduledTask task) { if (task.Id != 0) { return _database.UpdateAsync(task); } else { return _database.InsertAsync(task); } } // 软删除任务(标记为非活跃) public Task<int> DeleteTaskAsync(ScheduledTask task) { task.IsActive = false; return _database.UpdateAsync(task); // 物理删除用:return _database.DeleteAsync(task); } } }
初始化数据库(App.xaml.cs)
using YourAppName.Data; using Xamarin.Forms; using System.IO; namespace YourAppName { public partial class App : Application { static TaskDatabase _database; public static TaskDatabase Database { get { if (_database == null) { _database = new TaskDatabase(Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), "Tasks.db3")); } return _database; } } public App() { InitializeComponent(); MainPage = new NavigationPage(new Views.TaskListPage()); } protected override async void OnStart() { // 启动时重新注册所有活跃任务 var tasks = await App.Database.GetTasksAsync(); foreach (var task in tasks) { DependencyService.Get<Services.IScheduledTaskService>().ScheduleTask(task); } } } }
2. 定义跨平台定时任务服务接口
using YourAppName.Models; namespace YourAppName.Services { public interface IScheduledTaskService { // 调度任务 void ScheduleTask(ScheduledTask task); // 取消任务 void CancelTask(int taskId); // 重启后重新注册所有任务 void ReRegisterAllTasks(); } }
3. Android平台实现
权限配置(AndroidManifest.xml)
添加以下权限与组件注册:
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" /> <uses-permission android:name="android.permission.WAKE_LOCK" /> <uses-permission android:name="android.permission.FOREGROUND_SERVICE" /> <receiver android:name=".BootCompletedReceiver" android:enabled="true" android:exported="true"> <intent-filter> <action android:name="android.intent.action.BOOT_COMPLETED" /> <action android:name="android.intent.action.QUICKBOOT_POWERON" /> </intent-filter> </receiver> <service android:name=".TaskExecutionService" android:foregroundServiceType="mediaPlayback" /> <receiver android:name=".TaskAlarmReceiver" />
BootCompletedReceiver(重启后重新注册任务)
using Android.Content; using YourAppName.Services; namespace YourAppName.Droid { [BroadcastReceiver(Enabled = true, Exported = true)] [IntentFilter(new[] { Intent.ActionBootCompleted, "android.intent.action.QUICKBOOT_POWERON" })] public class BootCompletedReceiver : BroadcastReceiver { public override async void OnReceive(Context context, Intent intent) { if (intent.Action == Intent.ActionBootCompleted) { var taskService = DependencyService.Get<IScheduledTaskService>(); taskService.ReRegisterAllTasks(); } } } }
TaskAlarmReceiver(接收闹钟触发)
using Android.Content; namespace YourAppName.Droid { [BroadcastReceiver(Exported = true)] public class TaskAlarmReceiver : BroadcastReceiver { public override void OnReceive(Context context, Intent intent) { var message = intent.GetStringExtra("Message"); var taskId = intent.GetIntExtra("TaskId", 0); // 启动前台服务执行语音播报 var serviceIntent = new Intent(context, typeof(TaskExecutionService)); serviceIntent.PutExtra("Message", message); serviceIntent.PutExtra("TaskId", taskId); if (Android.OS.Build.VERSION.SdkInt >= Android.OS.BuildVersionCodes.O) { context.StartForegroundService(serviceIntent); } else { context.StartService(serviceIntent); } } } }
TaskExecutionService(前台服务执行语音播报)
using Android.App; using Android.Content; using Android.Media; using Android.OS; using AndroidX.Core.App; using YourAppName.Data; using YourAppName.Models; namespace YourAppName.Droid { [Service(ForegroundServiceType = Android.Content.PM.ForegroundService.TypeMediaPlayback)] public class TaskExecutionService : Service { private int _notificationId = 1001; public override IBinder OnBind(Intent intent) { return null; } public override async StartCommandResult OnStartCommand(Intent intent, StartCommandFlags flags, int startId) { var message = intent.GetStringExtra("Message"); var taskId = intent.GetIntExtra("TaskId", 0); // 创建前台通知(Android 8+必须) var notificationBuilder = new NotificationCompat.Builder(this, "TaskChannel") .SetContentTitle("定时播报") .SetContentText(message) .SetSmallIcon(Resource.Drawable.ic_notification) .SetPriority(NotificationCompat.PriorityDefault); // 创建通知渠道(Android 8+) if (Build.VERSION.SdkInt >= BuildVersionCodes.O) { var channel = new NotificationChannel("TaskChannel", "定时任务频道", NotificationImportance.Default); channel.Description = "用于定时语音播报的通知渠道"; var notificationManager = GetSystemService(NotificationService) as NotificationManager; notificationManager.CreateNotificationChannel(channel); } StartForeground(_notificationId, notificationBuilder.Build()); // 执行语音播报 var tts = new TextToSpeech(this, status => { if (status == OperationResult.Success) { tts.Speak(message, QueueMode.Flush, null, null); } }); // 播报完成后标记任务为非活跃 var task = await App.Database.GetTaskAsync(taskId); if (task != null) { task.IsActive = false; await App.Database.SaveTaskAsync(task); } StopForeground(true); StopSelf(); return StartCommandResult.NotSticky; } } }
IScheduledTaskService的Android实现
using Android.App; using Android.Content; using Android.OS; using YourAppName.Data; using YourAppName.Models; using YourAppName.Services; namespace YourAppName.Droid.Services { public class ScheduledTaskService : IScheduledTaskService { public void ScheduleTask(ScheduledTask task) { var alarmManager = (AlarmManager)Android.App.Application.Context.GetSystemService(Context.AlarmService); var intent = new Intent(Android.App.Application.Context, typeof(TaskAlarmReceiver)); intent.PutExtra("TaskId", task.Id); intent.PutExtra("Message", task.Message); var pendingIntent = PendingIntent.GetBroadcast( Android.App.Application.Context, task.Id, intent, PendingIntentFlags.UpdateCurrent | PendingIntentFlags.Immutable); // 计算触发时间的毫秒数 var triggerTime = (long)(task.ScheduleTime - DateTime.Now).TotalMilliseconds; if (triggerTime <= 0) triggerTime = 1000; // 防止立即触发 if (Build.VERSION.SdkInt >= BuildVersionCodes.M) { alarmManager.SetExactAndAllowWhileIdle(AlarmType.RtcWakeup, SystemClock.ElapsedRealtime() + triggerTime, pendingIntent); } else if (Build.VERSION.SdkInt >= BuildVersionCodes.Kitkat) { alarmManager.SetExact(AlarmType.RtcWakeup, SystemClock.ElapsedRealtime() + triggerTime, pendingIntent); } else { alarmManager.Set(AlarmType.RtcWakeup, SystemClock.ElapsedRealtime() + triggerTime, pendingIntent); } } public void CancelTask(int taskId) { var alarmManager = (AlarmManager)Android.App.Application.Context.GetSystemService(Context.AlarmService); var intent = new Intent(Android.App.Application.Context, typeof(TaskAlarmReceiver)); var pendingIntent = PendingIntent.GetBroadcast( Android.App.Application.Context, taskId, intent, PendingIntentFlags.UpdateCurrent | PendingIntentFlags.Immutable); alarmManager.Cancel(pendingIntent); pendingIntent.Cancel(); } public async void ReRegisterAllTasks() { var tasks = await App.Database.GetTasksAsync(); foreach (var task in tasks) { ScheduleTask(task); } } } }
4. iOS平台实现
Info.plist配置
添加后台模式与权限:
<key>UIBackgroundModes</key> <array> <string>audio</string> </array> <key>NSSpeechRecognitionUsageDescription</key> <string>需要使用语音播报功能</string>
IScheduledTaskService的iOS实现
using Foundation; using UserNotifications; using AVFoundation; using YourAppName.Data; using YourAppName.Models; using YourAppName.Services; namespace YourAppName.iOS.Services { public class ScheduledTaskService : IScheduledTaskService, IUNUserNotificationCenterDelegate { public ScheduledTaskService() { UNUserNotificationCenter.Current.Delegate = this; RequestNotificationPermissions(); } private void RequestNotificationPermissions() { UNUserNotificationCenter.Current.RequestAuthorization(UNAuthorizationOptions.Alert | UNAuthorizationOptions.Sound, (granted, error) => { if (granted) { UIKit.UIApplication.SharedApplication.RegisterForRemoteNotifications(); } }); } public void ScheduleTask(ScheduledTask task) { var content = new UNMutableNotificationContent(); content.Title = "定时播报"; content.Body = task.Message; content.Sound = UNNotificationSound.Default; // 计算触发时间(转UTC) var triggerDate = new DateTimeOffset(task.ScheduleTime).ToUniversalTime(); 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(task.Id.ToString(), content, trigger); UNUserNotificationCenter.Current.AddNotificationRequest(request, (error) => { // 可在此处理错误 }); } public void CancelTask(int taskId) { UNUserNotificationCenter.Current.RemovePendingNotificationRequests(new string[] { taskId.ToString() }); } public async void ReRegisterAllTasks() { var tasks = await App.Database.GetTasksAsync(); foreach (var task in tasks) { ScheduleTask(task); } } // 前台收到通知时触发播报 [Export("userNotificationCenter:willPresentNotification:withCompletionHandler:")] public void WillPresentNotification(UNUserNotificationCenter center, UNNotification notification, Action<UNNotificationPresentationOptions> completionHandler) { SpeakMessage(notification.Request.Content.Body); completionHandler(UNNotificationPresentationOptions.Alert | UNNotificationPresentationOptions.Sound); } // 用户点击通知时触发播报 [Export("userNotificationCenter:didReceiveNotificationResponse:withCompletionHandler:")] public void DidReceiveNotificationResponse(UNUserNotificationCenter center, UNNotificationResponse response, Action completionHandler) { SpeakMessage(response.Notification.Request.Content.Body); completionHandler(); } private void SpeakMessage(string message) { var synthesizer = new AVSpeechSynthesizer(); var utterance = new AVSpeechUtterance(message) { Voice = AVSpeechSynthesisVoice.FromLanguage("zh-CN"), Rate = 0.5f }; synthesizer.SpeakUtterance(utterance); } } }
iOS启动注册(AppDelegate.cs)
using Foundation; using UIKit; using YourAppName.Services; namespace YourAppName.iOS { [Register("AppDelegate")] public partial class AppDelegate : global::Xamarin.Forms.Platform.iOS.FormsApplicationDelegate { public override bool FinishedLaunching(UIApplication app, NSDictionary options) { global::Xamarin.Forms.Forms.Init(); LoadApplication(new App()); // 注册依赖服务 DependencyService.Register<IScheduledTaskService, iOS.Services.ScheduledTaskService>(); // 重启后重新注册任务 DependencyService.Get<IScheduledTaskService>().ReRegisterAllTasks(); return base.FinishedLaunching(app, options); } } }
5. 视图层实现
TaskListPage.xaml
<?xml version="1.0" encoding="utf-8" ?> <ContentPage xmlns="http://xamarin.com/schemas/2014/forms" xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" x:Class="YourAppName.Views.TaskListPage" Title="定时任务列表"> <ContentPage.ToolbarItems> <ToolbarItem Text="添加" Clicked="AddTask_Clicked"/> </ContentPage.ToolbarItems> <StackLayout> <ListView x:Name="TasksListView" Margin="10"> <ListView.ItemTemplate> <DataTemplate> <ViewCell> <StackLayout Orientation="Horizontal" Padding="10"> <StackLayout> <Label Text="{Binding TaskName}" FontSize="Medium" FontAttributes="Bold"/> <Label Text="{Binding ScheduleTime, StringFormat='执行时间: {0:yyyy-MM-dd HH:mm:ss}'}" FontSize="Small"/> <Label Text="{Binding Message}" FontSize="Small" TextColor="Gray"/> </StackLayout> <Button Text="删除" Clicked="DeleteTask_Clicked" CommandParameter="{Binding Id}" Margin="10,0"/> </StackLayout> </ViewCell> </DataTemplate> </ListView.ItemTemplate> </ListView> </StackLayout> </ContentPage>
TaskListPage.xaml.cs
using YourAppName.Models; using YourAppName.Services; using Xamarin.Forms; namespace YourAppName.Views { public partial class TaskListPage : ContentPage { public TaskListPage() { InitializeComponent(); } protected override async void OnAppearing() { base.OnAppearing(); var tasks = await App.Database.GetTasksAsync(); TasksListView.ItemsSource = tasks; } private async void AddTask_Clicked(object sender, EventArgs e) { await Navigation.PushAsync(new AddTaskPage()); } private async void DeleteTask_Clicked(object sender, EventArgs e) { var taskId = (int)((Button)sender).CommandParameter; var task = await App.Database.GetTaskAsync(taskId);
相关产品推荐
相关产品推荐

