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

NET MAUI前台服务保持CPU活跃:黑屏仅充电可用的原因排查

问题:NET MAUI前台服务熄屏未充电时无法正常扫描WiFi

我正在开发一款NET MAUI应用,内置前台服务负责扫描附近WiFi网络、保存结果到列表,发现指定SSID时执行对应操作。目前应用在设备活跃、应用关闭时运行正常;但熄屏后服务虽能启动,却只有在设备连接充电器时才能正常工作,请问这是什么原因?


服务实现代码

[Service(Name = "com.BackgroudAppTest.DemoServices")]
public class DemoServices : Service, IServicesTest
{
    private PowerManager.WakeLock wakeLock;  
    public int numeroUscite;
    private WifiManager wifiManager;
    private ObservableCollection<string> wifiNetworks = new ObservableCollection<string>();

    public override void OnCreate()   
    {
        base.OnCreate(); 
    }
    public override IBinder OnBind(Intent intent)
    {
        throw new NotImplementedException();
    }
    [return: GeneratedEnum]
    public override StartCommandResult OnStartCommand(Intent intent, [GeneratedEnum] StartCommandFlags flags, int startId)
    {
        if (intent.Action == "START_SERVICE")
        {
            wifiManager = (WifiManager)Android.App.Application.Context.GetSystemService(Android.Content.Context.WifiService);

            //保持CPU在熄屏时活跃
            WakeLockFlags wakeFlags = WakeLockFlags.Partial;
            var powerManager = (PowerManager)GetSystemService(PowerService);
            wakeLock = powerManager.NewWakeLock(wakeFlags, typeof(DemoServices).FullName);
            wakeLock.Acquire();

            System.Diagnostics.Debug.WriteLine("服务已启动");
            Task.Delay(2000);                
            //获取用户传入的数字
            int intValue = intent.GetIntExtra("MyIntValue", -1) -1;
            //获取用户传入的SSID
            string userString = intent.Extras.GetString("MySsid");

            Task.Run(async () =>
            {
                while (true)
                {
                    bool found = false;
                    var database = new Database();

                    wifiNetworks.Clear();
                    wifiManager.StartScan();
                    await Task.Delay(5000);
                    IList<ScanResult> scanResults = wifiManager.ScanResults;
                    foreach (ScanResult scanResult in scanResults)
                    {
                        wifiNetworks.Add(scanResult.Ssid);
                        if (scanResult.Ssid.Equals(userString, StringComparison.OrdinalIgnoreCase))
                        {
                            found = true;
                            break;                                
                        }                       
                    }

                    if (found)
                    {
                        intValue++;
                        MessItem nuovoGeoItem = new MessItem
                        {
                            Data = DateTime.Now,
                            Descrizione = "进入 " + intValue
                        };
                        int esito = await database.AggiungiGeoItem(nuovoGeoItem);
                    }
                    else
                    {
                        numeroUscite++;
                        MessItem nuovoGeoItem = new MessItem
                        {
                            Data = DateTime.Now,
                            Descrizione = "离开 " + numeroUscite
                        };
                        int esito = await database.AggiungiGeoItem(nuovoGeoItem);
                    }

                    //等待1分钟后执行下一次扫描
                    await Task.Delay(TimeSpan.FromMinutes(1));
                }             
            });
            RegisterNotification();  
        }
        //停止服务
        else if (intent.Action == "STOP_SERVICE")
        {
            System.Diagnostics.Debug.WriteLine("服务已停止");
            StopForeground(true);
            StopSelfResult(startId);               
        }
        return StartCommandResult.Sticky;
    }
    
    public void Start()
    {
        Intent startService = new Intent(MainActivity.ActivityCurrent, typeof(DemoServices));
        startService.SetAction("START_SERVICE");
        MainActivity.ActivityCurrent.StartService(startService);
    }
    public void Stop()
    {          
        Intent stopIntent = new Intent(MainActivity.ActivityCurrent, this.Class);
        stopIntent.SetAction("STOP_SERVICE");
        MainActivity.ActivityCurrent.StartService(stopIntent);
    }
    public override void OnDestroy()
    {
        if (wakeLock != null && wakeLock.IsHeld)
        {
            wakeLock.Release();
            wakeLock = null;
        }
        base.OnDestroy();
    }
    private void RegisterNotification()
    {
        NotificationChannel channel = new NotificationChannel("ServicioChannel", "扫描服务", NotificationImportance.Max);
        NotificationManager manager = (NotificationManager)MainActivity.ActivityCurrent.GetSystemService(Context.NotificationService);
        manager.CreateNotificationChannel(channel);
        Notification notification = new Notification.Builder(this, "ServicioChannel")
            .SetContentTitle("服务已激活")
            .SetSmallIcon(Resource.Drawable.abc_textfield_search_default_mtrl_alpha)
            .SetOngoing(true)
            .Build();

        StartForeground(100, notification);
    }
}

