.NET MAUI Android:前台服务无法持续运行问题求助
.NET MAUI Android前台服务后台运行时日志写入异常(稀疏/停止)问题
我创建了一个带有前台服务的简单.NET MAUI应用,参考相关示例实现Android前台服务,服务启动一个日志器,每5秒将时间戳写入文件。应用在前台激活状态时服务运行正常,但切换到后台后,时间戳写入变得越来越稀疏,甚至完全停止;恢复前台后服务又能正常持续运行。
我已经尝试了两种日志器实现方式:
- 使用
System.Timer,每5秒触发写入时间戳 - 让日志器在独立线程中循环运行,每次休眠5秒
环境:
- Visual Studio 2022
- .NET 7.0
- Android设备API 31
相关代码
LocationService代码
[Service] public partial class LocationService : Service { private SimpleLogger _logger; public LocationService() { _logger = new SimpleLogger(); } public override StartCommandResult OnStartCommand(Intent intent, [GeneratedEnum] StartCommandFlags flags, int startId) { if (intent.Action == "START_SERVICE") { var notification = RegisterNotification(); StartForeground(100, notification); _logger.Start(); } else if (intent.Action == "STOP_SERVICE") { StopForeground(true); _logger.Stop(); StopSelfResult(startId); } return StartCommandResult.NotSticky; } public partial void Start() { Intent startService = new Intent(MainActivity.ActivityCurrent, typeof(LocationService)); startService.SetAction("START_SERVICE"); MainActivity.ActivityCurrent.StartService(startService); } public partial void Stop() { Intent stopIntent = new Intent(MainActivity.ActivityCurrent, this.Class); stopIntent.SetAction("STOP_SERVICE"); MainActivity.ActivityCurrent.StartService(stopIntent); } private Notification RegisterNotification() { NotificationChannel channel = new NotificationChannel("ServiceChannel", "ServiceDemo", NotificationImportance.Max); NotificationManager manager = (NotificationManager)MainActivity.ActivityCurrent.GetSystemService(Context.NotificationService); manager.CreateNotificationChannel(channel); Notification notification = new Notification.Builder(this, "ServiceChannel") .SetContentTitle("Service Working") .SetSmallIcon(Build.VERSION.SdkInt >= BuildVersionCodes.Tiramisu ? Resource.Drawable.abc_action_bar_item_background_material : Resource.Mipmap.appicon) .SetOngoing(true) .Build(); return notification; } public override IBinder OnBind(Intent intent) { throw new NotImplementedException(); } }
SimpleLogger代码
public class SimpleLogger { private const int _periodMsec = 5000; private Timer _timer; private string _pathFilename; public SimpleLogger() { _pathFilename = Path.Combine(FileSystem.Current.CacheDirectory, "DemoLogging.txt"); } public void Start() { _timer = new Timer(TimerElapsed, null, 0, _periodMsec); } public void Stop() { _timer.Dispose(); _timer = null; } private void TimerElapsed(object state) { var s = DateTime.Now.ToString("G"); System.Diagnostics.Debug.WriteLine($"SimpleLogger.TimerElapsed() {s}"); using (StreamWriter sw = File.AppendText(_pathFilename)) { sw.WriteLine(s); } } }
解决方案建议
1. 用AlarmManager替代Timer/线程休眠
Android后台对普通线程和Timer有严格调度限制,即便前台服务也可能被系统节流。AlarmManager能让系统在指定时间唤醒服务执行任务,避免后台优化影响:
private PendingIntent _pendingIntent; private AlarmManager _alarmManager; public override StartCommandResult OnStartCommand(Intent intent, StartCommandFlags flags, int startId) { if (intent.Action == "START_SERVICE") { var notification = RegisterNotification(); StartForeground(100, notification); SetupAlarm(); WriteLog(); // 执行初始日志写入 } else if (intent.Action == "STOP_SERVICE") { StopForeground(true); _alarmManager.Cancel(_pendingIntent); StopSelfResult(startId); } return StartCommandResult.Sticky; // 改为Sticky,系统回收后可重启服务 } private void SetupAlarm() { _alarmManager = (AlarmManager)GetSystemService(Context.AlarmService); Intent alarmIntent = new Intent(this, typeof(AlarmReceiver)); _pendingIntent = PendingIntent.GetBroadcast(this, 0, alarmIntent, PendingIntentFlags.UpdateCurrent | PendingIntentFlags.Immutable); long triggerAtMillis = SystemClock.ElapsedRealtime(); if (Build.VERSION.SdkInt >= BuildVersionCodes.S) { _alarmManager.SetExactAndAllowWhileIdle(AlarmType.ElapsedRealtimeWakeup, triggerAtMillis + 5000, _pendingIntent); } else { _alarmManager.SetRepeating(AlarmType.ElapsedRealtimeWakeup, triggerAtMillis, 5000, _pendingIntent); } } [BroadcastReceiver(Enabled = true)] public class AlarmReceiver : BroadcastReceiver { public override void OnReceive(Context context, Intent intent) { WriteLog(); // API31+需手动循环设置闹钟 if (Build.VERSION.SdkInt >= BuildVersionCodes.S) { AlarmManager alarmManager = (AlarmManager)context.GetSystemService(Context.AlarmService); PendingIntent pendingIntent = PendingIntent.GetBroadcast(context, 0, intent, PendingIntentFlags.UpdateCurrent | PendingIntentFlags.Immutable); alarmManager.SetExactAndAllowWhileIdle(AlarmType.ElapsedRealtimeWakeup, SystemClock.ElapsedRealtime() + 5000, pendingIntent); } } } private static void WriteLog() { string pathFilename = Path.Combine(FileSystem.Current.CacheDirectory, "DemoLogging.txt"); string timestamp = DateTime.Now.ToString("G"); System.Diagnostics.Debug.WriteLine($"SimpleLogger.WriteLog() {timestamp}"); using (StreamWriter sw = File.AppendText(pathFilename)) { sw.WriteLine(timestamp); } }
2. 修改服务启动模式
原代码返回StartCommandResult.NotSticky,系统资源紧张时可能直接回收服务。改为StartCommandResult.Sticky,系统会在资源充足时重启服务,提升后台稳定性。
3. 确保前台服务通知合规
检查通知小图标资源是否有效,避免因图标加载失败导致前台服务被系统降级。同时在API33+设备上,需动态申请POST_NOTIFICATIONS权限,否则通知无法显示,前台服务会被判定为普通后台服务。
4. 添加必要权限
在AndroidManifest.xml中添加以下权限:
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" /> <uses-permission android:name="android.permission.WAKE_LOCK" />
内容的提问来源于stack exchange,提问作者Haimo
相关产品推荐
相关产品推荐

