Xamarin.Android前台服务重复创建线程 如何终止旧实例的线程
问题根因
Android前台服务一旦启动成功处于运行状态,后续重复调用StartService()不会创建新的服务实例,仅会触发OnStartCommand回调。你当前的代码每次接收到START_SERVICE动作时,都会无判断执行RegisterNotification()方法,该方法中每次都会新建工作线程赋值给refrescar变量,旧线程的循环逻辑没有终止标识,会持续在后台运行,最终导致每次重启应用都会多一个拉取数据的线程。
修复方案
按以下步骤修改Servicio类即可解决重复创建线程的问题:
- 新增类级别的线程运行状态标识、线程终止控制变量,避免重复创建线程,同时替代不安全的
Thread.Abort()方法 - 执行线程创建逻辑前先判断工作线程是否已存活,存活则直接跳过创建逻辑
- 避免重复执行前台服务通知注册逻辑
修改后的核心代码示例
[Service(ForegroundServiceType = Android.Content.PM.ForegroundService.TypeDataSync)] public class Servicio : Service, IServicio { Uri link = new Uri(new Url().Dir + "/estado_alarma.php"); Thread refrescar; // 新增控制变量 private bool _isThreadRunning = false; private bool _stopThreadFlag = false; // 标记通知是否已经注册,避免重复初始化 private bool _notificationRegistered = false; public override IBinder OnBind(Intent intent) { throw new NotImplementedException(); } public override StartCommandResult OnStartCommand(Intent intent, [GeneratedEnum] StartCommandFlags flags, int starId) { if (intent.Action == "START_SERVICE") { System.Diagnostics.Debug.WriteLine("Se ha iniciado el servicio"); // 仅当通知未注册时执行初始化逻辑 if (!_notificationRegistered) { RegisterNotification(); _notificationRegistered = true; } } else if (intent.Action == "STOP_SERVICE") { // 停止服务前先终止工作线程 _stopThreadFlag = true; if (refrescar != null && refrescar.IsAlive) { refrescar.Join(1000); } _isThreadRunning = false; _notificationRegistered = false; StopForeground(true); StopSelfResult(starId); } return StartCommandResult.NotSticky; } private void RegisterNotification() { NotificationChannel channel = new NotificationChannel("ServicioChannel", "Demo de servicio", NotificationImportance.Max); NotificationManager manager = (NotificationManager)MainActivity.ActivityCurrent.GetSystemService(Context.NotificationService); manager.CreateNotificationChannel(channel); Notification notification = new Notification.Builder(this, "ServicioChannel").SetContentTitle("Servicio Trabajando").SetOngoing(true).Build(); StartForeground(200, notification); IniciarServicio(); } public void Start() { Intent startService = new Intent(MainActivity.ActivityCurrent, typeof(Servicio)); startService.SetAction("START_SERVICE"); MainActivity.ActivityCurrent.StartService(startService); } public void Stop() { Intent stopService = new Intent(MainActivity.ActivityCurrent, typeof(Servicio)); stopService.SetAction("STOP_SERVICE"); MainActivity.ActivityCurrent.StartService(stopService); } public int IniciarServicio() { // 线程已运行则直接返回,不重复创建 if (_isThreadRunning && refrescar != null && refrescar.IsAlive) { return 0; } _stopThreadFlag = false; refrescar = new Thread(EsperaAlarma); refrescar.Start(); _isThreadRunning = true; return 1; } // 去掉无用的async标记,原方法没有await逻辑 public void EsperaAlarma() { WebClient cliente = new WebClient(); byte[] respuesta; string resultado; while (!_stopThreadFlag) { respuesta = cliente.DownloadData(link); resultado = System.Text.Encoding.ASCII.GetString(respuesta); if (resultado == "Alerta") { setNotificationVolumeToMax(); Device.BeginInvokeOnMainThread(() => { App.Current.MainPage = new Alerta(); var powerManager = (PowerManager)GetSystemService(PowerService); var wakeLock = powerManager.NewWakeLock(WakeLockFlags.ScreenDim | WakeLockFlags.AcquireCausesWakeup, "Ejemplo"); wakeLock.Acquire(); Intent x = new Intent(MainActivity.ActivityCurrent.Intent); StartActivity(x); }); // 触发警报后终止线程 _stopThreadFlag = true; break; } Thread.Sleep(3000); } cliente.Dispose(); _isThreadRunning = false; } private void setNotificationVolumeToMax() { try { var audioManager = (AudioManager)GetSystemService(Context.AudioService); int max_volume = audioManager.GetStreamMaxVolume(Android.Media.Stream.Music); audioManager.SetStreamVolume(Android.Media.Stream.Music, max_volume, 0); } catch (Exception ex) { Console.WriteLine(ex.Message); } } }
额外优化说明
- 原方法
EsperaAlarmaAsync标注了async但没有任何await逻辑,属于无效声明,已经移除避免异步异常被吞 - 用控制变量
_stopThreadFlag替代Thread.Abort(),后者属于不安全的线程终止方法,容易引发资源泄漏 - 新增重复逻辑判断,避免每次重启应用都重复创建通知通道、注册前台服务、创建工作线程
内容的提问来源于stack exchange,提问作者Alí Emmanuel Flores Navarro
相关产品推荐
相关产品推荐

