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 版本,您需要持续 运行 服务来维持主动扫描。
利用最近的限制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 版本,您需要持续 运行 服务来维持主动扫描。