Skip to content

Commit 158ac20

Browse files
authored
Merge pull request #430 from marcosalis/Issue136_ScanBlocksUIThread
Start and stop BLE scans from a background thread to prevent blocking the UI (issue #136)
2 parents b3ac622 + 4d72e38 commit 158ac20

4 files changed

Lines changed: 104 additions & 44 deletions

File tree

src/main/java/org/altbeacon/beacon/service/BeaconService.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -273,6 +273,7 @@ public void onDestroy() {
273273
LogManager.i(TAG, "onDestroy called. stopping scanning");
274274
handler.removeCallbacksAndMessages(null);
275275
mCycledScanner.stop();
276+
mCycledScanner.destroy();
276277
monitoringStatus.stopStatusPreservation();
277278
}
278279

src/main/java/org/altbeacon/beacon/service/scanner/CycledLeScanner.java

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@
1212
import android.content.pm.PackageManager;
1313
import android.os.Build;
1414
import android.os.Handler;
15+
import android.os.HandlerThread;
16+
import android.os.Looper;
1517
import android.os.SystemClock;
1618

1719
import org.altbeacon.beacon.BeaconManager;
@@ -40,7 +42,10 @@ public abstract class CycledLeScanner {
4042
private long mScanPeriod;
4143

4244
protected long mBetweenScanPeriod;
43-
protected final Handler mHandler = new Handler();
45+
46+
protected final Handler mHandler = new Handler(Looper.getMainLooper());
47+
protected final Handler mScanHandler;
48+
private final HandlerThread mScanThread;
4449

4550
protected final BluetoothCrashResolver mBluetoothCrashResolver;
4651
protected final CycledLeScanCallback mCycledLeScanCallback;
@@ -57,6 +62,10 @@ protected CycledLeScanner(Context context, long scanPeriod, long betweenScanPeri
5762
mCycledLeScanCallback = cycledLeScanCallback;
5863
mBluetoothCrashResolver = crashResolver;
5964
mBackgroundFlag = backgroundFlag;
65+
66+
mScanThread = new HandlerThread("CycledLeScannerThread");
67+
mScanThread.start();
68+
mScanHandler = new Handler(mScanThread.getLooper());
6069
}
6170

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

168+
public void destroy() {
169+
mScanThread.quit();
170+
}
171+
159172
protected abstract void stopScan();
160173

161174
protected abstract boolean deferScanIfNeeded();

src/main/java/org/altbeacon/beacon/service/scanner/CycledLeScannerForJellyBeanMr2.java

Lines changed: 43 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -18,17 +18,9 @@ public CycledLeScannerForJellyBeanMr2(Context context, long scanPeriod, long bet
1818
super(context, scanPeriod, betweenScanPeriod, backgroundFlag, cycledLeScanCallback, crashResolver);
1919
}
2020

21-
@SuppressWarnings("deprecation")
2221
@Override
2322
protected void stopScan() {
24-
try {
25-
BluetoothAdapter bluetoothAdapter = getBluetoothAdapter();
26-
if (bluetoothAdapter != null) {
27-
bluetoothAdapter.stopLeScan(getLeScanCallback());
28-
}
29-
} catch (Exception e) {
30-
LogManager.e(e, TAG, "Internal Android exception scanning for beacons");
31-
}
23+
postStopLeScan();
3224
}
3325

3426
@Override
@@ -54,19 +46,57 @@ public void run() {
5446
return false;
5547
}
5648

57-
@SuppressWarnings("deprecation")
5849
@Override
5950
protected void startScan() {
60-
getBluetoothAdapter().startLeScan(getLeScanCallback());
51+
postStartLeScan();
6152
}
6253

63-
@SuppressWarnings("deprecation")
6454
@Override
6555
protected void finishScan() {
66-
getBluetoothAdapter().stopLeScan(getLeScanCallback());
56+
postStopLeScan();
6757
mScanningPaused = true;
6858
}
6959

60+
private void postStartLeScan() {
61+
final BluetoothAdapter bluetoothAdapter = getBluetoothAdapter();
62+
if (bluetoothAdapter == null) {
63+
return;
64+
}
65+
final BluetoothAdapter.LeScanCallback leScanCallback = getLeScanCallback();
66+
mScanHandler.removeCallbacksAndMessages(null);
67+
mScanHandler.post(new Runnable() {
68+
@Override
69+
public void run() {
70+
try {
71+
//noinspection deprecation
72+
bluetoothAdapter.startLeScan(leScanCallback);
73+
} catch (Exception e) {
74+
LogManager.e(e, TAG, "Internal Android exception in startLeScan()");
75+
}
76+
}
77+
});
78+
}
79+
80+
private void postStopLeScan() {
81+
final BluetoothAdapter bluetoothAdapter = getBluetoothAdapter();
82+
if (bluetoothAdapter == null) {
83+
return;
84+
}
85+
final BluetoothAdapter.LeScanCallback leScanCallback = getLeScanCallback();
86+
mScanHandler.removeCallbacksAndMessages(null);
87+
mScanHandler.post(new Runnable() {
88+
@Override
89+
public void run() {
90+
try {
91+
//noinspection deprecation
92+
bluetoothAdapter.stopLeScan(leScanCallback);
93+
} catch (Exception e) {
94+
LogManager.e(e, TAG, "Internal Android exception in stopLeScan()");
95+
}
96+
}
97+
});
98+
}
99+
70100
private BluetoothAdapter.LeScanCallback getLeScanCallback() {
71101
if (leScanCallback == null) {
72102
leScanCallback =

src/main/java/org/altbeacon/beacon/service/scanner/CycledLeScannerForLollipop.java

Lines changed: 46 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@
88
import android.bluetooth.le.ScanResult;
99
import android.bluetooth.le.ScanSettings;
1010
import android.content.Context;
11-
import android.os.Build;
1211
import android.os.ParcelUuid;
1312
import android.os.SystemClock;
1413

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

4039
@Override
4140
protected void stopScan() {
42-
try {
43-
if (getScanner() != null) {
44-
try {
45-
getScanner().stopScan((android.bluetooth.le.ScanCallback) getNewLeScanCallback());
46-
}
47-
catch (NullPointerException npe) {
48-
// Necessary because of https://code.google.com/p/android/issues/detail?id=160503
49-
LogManager.e(TAG, "Cannot stop scan. Unexpected NPE.", npe);
50-
}
51-
}
52-
}
53-
catch (IllegalStateException e) {
54-
LogManager.w(TAG, "Cannot stop scan. Bluetooth may be turned off.");
55-
}
41+
postStopLeScan();
5642
}
5743

5844
/*
@@ -181,21 +167,7 @@ protected void startScan() {
181167
settings = (new ScanSettings.Builder().setScanMode(ScanSettings.SCAN_MODE_LOW_LATENCY)).build();
182168
}
183169

184-
try {
185-
if (getScanner() != null) {
186-
ScanCallback callback = getNewLeScanCallback();
187-
try {
188-
getScanner().startScan(filters, settings, callback);
189-
}
190-
catch (NullPointerException npe) {
191-
// Necessary because of https://code.google.com/p/android/issues/detail?id=160503
192-
LogManager.w(TAG, "Cannot start scan. Unexpected NPE.", npe);
193-
}
194-
}
195-
}
196-
catch (IllegalStateException e) {
197-
LogManager.w(TAG, "Cannot start scan. Bluetooth may be turned off.");
198-
}
170+
postStartLeScan(filters, settings);
199171
}
200172

201173
@Override
@@ -205,6 +177,50 @@ protected void finishScan() {
205177
mScanningPaused = true;
206178
}
207179

180+
private void postStartLeScan(final List<ScanFilter> filters, final ScanSettings settings) {
181+
final BluetoothLeScanner scanner = getScanner();
182+
if (scanner == null) {
183+
return;
184+
}
185+
final ScanCallback scanCallback = getNewLeScanCallback();
186+
mScanHandler.removeCallbacksAndMessages(null);
187+
mScanHandler.post(new Runnable() {
188+
@Override
189+
public void run() {
190+
try {
191+
scanner.startScan(filters, settings, scanCallback);
192+
} catch (IllegalStateException e) {
193+
LogManager.w(TAG, "Cannot start scan. Bluetooth may be turned off.");
194+
} catch (NullPointerException npe) {
195+
// Necessary because of https://code.google.com/p/android/issues/detail?id=160503
196+
LogManager.e(TAG, "Cannot start scan. Unexpected NPE.", npe);
197+
}
198+
}
199+
});
200+
}
201+
202+
private void postStopLeScan() {
203+
final BluetoothLeScanner scanner = getScanner();
204+
if (scanner == null) {
205+
return;
206+
}
207+
final ScanCallback scanCallback = getNewLeScanCallback();
208+
mScanHandler.removeCallbacksAndMessages(null);
209+
mScanHandler.post(new Runnable() {
210+
@Override
211+
public void run() {
212+
try {
213+
scanner.stopScan(scanCallback);
214+
} catch (IllegalStateException e) {
215+
LogManager.w(TAG, "Cannot stop scan. Bluetooth may be turned off.");
216+
} catch (NullPointerException npe) {
217+
// Necessary because of https://code.google.com/p/android/issues/detail?id=160503
218+
LogManager.e(TAG, "Cannot stop scan. Unexpected NPE.", npe);
219+
}
220+
}
221+
});
222+
}
223+
208224
private BluetoothLeScanner getScanner() {
209225
if (mScanner == null) {
210226
LogManager.d(TAG, "Making new Android L scanner");

0 commit comments

Comments
 (0)