Утечка памяти при непрерывном сканировании Bluetooth - PullRequest
0 голосов
/ 01 июля 2019

У меня есть приложение, которое нужно сканировать устройства Bluetooth на неопределенное время. Таким образом, мой вариант использования: я сканирую устройства Bluetooth в течение 5 секунд, получаю все рекламные данные, откладываю 1 секунду и повторяю сканирование снова. Мой код отлично работает в течение 7-8 часов (иногда более 24 часов), а затем запускается с ошибкой утечки памяти.

public class BLEScan:IBLEScan
{
    private CancellationTokenSource _scanCancellationToken;
    private volatile bool _IsScanning;
    public int ScanTimeout { get; set; } = 5000;
    public bool IsScanning
    {
        get { return _IsScanning; }
        private set { _IsScanning = value; }
    };

    private readonly BluetoothManager _bluetoothManager;
    private readonly BluetoothAdapter _bluetoothAdapter;

    private readonly Api21BleScanCallback _api21ScanCallback;
    private PendingIntent pendingIntent;

    public BLEScan()
    {
        _bluetoothManager = (BluetoothManager)Android.App.Application.Context.GetSystemService(Android.Content.Context.BluetoothService);
        _bluetoothAdapter = _bluetoothManager.Adapter;

        if (Build.VERSION.SdkInt >= BuildVersionCodes.Lollipop)
        {
            _api21ScanCallback = new Api21BleScanCallback();
        }
    }

    public async Task StartScanning(CancellationToken cancellationToken)
    {
        if (IsScanning)
        {
            Console.WriteLine("Scan Already Running.");
            return;
        }

        IsScanning = true;
        _scanCancellationToken = new CancellationTokenSource();
        try
        {
            using (cancellationToken.Register(() => _scanCancellationToken?.Cancel()))
            {
                await StartScanNative();
                await Task.Delay(ScanTimeout, _scanCancellationToken.Token);
                CleanUpScan();
            }
        }
        catch
        {

        }
    }

    public void CleanUpScan()
    {
        StopScanNative();

        if (_scanCancellationToken != null)
        {
            _scanCancellationToken.Dispose();
            _scanCancellationToken = null;
        }
        IsScanning = false;
    }

    public void StopScanNative()
    {
        if (Build.VERSION.SdkInt >= BuildVersionCodes.Lollipop)
        {
            _bluetoothAdapter.BluetoothLeScanner?.StopScan(pendingIntent);
        }
    }

    public Task StartScanNative()
    {
        if (Build.VERSION.SdkInt > BuildVersionCodes.Lollipop)
        {
            Intent receiverIntent = new Intent(Application.Context, typeof(Api21BleScanCallback));
            receiverIntent.SetAction("com.company.ACTION_FOUND");
            pendingIntent = PendingIntent.GetBroadcast(Application.Context, 1, receiverIntent, PendingIntentFlags.UpdateCurrent);
            _bluetoothAdapter.BluetoothLeScanner.StartScan(null, null, pendingIntent);
        }
        return Task.FromResult(true);
    }

    [BroadcastReceiver]
    [IntentFilter(new[] { "com.company.ACTION_FOUND" })]
    public class Api21BleScanCallback : BroadcastReceiver
    {
        public override void OnReceive(Context context, Intent intent)
        {
            var action = intent.Action;
            if (intent.HasExtra(BluetoothLeScanner.ExtraListScanResult))
            {
                var x = intent.GetParcelableArrayListExtra(BluetoothLeScanner.ExtraListScanResult);
              // Parse necessary data here 
            }
         }
    }
    }

  public interface IBLEScan
{
    int ScanTimeout { get; set; }
    bool IsScanning { get;}
    Task StartScanning(CancellationToken cancellationToken = default(CancellationToken));
}

Я не уверен, какой объект вызывает эту ошибку. Я также реализовал сканирование в сервисе, но ошибка та же. Само сканирование выполняется в задаче, и объект BLEscan лениво инициализируется.

 public async override void OnCreate()
    {
        base.OnCreate();
        var adapter = LazyPodScanner.Current;
        var task = Task.Run(async () =>
        {
            while (!cancellationTokenSource.IsCancellationRequested)
            {
                await adapter.StartScanning(cancellationTokenSource.Token);
                await Task.Delay(1000);
            }
        }, cancellationTokenSource.Token);
    }

Кроме того, иногда также возникает ошибка переполнения глобальной справочной таблицы.

Реализация LLE-инициализированного сканирования BLE:

 public class LazyPodScanner
{
    static readonly Lazy<IBLEScan> Implementation = new Lazy<IBLEScan>(CreateImplementation, System.Threading.LazyThreadSafetyMode.PublicationOnly);

    static IBLEScan CreateImplementation()
    {
        return new BLEScan();
    }

    public static IBLEScan Current
    {
        get
        {
            var val = Implementation.Value;
            if (val == null)
            {
                throw NotImplementedInReferenceAssembly();
            }
            return val;
        }
    }

    internal static Exception NotImplementedInReferenceAssembly()
    {
        return new NotImplementedException("Lazy Initialization of Scanner failed");
    }

}
...