BLuetooth Gatt Callback not working with new API for Lollipop

前端 未结 2 716
太阳男子
太阳男子 2021-02-19 15:23

I currently have a method which writes to the BLE devices to beep it. My Bluetooth Callback goes as follows :

ReadCharacteristic rc = new ReadCharacteristic(con         


        
2条回答
  •  生来不讨喜
    2021-02-19 15:30

    The problem has been reported to Google as Issue 183108: NullPointerException in BluetoothGatt.java when disconnecting and closing.

    A workaround is to call disconnect() when you want to close BLE connection - and then only call close() in the onConnectionStateChange callback:

      public void shutdown() {
        try {
          mBluetoothGatt.disconnect();
        } catch (Exception e) {
          Log.d(TAG, "disconnect ignoring: " + e);
        }
      }
    
      private final BluetoothGattCallback mGattCallback = 
       new BluetoothGattCallback() {
        @Override
          public void onConnectionStateChange(BluetoothGatt gatt, 
           int status, int newState) {
            if (newState == BluetoothProfile.STATE_CONNECTED) {
    
            } else if (newState == BluetoothProfile.STATE_DISCONNECTED) {
    
              try {
                gatt.close();
              } catch (Exception e) {
                Log.d(TAG, "close ignoring: " + e);
              }
            }
          }
    

    Here is my full source code (a class doing normal scan, directed scan - and discovering services):

    public class BleObject {
    
      public static final String ACTION_BLUETOOTH_ENABLED   = "action.bluetooth.enabled";
      public static final String ACTION_BLUETOOTH_DISABLED  = "action.bluetooth.disabled";
      public static final String ACTION_DEVICE_FOUND        = "action.device.found";
      public static final String ACTION_DEVICE_BONDED       = "action.device.bonded";
      public static final String ACTION_DEVICE_CONNECTED    = "action.device.connected";
      public static final String ACTION_DEVICE_DISCONNECTED = "action.device.disconnected";
      public static final String ACTION_POSITION_READ       = "action.position.read";
    
      public static final String EXTRA_BLUETOOTH_DEVICE     = "extra.bluetooth.device";
      public static final String EXTRA_BLUETOOTH_RSSI       = "extra.bluetooth.rssi";
    
      private Context mContext;
      private IntentFilter mIntentFilter;
      private LocalBroadcastManager mBroadcastManager;
      private BluetoothAdapter mBluetoothAdapter;
      private BluetoothGatt mBluetoothGatt;
      private BluetoothLeScanner mScanner;
      private ScanSettings mSettings;
      private List mScanFilters;
    
      private Handler mConnectHandler;
    
      public BleObject(Context context) {
        mContext = context;
    
        if (!context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_BLUETOOTH_LE)) {
          Log.d(TAG, "BLE not supported");
          return;
        }
    
        BluetoothManager bluetoothManager = (BluetoothManager) context.getSystemService(Context.BLUETOOTH_SERVICE);
        mBluetoothAdapter = bluetoothManager.getAdapter();
        if (mBluetoothAdapter == null) {
          Log.d(TAG, "BLE not accessible");
          return;
        }
    
        mIntentFilter = new IntentFilter();
        mIntentFilter.addAction(BluetoothAdapter.ACTION_STATE_CHANGED);
        mIntentFilter.addAction(BluetoothDevice.ACTION_BOND_STATE_CHANGED);
    
        mSettings = new ScanSettings.Builder().setScanMode(ScanSettings.SCAN_MODE_LOW_LATENCY).build();
        mScanFilters = new ArrayList();
    
        mConnectHandler = new Handler();
    
        mBroadcastManager = LocalBroadcastManager.getInstance(context);
      }
    
      public boolean isEnabled() {
        return (mBluetoothAdapter != null && mBluetoothAdapter.isEnabled());
      }
    
      private ScanCallback mScanCallback = new ScanCallback() {
        @Override
          public void onScanResult(int callbackType, ScanResult result) {
            processResult(result);
          }
    
        @Override
          public void onBatchScanResults(List results) {
            for (ScanResult result: results) {
              processResult(result);
            }
          }
    
        private void processResult(ScanResult result) {
          if (result == null)
            return;
    
          BluetoothDevice device = result.getDevice();
          if (device == null)
            return;
    
          Intent i = new Intent(Utils.ACTION_DEVICE_FOUND);
          i.putExtra(Utils.EXTRA_BLUETOOTH_DEVICE, device);
          i.putExtra(Utils.EXTRA_BLUETOOTH_RSSI, result.getRssi());
          mBroadcastManager.sendBroadcast(i);
        }
      };
    
      private final BluetoothGattCallback mGattCallback = new BluetoothGattCallback() {
        @Override
          public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) {
            if (newState == BluetoothProfile.STATE_CONNECTED) {
              if (gatt == null)
                return;
    
              BluetoothDevice device = gatt.getDevice();
              if (device == null)
                return;
    
              Log.d(TAG, "BluetoothProfile.STATE_CONNECTED: " + device);
              Intent i = new Intent(Utils.ACTION_DEVICE_CONNECTED);
              i.putExtra(Utils.EXTRA_BLUETOOTH_DEVICE, device);
              mBroadcastManager.sendBroadcast(i);
    
            } else if (newState == BluetoothProfile.STATE_DISCONNECTED) {
    
              Log.d(TAG, "BluetoothProfile.STATE_DISCONNECTED");
              Intent i = new Intent(Utils.ACTION_DEVICE_DISCONNECTED);
              mBroadcastManager.sendBroadcast(i);
    
              // Issue 183108: https://code.google.com/p/android/issues/detail?id=183108
              try {
                gatt.close();
              } catch (Exception e) {
                Log.d(TAG, "close ignoring: " + e);
              }
            }
          }
    
        @Override
          public void onServicesDiscovered(BluetoothGatt gatt, int status) {
            if (gatt == null)
              return;
    
            for (BluetoothGattService service: gatt.getServices()) {
              Log.d(TAG, "service: " + service.getUuid());
    
              for (BluetoothGattCharacteristic chr: service.getCharacteristics()) {
                Log.d(TAG, "char: " + chr.getUuid());
              }
            }
          }
      };
    
      private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
        public void onReceive(Context context, Intent intent) {
          final String action = intent.getAction();
    
          if (BluetoothAdapter.ACTION_STATE_CHANGED.equals(action)) {
            final int state = intent.getIntExtra(BluetoothAdapter.EXTRA_STATE, BluetoothAdapter.ERROR);
    
            switch (state) {
              case BluetoothAdapter.STATE_TURNING_OFF: {
                                                         Log.d(TAG, "BluetoothAdapter.STATE_TURNING_OFF");
                                                         break;
                                                       }
              case BluetoothAdapter.STATE_OFF: {
                                                 Log.d(TAG, "BluetoothAdapter.STATE_OFF");
                                                 Intent i = new Intent(Utils.ACTION_BLUETOOTH_DISABLED);
                                                 mBroadcastManager.sendBroadcast(i);
                                                 break;
                                               }
              case BluetoothAdapter.STATE_TURNING_ON: {
                                                        Log.d(TAG, "BluetoothAdapter.STATE_TURNING_ON");
                                                        break;
                                                      }
              case BluetoothAdapter.STATE_ON: {
                                                Log.d(TAG, "BluetoothAdapter.STATE_ON");
                                                Intent i = new Intent(Utils.ACTION_BLUETOOTH_ENABLED);
                                                mBroadcastManager.sendBroadcast(i);
                                                break;
                                              }
            }
          } else if (BluetoothDevice.ACTION_BOND_STATE_CHANGED.equals(action)) {
            final int state     = intent.getIntExtra(BluetoothDevice.EXTRA_BOND_STATE, BluetoothDevice.ERROR);
            final int prevState = intent.getIntExtra(BluetoothDevice.EXTRA_PREVIOUS_BOND_STATE, BluetoothDevice.ERROR);
    
            if (state == BluetoothDevice.BOND_BONDED &&
                prevState == BluetoothDevice.BOND_BONDING) {
    
              if (mBluetoothGatt != null) {
                BluetoothDevice device = mBluetoothGatt.getDevice();
                if (device == null)
                  return;
    
                Intent i = new Intent(Utils.ACTION_DEVICE_BONDED);
                i.putExtra(Utils.EXTRA_BLUETOOTH_DEVICE, device);
                mBroadcastManager.sendBroadcast(i);
              }
            }
          }
        }
      };
    
      // scan for all BLE devices nearby
      public void startScanning() {
        Log.d(TAG, "startScanning");
    
        mScanFilters.clear();
        // create the scanner here, rather than in init() -
        // because otherwise app crashes when Bluetooth is switched on
        mScanner = mBluetoothAdapter.getBluetoothLeScanner();
        mScanner.startScan(mScanFilters, mSettings, mScanCallback);
      }
    
      // scan for a certain BLE device and after delay
      public void startScanning(final String address) {
        Log.d(TAG, "startScanning for " + address);
    
        mScanFilters.clear();
        mScanFilters.add(new ScanFilter.Builder().setDeviceAddress(address).build());
        // create the scanner here, rather than in init() -
        // because otherwise app crashes when Bluetooth is switched on
        mScanner = mBluetoothAdapter.getBluetoothLeScanner();
        mScanner.startScan(mScanFilters, mSettings, mScanCallback);
      }
    
      public void stopScanning() {
        Log.d(TAG, "stopScanning");
    
        if (mScanner != null) {
          mScanner.stopScan(mScanCallback);
          mScanner = null;
        }
    
        mScanFilters.clear();
      }
    
      public void connect(final BluetoothDevice device) {
        Log.d(TAG, "connect: " + device.getAddress() + ", mBluetoothGatt: " + mBluetoothGatt);
    
        mConnectHandler.post(new Runnable() {
            @Override
            public void run() {
            setPin(device, Utils.PIN);
            mBluetoothGatt = device.connectGatt(mContext, true, mGattCallback);
            }
            });
      }
    
      private void setPin(BluetoothDevice device, String pin) {
        if (device == null || pin == null || pin.length() < 4)
          return;
    
        try {
          device.setPin(pin.getBytes("UTF8"));
        } catch (Exception e) {
          Utils.logw("setPin ignoring: " + e);
        }
      }
    
      // called on successful device connection and will toggle reading coordinates
      public void discoverServices() {
        if (mBluetoothGatt != null)
          mBluetoothGatt.discoverServices();
      }
    
      public boolean isBonded(BluetoothDevice device) {
        Set bondedDevices = mBluetoothAdapter.getBondedDevices();
        if (bondedDevices == null || bondedDevices.size() == 0)
          return false;
    
        for (BluetoothDevice bondedDevice: bondedDevices) {
          Log.d(TAG, "isBonded bondedDevice: " + bondedDevice);
    
          if (bondedDevice.equals(device)) {
            Log.d(TAG, "Found bonded device: " + device);
            return true;
          }
        }
    
        return false;
      }
    
      public void startup() {
        try {
          mContext.registerReceiver(mReceiver, mIntentFilter);
        } catch (Exception e) {
          Log.d(TAG, "registerReceiver ignoring: " + e);
        }
      }
    
      public void shutdown() {
        Log.d(TAG, "BleObject shutdown");
    
        try {
          mContext.unregisterReceiver(mReceiver);
        } catch (Exception e) {
          Log.d(TAG, "unregisterReceiver ignoring: " + e);
        }
    
        try {
          stopScanning();
        } catch (Exception e) {
          Log.d(TAG, "stopScanning ignoring: " + e);
        }
    
        try {
          mBluetoothGatt.disconnect();
        } catch (Exception e) {
          Log.d(TAG, "disconnect ignoring: " + e);
        }
    
        mConnectHandler.removeCallbacksAndMessages(null);
      }
    }
    

    On each BLE event it broadcasts intents via LocalBroadcastManager.

提交回复
热议问题