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

Xamarin后台IntentService启动后触发WaitingInMainSignalCatcherLoop问题求助

解决Xamarin Android传感器收集服务的ANR问题

看起来你遇到的是**ANR(Application Not Responding)**问题——日志里生成/data/anr/traces.txt就是明确信号,系统在主线程被阻塞超过阈值(Android 8.0+是5秒)时会触发Signal 3并导出堆栈。结合你的场景,问题根源主要有两个:

核心问题分析

  1. IntentService不适合长期传感器收集:IntentService的设计目标是处理一次性后台任务,执行完OnHandleIntent后会自动调用stopSelf()停止服务。但传感器收集是持续任务,这会导致服务生命周期混乱,传感器注册后无法及时解绑,持续的回调会占用主线程资源。
  2. 主线程执行耗时DB操作:ISensorEventListener的OnSensorChanged默认在主线程执行,而你在回调里直接用同步SQLite写入——传感器回调频率很高(比如加速度计可达几十Hz),频繁的同步DB写入会直接阻塞主线程,最终触发ANR。

具体修复方案

1. 替换IntentService为普通Service(加前台服务)

普通Service更适合长期运行的后台任务,同时Android 8.0+要求长期运行的后台服务必须改为前台服务,避免被系统强制杀死。

2. 把传感器回调移到后台线程

创建独立的HandlerThread处理传感器回调,彻底避免阻塞主线程。

3. 异步+批量写入SQLite

用sqlite-net的异步API(InsertAsync)替代同步写入,同时攒批量数据再写入,减少数据库IO次数,提升性能。

修改后的示例代码

using System;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
using System.Collections.Concurrent;
using Android.App;
using Android.Content;
using Android.Hardware;
using Android.OS;
using Android.Runtime;
using AndroidX.Core.App;
using Tabi.DataStorage;
using Tabi.DataObjects;

namespace Tabi.Droid.CollectionService
{
    [Service(ForegroundServiceType = Android.Content.PM.ForegroundService.TypeDataCollection)]
    public class SensorService : Service, ISensorEventListener
    {
        private ISensorRepository<Accelerometer> _accelerometerRepository;
        private ISensorRepository<Gyroscope> _gyroscopeRepository;
        private ISensorRepository<Magnetometer> _magnetometerRepository;
        private SensorManager _sensorManager;
        private HandlerThread _sensorCallbackThread;
        private Handler _sensorHandler;
        private BlockingCollection<SensorDataBase> _dataQueue;
        private Task _batchWriteTask;
        private CancellationTokenSource _cts;
        private const int BatchSize = 50; // 批量写入的条数,可根据需求调整

        public override void OnCreate()
        {
            base.OnCreate();
            // 初始化你的数据仓库(这里假设用依赖注入获取,根据你的实际情况调整)
            _accelerometerRepository = ...;
            _gyroscopeRepository = ...;
            _magnetometerRepository = ...;

            // 创建后台线程处理传感器回调
            _sensorCallbackThread = new HandlerThread("SensorCallbackWorker");
            _sensorCallbackThread.Start();
            _sensorHandler = new Handler(_sensorCallbackThread.Looper);

            // 初始化传感器管理器并注册回调(指定后台线程的Looper)
            _sensorManager = (SensorManager)GetSystemService(SensorService);
            
            var accelerometer = _sensorManager.GetDefaultSensor(SensorType.Accelerometer);
            _sensorManager.RegisterListener(this, accelerometer, SensorDelay.Normal, _sensorHandler);
            
            var gyroscope = _sensorManager.GetDefaultSensor(SensorType.Gyroscope);
            _sensorManager.RegisterListener(this, gyroscope, SensorDelay.Normal, _sensorHandler);
            
            var magnetometer = _sensorManager.GetDefaultSensor(SensorType.MagneticField);
            _sensorManager.RegisterListener(this, magnetometer, SensorDelay.Normal, _sensorHandler);

            // 初始化数据队列和批量写入任务
            _dataQueue = new BlockingCollection<SensorDataBase>();
            _cts = new CancellationTokenSource();
            _batchWriteTask = Task.Run(ProcessDataQueueAsync, _cts.Token);

            // 启动前台服务(Android 8.0+必须)
            var notification = new NotificationCompat.Builder(this, "sensor_collection_channel")
                .SetContentTitle("传感器数据收集")
                .SetContentText("正在收集传感器数据...")
                .SetSmallIcon(Resource.Drawable.ic_notification) // 替换成你的通知图标
                .Build();
            StartForeground(1, notification);
        }