AndroidManifest.xml配置

<manifest xmlns:android="http://schemas.android.com/apk/res/android">
    <application android:allowBackup="true" android:icon="@mipmap/appicon" android:roundIcon="@mipmap/appicon_round" android:supportsRtl="true">
        <service
            android:name=".Services.DemoServices"
            android:exported="false"
            android:stopWithTask="false" />     
    </application>
    
    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
    <uses-permission android:name="android.permission.INTERNET" />
    <uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
    <uses-permission android:name="android.permission.CHANGE_WIFI_STATE" />
    <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
    <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
    <uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
    <uses-permission android:name="android.permission.NETWORK_SETUP_WIZARD" />
    <uses-permission android:name="android.permission.NETWORK_SETTINGS" />
    <uses-permission android:name="android.permission.PROCESS_STATE_BACKGROUND" />
    <uses-permission android:name="android.permission.PROCESS_STATE_CACHED" />
    <uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION" />
    <uses-permission android:name="android.permission.POST_NOTIFICATIONS" /> 
    <uses-permission android:name="android.permission.WAKE_LOCK" />
    
</manifest>

原因分析与解决方案

核心原因

  1. 系统休眠策略限制:设备未充电且熄屏时,Android会进入深度休眠,即使持有Partial WakeLock,WiFi模块也可能被强制关闭以省电,导致扫描无法正常执行。
  2. 后台扫描权限收紧:Android 12+对后台WiFi扫描有严格限制,未充电时系统会大幅降低扫描频率甚至直接阻止操作。
  3. WakeLock使用不规范:代码中Task.Delay(2000)未加await,可能导致WakeLock还未完全获取就启动扫描;长时间持有WakeLock也可能触发系统电池优化干预。

解决方案

1. 适配前台服务类型

在Manifest的service标签中添加位置类型声明,明确服务用于位置相关操作,提升系统优先级:

<service
    android:name=".Services.DemoServices"
    android:exported="false"
    android:stopWithTask="false"
    android:foregroundServiceType="location" />

2. 优化WakeLock与WiFi锁定

  • 将Task.Delay(2000)改为await Task.Delay(2000),确保WakeLock完全生效后再执行扫描。
  • 添加WifiLock强制保持WiFi模块活跃:
// 在获取wifiManager后添加
WifiLock wifiLock = wifiManager.CreateWifiLock(WifiMode.FullHighPerf, "WifiScanLock");
wifiLock.Acquire();

在服务销毁时同步释放:

public override void OnDestroy()
{
    if (wakeLock != null && wakeLock.IsHeld)
    {
        wakeLock.Release();
        wakeLock = null;
    }
    if (wifiLock != null && wifiLock.IsHeld)
    {
        wifiLock.Release();
        wifiLock = null;
    }
    base.OnDestroy();
}

3. 避开电池优化

引导用户将应用加入电池优化白名单,避免系统限制后台操作:

private void RequestIgnoreBatteryOptimizations()
{
    var powerManager = (PowerManager)GetSystemService(PowerService);
    if (!powerManager.IsIgnoringBatteryOptimizations(PackageName))
    {
        Intent intent = new Intent(Android.OS.PowerManager.ActionRequestIgnoreBatteryOptimizations);
        intent.SetData(Android.Net.Uri.Parse("package:" + PackageName));
        StartActivity(intent);
    }
}

在服务启动时调用此方法,或在应用首次启动时引导用户操作。

4. 改用系统调度任务

替换无限循环扫描逻辑,使用WorkManager调度扫描任务,利用系统机制提升后台执行优先级,尤其是未充电场景下。


内容的提问来源于stack exchange,提问作者loury Pe

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.08 09:58:13