Android WorkManager: как обрабатывать текущую работу, например, сканирование Bluetooth - PullRequest
0 голосов
/ 30 августа 2018

Ввиду недавних ограничений на фоновые службы и неявных трансляций , разработчикам Android осталось JobScheduler и, на более высоком уровне, WorkManager для планирования фоновых задач.

Класс Worker для WorkManager достаточно прост, но меня немного смущает лучший способ реализации текущей работы, а не разовая. Для нашего примера давайте рассмотрим сканирование Bluetooth с низким энергопотреблением, но то же самое относится ко всей текущей неопределенной работе.

Что-то вроде этого, очевидно, не работает:

public class MyWorker extends Worker {

    private BluetoothLeScanner mBluetoothLeScanner;

    @Override
    public Worker.Result doWork() {
        mBluetoothLeScanner = BluetoothAdapter.getDefaultAdapter().getBluetoothLeScanner();

        // Pretend there's some bluetooth setup here
        // ...

        mBluetoothLeScanner.startScan( .. ,  .. , .. );
        return Result.SUCCESS;
    }

}

Выше мы начинаем сканирование, а затем немедленно выпадаем из области видимости, поэтому сканирование не будет продолжаться.

Мы можем использовать wait () / notify (), чтобы обойти это, но это выглядит очень грязно. Как то так ...

public class MyWorker extends Worker {

    private BluetoothLeScanner mBluetoothLeScanner;
    private final Object mLock = new Object();
    private Handler mBackgroundHandler;

    private Handler getBackgroundHandler() {
        if (mBackgroundHandler == null) {
            HandlerThread thread = new HandlerThread("background");
            thread.start();
            mBackgroundHandler = new Handler(thread.getLooper());
        }
        return mBackgroundHandler;
    }

    @Override
    public Worker.Result doWork() {
        getBackgroundHandler().post(new Runnable() {
            @Override
            public void run() {
                mBluetoothLeScanner = BluetoothAdapter.getDefaultAdapter().getBluetoothLeScanner();
                // Pretend there's some bluetooth setup here
                // ...
                mBluetoothLeScanner.startScan( .. ,  .. , mScanCallback);
            }
        });

        getBackgroundHandler().postDelayed(new Runnable() {
            @Override
            public void run() {
                mBluetoothLeScanner.stopScan(mScanCallback);
                synchronized (mLock) {
                    mLock.notify();
                }
            }
        },  60 * 1000); //stop after a minute

        try {
            synchronized (mLock) {
                mLock.wait();
            }
        }
        catch (InterruptedException ex) {
            Thread.currentThread().interrupt();
        }

        return Result.SUCCESS;
    }

    private ScanCallback mScanCallback = new ScanCallback() {
        @Override
        public void onScanResult(int callbackType, ScanResult result) {
            super.onScanResult(callbackType, result);
            //We found an advertisement
            mBluetoothLeScanner.stopScan(mScanCallback);
            synchronized (mLock) {
                mLock.notify();
            }
        }

        @Override
        public void onBatchScanResults(List<ScanResult> results) {
            super.onBatchScanResults(results);
        }

        @Override
        public void onScanFailed(int errorCode) {
            super.onScanFailed(errorCode);
            mBluetoothLeScanner.stopScan(mScanCallback);
            synchronized (mLock) {
                mLock.notify();
            }
        }
    };

    @Override
    public void onStopped(boolean cancelled) {
        if (mBackgroundHandler != null) {
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) {
                mBackgroundHandler.getLooper().quitSafely();
            } else {
                mBackgroundHandler.getLooper().quit();
            }
            mBackgroundHandler = null;
        }
    }
}

TLDR: Каков наилучший способ реализовать текущую фоновую работу в современной (8.1+) Android? Учитывая архитектуру Worker / WorkManager, кажется, что этот вид постоянной фоновой работы в настоящее время погасил в гугле. Допустим ли шаблон wait () / notify () в Worker, или этот обходной путь будет убит системой?

Любые советы приветствуются.

Редактировать:

Я надеялся избежать использования службы переднего плана + текущее уведомление. Ответ здесь показался многообещающим, но он, очевидно, был исправлен в Android 7.1. На моем телефоне под управлением Android 9 мои беспроводные наушники BLE подключаются практически сразу, когда их вынимают из чехла. Поставщик наушников НЕ использует службу переднего плана (по крайней мере, не заметно - нет постоянных уведомлений) для обнаружения рекламы. Я понятия не имею, как они делают это так надежно.

1 Ответ

0 голосов
/ 31 августа 2018

WorkManager не подходит для непрерывной работы - это будет вариант использования для служб переднего плана.

Однако сканирование BLE не требует непрерывной работы вашего приложения на устройствах API 26+ с введением метода BluetoothLeScanner.startScan(List<ScanFilter>, ScanSettings, PendingIntent), который позволяет зарегистрировать PendingIntent в качестве обратного вызова, запуск приложения только при наличии результатов сканирования.

Для предыдущих версий Android вам потребуется постоянно работающий сервис для поддержки активного сканирования.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...