        public override IBinder OnBind(Intent intent) => null;

        public void OnSensorChanged(SensorEvent e)
        {
            if (_cts.Token.IsCancellationRequested) return;

            // 封装传感器数据到队列,不直接写入DB
            SensorDataBase data = e.Sensor.Type switch
            {
                SensorType.Accelerometer => new Accelerometer
                {
                    Timestamp = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds(),
                    X = e.Values[0],
                    Y = e.Values[1],
                    Z = e.Values[2]
                },
                SensorType.Gyroscope => new Gyroscope
                {
                    Timestamp = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds(),
                    X = e.Values[0],
                    Y = e.Values[1],
                    Z = e.Values[2]
                },
                SensorType.MagneticField => new Magnetometer
                {
                    Timestamp = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds(),
                    X = e.Values[0],
                    Y = e.Values[1],
                    Z = e.Values[2]
                },
                _ => null
            };

            if (data != null)
            {
                _dataQueue.TryAdd(data);
            }
        }

        public void OnAccuracyChanged(Sensor sensor, [GeneratedEnum] SensorStatus accuracy)
        {
            // 可根据需求处理传感器精度变化,暂时忽略也没问题
        }

        private async Task ProcessDataQueueAsync()
        {
            var batch = new List<SensorDataBase>();
            try
            {
                foreach (var data in _dataQueue.GetConsumingEnumerable(_cts.Token))
                {
                    batch.Add(data);
                    if (batch.Count >= BatchSize)
                    {
                        await WriteBatchToDbAsync(batch);
                        batch.Clear();
                    }
                }

                // 服务停止前处理剩余的未写入数据
                if (batch.Count > 0)
                {
                    await WriteBatchToDbAsync(batch);
                }
            }
            catch (OperationCanceledException)
            {
                // 任务被取消,正常退出
            }
        }

        private async Task WriteBatchToDbAsync(List<SensorDataBase> batch)
        {
            foreach (var data in batch)
            {
                switch (data)
                {
                    case Accelerometer acc:
                        await _accelerometerRepository.InsertAsync(acc);
                        break;
                    case Gyroscope gyro:
                        await _gyroscopeRepository.InsertAsync(gyro);
                        break;
                    case Magnetometer mag:
                        await _magnetometerRepository.InsertAsync(mag);
                        break;
                }
            }
        }

        public override void OnDestroy()
        {
            // 取消批量写入任务
            _cts.Cancel();
            _batchWriteTask.Wait(1000); // 等待最多1秒让剩余数据写入

            // 解绑传感器,避免内存泄漏
            _sensorManager.UnregisterListener(this);

            // 停止后台线程
            _sensorCallbackThread.QuitSafely();
            _sensorCallbackThread.Join();

            // 清理资源
            _dataQueue.Dispose();
            _cts.Dispose();

            StopForeground(true);
            base.OnDestroy();
        }
    }

    // 假设你的传感器数据类继承自这个基类
    public abstract class SensorDataBase
    {
        public long Timestamp { get; set; }
    }
}

额外调试建议

  1. 查看ANR堆栈详情:通过adb pull /data/anr/traces.txt导出堆栈文件,里面会明确显示哪个线程被阻塞、阻塞在什么方法上(比如SQLite的写入锁),这是定位问题的关键。
  2. 用Profiler监控性能:使用Android Studio Profiler或Xamarin Profiler,监控线程状态、DB操作耗时,找到具体的性能瓶颈。
  3. 确保SQLite线程安全:sqlite-net默认是线程安全的,但要避免多线程同时打开独立的数据库连接,建议用单例模式管理数据库实例。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.21 04:03:42