1

The error message I see is:

Attempt to invoke virtual method 'void android.bluetooth.le.BluetoothLeScanner.startScan(java.util.List, android.bluetooth.le.ScanSettings, android.bluetooth.le.ScanCallback)' on a null object reference at com.example.drodo.diamondbeacons.BleDevicesActivity.startScanning(BleDevicesActivity.java:91) at com.example.drodo.diamondbeacons.BleDevicesActivity$1.onClick(BleDevicesActivity.java:68). Error in line: mBluetoothLeScanner.startScan(buildScanFilters(), buildScanSettings(), mScanCallback);

Here's my code:

public class BleDevicesActivity extends AppCompatActivity {

private Toolbar bleToolbar;

private static final String TAG = BleDevicesActivity.class.getName();

private static final long SCAN_PERIOD = 5000;

private BluetoothAdapter mBluetoothAdapter;
private BluetoothLeScanner mBluetoothLeScanner;
private ScanCallback mScanCallback;
private TextView deviceName;
private TextView deviceAddress;
private Button scanDevicesBtn;
private Handler mHandler;

public void setBluetoothAdapter(BluetoothAdapter btAdapter) {
    this.mBluetoothAdapter = btAdapter;
    mBluetoothLeScanner = mBluetoothAdapter.getBluetoothLeScanner();
}

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_ble_devices);

    mHandler = new Handler();

    bleToolbar = (Toolbar) findViewById(R.id.ble_toolbar);
    setSupportActionBar(bleToolbar);
    getSupportActionBar().setTitle("Ble Devices Nearby");
    getSupportActionBar().setDisplayHomeAsUpEnabled(true);

    deviceName = (TextView) findViewById(R.id.device_name_text);
    deviceAddress = (TextView) findViewById(R.id.device_address_text);
    scanDevicesBtn = (Button) findViewById(R.id.scan_devices_btn);

    scanDevicesBtn.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            startScanning();

            Snackbar.make(v, "Scanning for BLE Devices Nearby", Snackbar.LENGTH_SHORT)
                    .setAction("Action", null).show();
        }
    });
}

public void startScanning() {
    if (mScanCallback == null) {
        Log.d(TAG, "Starting Scanning");

        // Will stop the scanning after a set time.
        mHandler.postDelayed(new Runnable() {
            @Override
            public void run() {
                stopScanning();
                Toast.makeText(BleDevicesActivity.this, "Scanning Stopped", Toast.LENGTH_SHORT).show();
            }
        }, SCAN_PERIOD);

        // Kick off a new scan.
        mScanCallback = new SampleScanCallback();
        mBluetoothLeScanner.startScan(buildScanFilters(), buildScanSettings(), mScanCallback);

        String toastText = getString(R.string.scan_start_toast) + " "
                + TimeUnit.SECONDS.convert(SCAN_PERIOD, TimeUnit.MILLISECONDS) + " "
                + getString(R.string.seconds);
        Toast.makeText(BleDevicesActivity.this, toastText, Toast.LENGTH_LONG).show();
    } else {
        Toast.makeText(BleDevicesActivity.this, R.string.already_scanning, Toast.LENGTH_SHORT).show();
    }
}

/**
 * Stop scanning for BLE Advertisements.
 */
public void stopScanning() {
    Log.d(TAG, "Stopping Scanning");

    // Stop the scan, wipe the callback.
    mBluetoothLeScanner.stopScan(mScanCallback);
    mScanCallback = null;

    // Even if no new results, update 'last seen' times.
    //amAdapter.notifyDataSetChanged();
}

/**
 * Return a List of {@link ScanFilter} objects to filter by Service UUID.
 */
private List<ScanFilter> buildScanFilters() {
    List<ScanFilter> scanFilters = new ArrayList<>();

    ScanFilter.Builder builder = new ScanFilter.Builder();
    builder.setServiceUuid(BleConstants.Service_UUID);
    scanFilters.add(builder.build());

    return scanFilters;
}

/**
 * Return a {@link ScanSettings} object set to use low power (to preserve battery life).
 */
private ScanSettings buildScanSettings() {
    ScanSettings.Builder builder = new ScanSettings.Builder();
    builder.setScanMode(ScanSettings.SCAN_MODE_LOW_POWER);
    return builder.build();
}

/**
 * Custom ScanCallback object - adds to adapter on success, displays error on failure.
 */
