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>
原因分析与解决方案
核心原因
- 系统休眠策略限制:设备未充电且熄屏时,Android会进入深度休眠,即使持有
Partial WakeLock,WiFi模块也可能被强制关闭以省电,导致扫描无法正常执行。 - 后台扫描权限收紧:Android 12+对后台WiFi扫描有严格限制,未充电时系统会大幅降低扫描频率甚至直接阻止操作。
- 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
相关产品推荐
相关产品推荐

