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

.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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.29 02:40:38