Ввиду недавних ограничений на фоновые службы и неявных трансляций , разработчикам 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 подключаются практически сразу, когда их вынимают из чехла. Поставщик наушников НЕ использует службу переднего плана (по крайней мере, не заметно - нет постоянных уведомлений) для обнаружения рекламы. Я понятия не имею, как они делают это так надежно.