.NET MAUI Android端:关闭APP后持续扫描WiFi网络的问题
解决.NET MAUI前台服务在APP关闭后停止WiFi扫描的问题
问题根源分析
你的代码存在几个关键问题导致APP关闭后扫描停止:
- 服务重启策略错误:
OnStartCommand返回StartCommandResult.NotSticky,系统不会在服务被回收后重启它,APP关闭后服务可能被系统杀死。 - 扫描逻辑嵌套循环冲突:
ScanWifiNetworks内部的while(isScanning)和外层的Task.Run循环重复,且每次调用都会重新创建cancellationTokenSource,导致取消令牌失效。 - 扫描结果等待不可靠:用固定的
Task.Delay(5000)等待扫描结果,无法保证扫描完成,可能导致结果为空。 - 服务启动方式不兼容高版本Android:Android 8.0+要求用
StartForegroundService启动前台服务,否则会触发ANR。
修复步骤
1. 调整服务重启策略
在OnStartCommand中返回StartCommandResult.Sticky,确保系统在服务意外终止后自动重启:
return StartCommandResult.Sticky;
2. 重构扫描逻辑,移除嵌套循环
删除ScanWifiNetworks内部的while(isScanning)循环,由外层的Task.Run循环控制扫描周期,避免重复创建取消令牌。
3. 用TaskCompletionSource可靠等待扫描结果
替换固定延迟,通过TaskCompletionSource等待广播接收器触发,确保获取到扫描结果。
4. 适配高版本Android的服务启动方式
修改Start()方法,使用StartForegroundService:
if (Build.VERSION.SdkInt >= BuildVersionCodes.O) { MainActivity.ActivityCurrent.StartForegroundService(startService); } else { MainActivity.ActivityCurrent.StartService(startService); }
5. 确保权限配置正确
在Android项目的AndroidManifest.xml中添加必要权限:
<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.POST_NOTIFICATIONS" />
修改后的完整服务代码
[Service(ForegroundServiceType = Android.Content.PM.ForegroundService.TypeDataSync)] public class DemoServices : Service, IServicesTest { private bool isScanning = false; private CancellationTokenSource cancellationTokenSource; 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") { System.Diagnostics.Debug.WriteLine("服务已启动"); if (!isScanning) { isScanning = true; cancellationTokenSource = new CancellationTokenSource(); Task.Run(async () => { while (!cancellationTokenSource.Token.IsCancellationRequested) { await ScanWifiNetworks(cancellationTokenSource.Token); await Task.Delay(TimeSpan.FromMinutes(2), cancellationTokenSource.Token); } }, cancellationTokenSource.Token); RegisterNotification(); } } else if (intent.Action == "STOP_SERVICE") { System.Diagnostics.Debug.WriteLine("服务已停止"); cancellationTokenSource?.Cancel(); isScanning = false; StopForeground(true); StopSelfResult(startId); } // 改为Sticky,让系统重启服务 return StartCommandResult.Sticky; } public async Task ScanWifiNetworks(CancellationToken token) { if (Device.RuntimePlatform != Device.Android) throw new NotSupportedException("此功能仅支持Android设备。"); var wifiManager = (WifiManager)Android.App.Application.Context.GetSystemService(Context.WifiService); if (!wifiManager.IsWifiEnabled) { wifiManager.SetWifiEnabled(true); await Task.Delay(2000, token); } var tcs = new TaskCompletionSource<IList<ScanResult>>(); var wifiScanReceiver = new WifiScanReceiver(wifiManager, tcs); var intentFilter = new IntentFilter(WifiManager.ScanResultsAvailableAction); try { Android.App.Application.Context.RegisterReceiver(wifiScanReceiver, intentFilter); wifiManager.StartScan(); // 等待扫描结果,超时时间设为10秒 var scanResults = await Task.WhenAny(tcs.Task, Task.Delay(10000, token)).Unwrap(); var database = new Database(); bool found = scanResults.Any(r => r.Ssid == "AndroidWifi"); MessItem nuovoGeoItem = new MessItem { Data = DateTime.Now, Descrizione = found ? "进入" : "离开" }; int esito = await database.AggiungiGeoItem(nuovoGeoItem); } finally { try { Android.App.Application.Context.UnregisterReceiver(wifiScanReceiver); } catch { // 忽略注销失败的情况 } } } class WifiScanReceiver : BroadcastReceiver { private readonly WifiManager _wifiManager; private readonly TaskCompletionSource<IList<ScanResult>> _tcs; public WifiScanReceiver(WifiManager wifiManager, TaskCompletionSource<IList<ScanResult>> tcs) { _wifiManager = wifiManager; _tcs = tcs; } public override void OnReceive(Context context, Intent intent) { if (intent.Action == WifiManager.ScanResultsAvailableAction && !_tcs.Task.IsCompleted) { var results = _wifiManager.ScanResults; _tcs.SetResult(results.ToList()); } } } public void Start() { Intent startService = new Intent(MainActivity.ActivityCurrent, typeof(DemoServices)); startService.SetAction("START_SERVICE"); // 适配Android 8.0+的前台服务启动方式 if (Build.VERSION.SdkInt >= BuildVersionCodes.O) { MainActivity.ActivityCurrent.StartForegroundService(startService); } else { 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() { cancellationTokenSource?.Cancel(); isScanning = false; base.OnDestroy(); } private void RegisterNotification() { NotificationChannel channel = new NotificationChannel("ServicioChannel", "服务演示", NotificationImportance.Max); NotificationManager manager = (NotificationManager)GetSystemService(Context.NotificationService); manager.CreateNotificationChannel(channel); Notification notification = new Notification.Builder(this, "ServicioChannel") .SetContentTitle("服务活跃中") .SetSmallIcon(Resource.Drawable.abc_ab_share_pack_mtrl_alpha) .SetOngoing(true) .Build(); StartForeground(100, notification); } }
额外注意事项
- Android 10+对WiFi扫描频率有限制,短时间内多次扫描会被系统限制,所以2分钟的间隔是合理的,不要设置过短。
- 测试时不要通过开发者选项的"停止应用"来关闭APP,这种方式会直接杀死进程,正常用户通过最近任务列表关闭APP,前台服务应该能继续运行。
- 确保MainActivity.ActivityCurrent是正确的当前Activity引用,避免空引用异常。
内容的提问来源于stack exchange,提问作者loury Pe
相关产品推荐
相关产品推荐