private class SampleScanCallback extends ScanCallback {

    @Override
    public void onBatchScanResults(List<ScanResult> results) {
        super.onBatchScanResults(results);

        for (ScanResult result : results) {
            String device_name = result.getDevice().getName();
            String device_address = result.getDevice().getAddress();

            deviceName.setText(device_name);
            deviceAddress.setText(device_address);
        }

    }

    @Override
    public void onScanResult(int callbackType, ScanResult result) {
        super.onScanResult(callbackType, result);
        try {
            ScanRecord scanRecord = result.getScanRecord();

            assert scanRecord != null;
            byte[] manufacturerData = scanRecord.getManufacturerSpecificData(0x0590);

            assert manufacturerData != null;

            double lightValue = Double.parseDouble(Arrays.toString(new byte[]{manufacturerData[0]}).replace("[", "").replace("]", ""));
            double tempValue = Double.parseDouble(Arrays.toString(new byte[]{manufacturerData[1]}).replace("[", "").replace("]", ""));
            double batteryValue = Integer.parseInt(Arrays.toString(new byte[]{manufacturerData[2]}).replace("[", "").replace("]", ""));
            int alertPresses = Integer.parseInt(Arrays.toString(new byte[]{manufacturerData[3]}).replace("[", "").replace("]", ""));

            Toast.makeText(BleDevicesActivity.this, "LIGHT: " + lightValue + " %", Toast.LENGTH_SHORT).show();
            Toast.makeText(BleDevicesActivity.this, "TEMPERATURE: " + tempValue + " \u2103", Toast.LENGTH_SHORT).show();
            Toast.makeText(BleDevicesActivity.this, "BATTERY LEVEL: " + batteryValue + " Volts", Toast.LENGTH_SHORT).show();
            Toast.makeText(BleDevicesActivity.this, "ALERT PRESSES: " + alertPresses, Toast.LENGTH_SHORT).show();

        } catch (Exception e) {

            String error_message = e.getMessage();
            Toast.makeText(BleDevicesActivity.this, "Error: " + error_message, Toast.LENGTH_SHORT).show();
        }

    }

    @Override
    public void onScanFailed(int errorCode) {
        super.onScanFailed(errorCode);
        Toast.makeText(BleDevicesActivity.this, "Scan failed with error: " + errorCode, Toast.LENGTH_LONG)
                .show();
    }
}
}

What could be causing the crash?

stkent
  • 19,772
  • 14
  • 85
  • 111
diamantis
  • 21
  • 1
  • 8

1 Answers1

2

This line:

mBluetoothLeScanner = mBluetoothAdapter.getBluetoothLeScanner();

is initializing your BluetoothLeScanner instance to null.

The method getBluetoothLeScanner returns non-null only when Bluetooth is actually switched on on your device. As a result, you should call getBluetoothLeScanner each time you need to use the scanner (rather than storing a reference), and aggressively check the result is not null before using it. If the result is null, you probably need to prompt the user to enable Bluetooth before continuing.

In your specific case, you should:

  • remove the mBluetoothLeScanner field;
  • replace the line mBluetoothLeScanner.startScan(buildScanFilters(), buildScanSettings(), mScanCallback); with something like

    BluetoothLeScanner scanner = mBluetoothAdapter.getBluetoothLeScanner();
    if (scanner != null) {
        scanner.startScan(buildScanFilters(), buildScanSettings(), mScanCallback);
    } else {
        // Device Bluetooth is disabled; check and prompt user to enable.
    }
    
  • replace the line mBluetoothLeScanner.stopScan(mScanCallback); with something like

    BluetoothLeScanner scanner = mBluetoothAdapter.getBluetoothLeScanner();
    if (scanner != null) {
        scanner.stopScan(mScanCallback);
    } else {
        // Device Bluetooth is disabled; scanning should already be stopped, nothing to do here.
    }
    
stkent
  • 19,772
  • 14
  • 85
  • 111
  • What I would change to my code then? Thanks in advance! – diamantis Apr 24 '18 at 16:37
  • Updated with some concrete suggestions. Showing how to check Bluetooth state and prompt the user to enable is out of scope for this question, but here are [some slides that should point you in the right direction](https://speakerdeck.com/stkent/bluetooth-low-energy-on-android-top-tips-for-the-tricky-bits-v4-codemash?slide=28) :) – stkent Apr 24 '18 at 16:43