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

Android WorkManager: how to handle ongoing work, like Bluetooth scanning

利用最近的限制background Services and implicit Broadcasts, Android devs are left with JobScheduler, and, at a higher level, WorkManager来安排后台任务。

WorkManager 的 Worker class 非常简单,但我对实施持续工作而不是一次性工作的最佳方式感到有点困惑。对于我们的示例,让我们考虑蓝牙低功耗扫描,但同样的问题适用于所有正在进行的、不确定的工作。

这样的事情显然行不通:

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 中实施正在进行的后台工作的最佳方式是什么? 考虑到 [=37 的架构,它确实出现了=],这种正在进行的后台工作正在被 Google 扼杀。 Worker 中的 wait()/notify() 模式是否可以接受,或者此解决方法是否会被系统终止?

如有任何提示,我们将不胜感激。

编辑:

我希望避免使用前台服务 + 持续通知。答案 here 似乎很有希望,但它显然已在 Android 7.1 中进行了修补。在我的 phone 运行 Android 9 上,我的无线 BLE 头 phone 从外壳中取出后几乎立即连接。 headphone 供应商不是 运行 检测广告的前台服务(至少不可见——没有持久通知)。我不知道他们是怎么做到如此可靠的。

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

然而,BLE 扫描不需要您的应用程序在 API 26+ 设备上连续 运行 引入 BluetoothLeScanner.startScan(List<ScanFilter>, ScanSettings, PendingIntent) 方法,它允许您注册一个 PendingIntent 作为回调,只有在扫描结果可用时才启动您的应用程序。

对于以前的 Android 版本,您需要持续 运行 服务来维持主动扫描。