I'm using a thread and handler to achieve a Bluetooth scanning service of certain scanning period and frequency (scan 1s, sleep 1s, scan 1s, etc) by BluetoothLeScanner (actually migrating from BluetoothAdapter). However, as times goes by, I realise it is eating up the memory.
Then I tried to use the profiler to see what happened and I found that there's something looping recursively. I checked my code and could not find the issue. Anyone can help/encountered the same issue?
public class BleDetectionService extends Service {
private static Handler handler;
private static final long SCAN_PERIOD = 1100;
...
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
....
startBLEScanning();
return super.onStartCommand(intent, flags, startId);
}
private void startBLEScanning() {
mLeDeviceAdapter = new LeDeviceAdapter();
mScanning = true;
Toast.makeText(this, "BLE Service sucessfully started", Toast.LENGTH_SHORT).show();
bleScanningThread = new Thread(new Runnable() {
@Override
public void run() {
Looper.prepare();
while (mScanning) {
scanLeDevice(true);
}
}
}, "BLE Scanning Thread");
bleScanningThread.start();
}
private void scanLeDevice(final boolean enable) {
if (enable) {
handler = new Handler();
handler.postDelayed(new Runnable() {
@Override
public void run() {
bluetoothLeScanner.stopScan(scanCallback);
}
}, SCAN_PERIOD);
// filter
ScanFilter.Builder builder = new ScanFilter.Builder();
ScanFilter filter = builder.build();
ArrayList<ScanFilter> filters = new ArrayList<>();
filters.add(filter);
// scansetting
ScanSettings.Builder settingsBuilder = new ScanSettings.Builder();
settingsBuilder.setScanMode(ScanSettings.SCAN_MODE_BALANCED);
settingsBuilder.setReportDelay(0);
ScanSettings settings = settingsBuilder.build();
bluetoothLeScanner.startScan(filters, settings, scanCallback);
} else {
bluetoothLeScanner.stopScan(scanCallback);
}
}
}
EDIT: I have removed the thread and handler as I had mixed up the usage of BluetoothLeScanner with BluetoothAdapter. The scanLeDevice() is now called from main thread as suggested by @greeble31
Consider the following:
scanLeDevice()
is called in a never-ending loop, and always with a parameter of true
.
scanLeDevice()
returns almost immediately (1ms or less).
Each invocation of scanLeDevice()
creates a new Handler
, and a new anonymous Runnable
. The binding of the Runnable
to the Handler
involves a Message
(one for each call to postDelayed()
).
You have omitted the definition of SCAN_PERIOD
from your post, but I'm going to take an educated guess, and say it's 60000
(1 minute). That means each new Handler
must exist for 1 minute, because it needs to hold a Message
for 1 minute, because it is tracking a Runnable
which needs be executed when that minute expires.
In other words: You are calling scanLeDevice()
roughly 1000 times per second, and each call creates 3 interrelated objects that must exist for at least 1 minute.
Your architectural mistake was probably including bleScanningThread
. scanLeDevice()
can be safely called on the main thread. It need only be called once. It will return immediately, but the scan will still be conducted (by the OS), in the background. ScanCallback
will execute when appropriate, even though you have not supplied an extra thread. The scan will cease when your postDelayed()
Runnable
executes; this is probably what you intended from the beginning.