Skip to content

Commit

Permalink
Start and stop BLE scans from a background thread to prevent blocking…
Browse files Browse the repository at this point in the history
… the UI (issue AltBeacon#136)
  • Loading branch information
marcosalis authored and ost-ct committed Oct 24, 2016
1 parent e4afd02 commit c59223b
Show file tree
Hide file tree
Showing 4 changed files with 104 additions and 44 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -273,6 +273,7 @@ public void onDestroy() {
LogManager.i(TAG, "onDestroy called. stopping scanning");
handler.removeCallbacksAndMessages(null);
mCycledScanner.stop();
mCycledScanner.destroy();
monitoringStatus.stopStatusPreservation();
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@
import android.content.pm.PackageManager;
import android.os.Build;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.Looper;
import android.os.SystemClock;

import org.altbeacon.beacon.BeaconManager;
Expand Down Expand Up @@ -40,7 +42,10 @@ public abstract class CycledLeScanner {
private long mScanPeriod;

protected long mBetweenScanPeriod;
protected final Handler mHandler = new Handler();

protected final Handler mHandler = new Handler(Looper.getMainLooper());
protected final Handler mScanHandler;
private final HandlerThread mScanThread;

protected final BluetoothCrashResolver mBluetoothCrashResolver;
protected final CycledLeScanCallback mCycledLeScanCallback;
Expand All @@ -57,6 +62,10 @@ protected CycledLeScanner(Context context, long scanPeriod, long betweenScanPeri
mCycledLeScanCallback = cycledLeScanCallback;
mBluetoothCrashResolver = crashResolver;
mBackgroundFlag = backgroundFlag;

mScanThread = new HandlerThread("CycledLeScannerThread");
mScanThread.start();
mScanHandler = new Handler(mScanThread.getLooper());
}

public static CycledLeScanner createScanner(Context context, long scanPeriod, long betweenScanPeriod, boolean backgroundFlag, CycledLeScanCallback cycledLeScanCallback, BluetoothCrashResolver crashResolver) {
Expand Down Expand Up @@ -156,6 +165,10 @@ public void stop() {
}
}

public void destroy() {
mScanThread.quit();
}

protected abstract void stopScan();

protected abstract boolean deferScanIfNeeded();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,17 +18,9 @@ public CycledLeScannerForJellyBeanMr2(Context context, long scanPeriod, long bet
super(context, scanPeriod, betweenScanPeriod, backgroundFlag, cycledLeScanCallback, crashResolver);
}

@SuppressWarnings("deprecation")
@Override
protected void stopScan() {
try {
BluetoothAdapter bluetoothAdapter = getBluetoothAdapter();
if (bluetoothAdapter != null) {
bluetoothAdapter.stopLeScan(getLeScanCallback());
}
} catch (Exception e) {
LogManager.e(e, TAG, "Internal Android exception scanning for beacons");
}
postStopLeScan();
}

@Override
Expand All @@ -54,19 +46,57 @@ public void run() {
return false;
}

@SuppressWarnings("deprecation")
@Override
protected void startScan() {
getBluetoothAdapter().startLeScan(getLeScanCallback());
postStartLeScan();
}

@SuppressWarnings("deprecation")
@Override
protected void finishScan() {
getBluetoothAdapter().stopLeScan(getLeScanCallback());
postStopLeScan();
mScanningPaused = true;
}

private void postStartLeScan() {
final BluetoothAdapter bluetoothAdapter = getBluetoothAdapter();
if (bluetoothAdapter == null) {
return;
}
final BluetoothAdapter.LeScanCallback leScanCallback = getLeScanCallback();
mScanHandler.removeCallbacksAndMessages(null);
mScanHandler.post(new Runnable() {
@Override
public void run() {
try {
//noinspection deprecation
bluetoothAdapter.startLeScan(leScanCallback);
} catch (Exception e) {
LogManager.e(e, TAG, "Internal Android exception in startLeScan()");
}
}
});
}

private void postStopLeScan() {
final BluetoothAdapter bluetoothAdapter = getBluetoothAdapter();
if (bluetoothAdapter == null) {
return;
}
final BluetoothAdapter.LeScanCallback leScanCallback = getLeScanCallback();
mScanHandler.removeCallbacksAndMessages(null);
mScanHandler.post(new Runnable() {
@Override
public void run() {
try {
//noinspection deprecation
bluetoothAdapter.stopLeScan(leScanCallback);
} catch (Exception e) {
LogManager.e(e, TAG, "Internal Android exception in stopLeScan()");
}
}
});
}

private BluetoothAdapter.LeScanCallback getLeScanCallback() {
if (leScanCallback == null) {
leScanCallback =
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@
import android.bluetooth.le.ScanResult;
import android.bluetooth.le.ScanSettings;
import android.content.Context;
import android.os.Build;
import android.os.ParcelUuid;
import android.os.SystemClock;

Expand Down Expand Up @@ -39,20 +38,7 @@ public CycledLeScannerForLollipop(Context context, long scanPeriod, long between

@Override
protected void stopScan() {
try {
if (getScanner() != null) {
try {
getScanner().stopScan((android.bluetooth.le.ScanCallback) getNewLeScanCallback());
}
catch (NullPointerException npe) {
// Necessary because of https://code.google.com/p/android/issues/detail?id=160503
LogManager.e(TAG, "Cannot stop scan. Unexpected NPE.", npe);
}
}
}
catch (IllegalStateException e) {
LogManager.w(TAG, "Cannot stop scan. Bluetooth may be turned off.");
}
postStopLeScan();
}

/*
Expand Down Expand Up @@ -181,21 +167,7 @@ protected void startScan() {
settings = (new ScanSettings.Builder().setScanMode(ScanSettings.SCAN_MODE_LOW_LATENCY)).build();
}

try {
if (getScanner() != null) {
ScanCallback callback = getNewLeScanCallback();
try {
getScanner().startScan(filters, settings, callback);
}
catch (NullPointerException npe) {
// Necessary because of https://code.google.com/p/android/issues/detail?id=160503
LogManager.w(TAG, "Cannot start scan. Unexpected NPE.", npe);
}
}
}
catch (IllegalStateException e) {
LogManager.w(TAG, "Cannot start scan. Bluetooth may be turned off.");
}
postStartLeScan(filters, settings);
}

@Override
Expand All @@ -205,6 +177,50 @@ protected void finishScan() {
mScanningPaused = true;
}

private void postStartLeScan(final List<ScanFilter> filters, final ScanSettings settings) {
final BluetoothLeScanner scanner = getScanner();
if (scanner == null) {
return;
}
final ScanCallback scanCallback = getNewLeScanCallback();
mScanHandler.removeCallbacksAndMessages(null);
mScanHandler.post(new Runnable() {
@Override
public void run() {
try {
scanner.startScan(filters, settings, scanCallback);
} catch (IllegalStateException e) {
LogManager.w(TAG, "Cannot start scan. Bluetooth may be turned off.");
} catch (NullPointerException npe) {
// Necessary because of https://code.google.com/p/android/issues/detail?id=160503
LogManager.e(TAG, "Cannot start scan. Unexpected NPE.", npe);
}
}
});
}

private void postStopLeScan() {
final BluetoothLeScanner scanner = getScanner();
if (scanner == null) {
return;
}
final ScanCallback scanCallback = getNewLeScanCallback();
mScanHandler.removeCallbacksAndMessages(null);
mScanHandler.post(new Runnable() {
@Override
public void run() {
try {
scanner.stopScan(scanCallback);
} catch (IllegalStateException e) {
LogManager.w(TAG, "Cannot stop scan. Bluetooth may be turned off.");
} catch (NullPointerException npe) {
// Necessary because of https://code.google.com/p/android/issues/detail?id=160503
LogManager.e(TAG, "Cannot stop scan. Unexpected NPE.", npe);
}
}
});
}

private BluetoothLeScanner getScanner() {
if (mScanner == null) {
LogManager.d(TAG, "Making new Android L scanner");
Expand Down

0 comments on commit c59223b

Please sign in to comment.