Android WorkManager:如何处理正在进行的工作,例如蓝牙扫描

时间:2018-08-30 20:03:05

标签: android bluetooth background bluetooth-lowenergy android-workmanager

由于最近对background Servicesimplicit Broadcasts的限制,Android开发人员只剩下JobScheduler,以及更高级别的WorkManager来安排后台任务。

WorkManager的Worker类很简单,但是我对实现正在进行的工作而不是一次性工作的最佳方法有些困惑。对于我们的示例,让我们考虑“蓝牙低功耗”扫描,但是所有正在进行的不确定工作也存在同样的问题。

类似的东西显然不起作用:

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的体系结构,这种正在进行的后台工作是被Google扼杀。 Worker中的wait()/ notify()模式是否可以接受,还是该解决方法被系统杀死?

任何提示将不胜感激。

编辑:

我希望避免使用前台服务+正在进行的通知。答案here似乎很有希望,但显然已在Android 7.1中进行了修补。在运行Android 9的手机上,将无线BLE耳机从手机壳中取出后几乎可以立即连接。耳机供应商未运行前台服务(至少在不可见的情况下-没有持久通知)来检测广告。我不知道他们如何可靠地做到这一点。

1 个答案:

答案 0 :(得分:3)

WorkManager不适合连续工作-这将是前台服务的用例。

但是,通过引入BluetoothLeScanner.startScan(List<ScanFilter>, ScanSettings, PendingIntent)方法,BLE扫描不需要您的应用程序就可以在API 26+设备上连续运行,该方法允许您将PendingIntent注册为回调,从而开始仅当扫描结果可用时。

对于早期版本的Android,您将需要一个持续运行的服务来维持主动扫描。