12

Ich habe derzeit eine Methode, die auf die BLE-Geräte schreibt, um es zu piepen. Mein Bluetooth Rückruf geht wie folgt:Bluetooth Gatt Callback funktioniert nicht mit neuer API für Lollipop

ReadCharacteristic rc = new ReadCharacteristic(context, ds.getMacAddress(), serviceUUID, UUID.fromString(myUUID), "") { 
       @Override 
       public void onRead() { 
        Log.w(TAG, "callDevice onRead"); 
        try{Thread.sleep(1000);}catch(InterruptedException ex){} 
        WriteCharacteristic wc = new WriteCharacteristic(activity, context, getMacAddress(), serviceUUID, UUID.fromString(myUUID), ""){ 
         @Override 
         public void onWrite(){ 
          Log.w(TAG, "callDevice onWrite"); 
         } 
         @Override 
         public void onError(){ 
          Log.w(TAG, "callDevice onWrite-onError"); 
         } 
        }; 

//     Store data in writeBuffer 
        wc.writeCharacteristic(writeBuffer); 
       } 

       @Override 
       public void onError(){ 
        Log.w(TAG, "callDevice onRead-onError"); 
       } 
      }; 

      rc.readCharacteristic(); 

Meine ReadCharacteristic Implementierung ist wie folgt:

public class ReadCharacteristic extends BluetoothGattCallback { 
    public ReadCharacteristic(Context context, String macAddress, UUID service, UUID characteristic, Object tag) { 
     mMacAddress = macAddress; 
     mService = service; 
     mCharacteristic = characteristic; 
     mTag = tag; 
     mContext = context; 
     this.activity =activity; 
     final BluetoothManager bluetoothManager = 
       (BluetoothManager) mContext.getSystemService(Context.BLUETOOTH_SERVICE); 
     mBluetoothAdapter = bluetoothManager.getAdapter(); 

    } 

    final private static String TAG = "ReadCharacteristic"; 
    private Object mTag; 
    private String mMacAddress; 
    private UUID mService; 
    private UUID mCharacteristic; 
    private byte[] mValue; 
    private Activity activity; 
    private BluetoothAdapter mBluetoothAdapter; 
    private Context mContext; 

    private int retry = 5; 


    public String getMacAddress() { 
     return mMacAddress; 
    } 

    public UUID getService() { 
     return mService; 
    } 

    public UUID getCharacteristic() { 
     return mCharacteristic; 
    } 

    public byte[] getValue() { return mValue; } 

    public void onRead() { 
     Log.w(TAG, "onRead: " + getDataHex(getValue())); 
    } 

    public void onError() { 
     Log.w(TAG, "onError"); 
    } 

    public void readCharacteristic(){ 
     if (retry == 0) 
     { 
      onError(); 
      return; 
     } 
     retry--; 



       final BluetoothDevice device = mBluetoothAdapter.getRemoteDevice(getMacAddress()); 
       if (device != null) { 
        Log.w(TAG, "Starting Read [" + getService() + "|" + getCharacteristic() + "]"); 
        final ReadCharacteristic rc = ReadCharacteristic.this; 
        device.connectGatt(mContext, false, rc); 
       } 

    } 

    @Override 
    public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) { 
     Log.w(TAG,"onConnectionStateChange [" + status + "|" + newState + "]"); 
     if ((newState == 2)&&(status ==0)) { 
      gatt.discoverServices(); 
     } 

     else{ 
      Log.w(TAG, "[" + status + "]"); 
     // gatt.disconnect(); 
      gatt.close(); 
      try 
      { 
       Thread.sleep(2000); 
      } 
      catch(Exception e) 
      { 

      } 
      readCharacteristic(); 
     } 
    } 

    @Override 
    public void onServicesDiscovered(BluetoothGatt gatt, int status) { 
     Log.w(TAG,"onServicesDiscovered [" + status + "]"); 
     BluetoothGattService bgs = gatt.getService(getService()); 
     if (bgs != null) { 
      BluetoothGattCharacteristic bgc = bgs.getCharacteristic(getCharacteristic()); 
      gatt.readCharacteristic(bgc); 
     } 
    } 

    @Override 
    public void onCharacteristicRead(BluetoothGatt gatt, 
            BluetoothGattCharacteristic characteristic, 
            int status) { 
     Log.w(TAG,"onCharacteristicRead [" + status + "]"); 
     if (status == BluetoothGatt.GATT_SUCCESS) { 
      mValue = characteristic.getValue(); 
      Log.w(TAG,"onCharacteristicRead [" + mValue + "]"); 
      gatt.disconnect(); 
      gatt.close(); 
      onRead(); 
     } 

     else { 
      gatt.disconnect(); 
      gatt.close(); 
     } 
    } 


} 

Dieser Strom Methode funktioniert völlig in Ordnung für Geräte KitKat und unten. Aber wenn ich die gleiche Funktion auf Lollipop ausführe, piept es das Gerät ein paar Mal und hört dann auf zu arbeiten. Von nun an sagt es jedes Mal, wenn ich versuche, eine Verbindung herzustellen, dass das Gerät getrennt ist und gibt mir einen Fehlercode von 257 in der OnConnectionStateChanged-Methode.

ich auch diesen Fehler, wenn ich diese Methode aufrufen -

04-20 14:14:23.503 12329-12384/com.webble.xy W/BluetoothGatt﹕ Unhandled exception in callback 
    java.lang.NullPointerException: Attempt to invoke virtual method 'void android.bluetooth.BluetoothGattCallback.onConnectionStateChange(android.bluetooth.BluetoothGatt, int, int)' on a null object reference 
      at android.bluetooth.BluetoothGatt$1.onClientConnectionState(BluetoothGatt.java:181) 
      at android.bluetooth.IBluetoothGattCallback$Stub.onTransact(IBluetoothGattCallback.java:70) 
      at android.os.Binder.execTransact(Binder.java:446) 

Gibt es jemand, das gleiche Problem konfrontiert? Ich habe das Objekt nie als null erkannt, wenn ich das Debugging probiert habe.

+0

Ich habe das gleiche Problem in meiner App auf> = 5.0. Nichts Neues auf deiner Seite? – Hrk

+3

Ich habe gerade herausgefunden, dass dieser Fehler auftritt, wenn ich disconnect gefolgt von close anrufe, weil das onConnectionStateChange-Objekt vor dem Aufruf von disconnect auf null gesetzt wird. Dieser Fehler wird nicht angezeigt, wenn ich nur kurz anrufe. Aber das hat das Problem nicht gelöst. Ich versuche immer noch, mir den Kopf zu zerbrechen. Es beginnt wieder zu arbeiten, wenn ich Bluetooth neu starte, aber dieses Problem bleibt auch nach einer Weile bestehen. Bitte lassen Sie mich wissen, wenn Sie eine Lösung dafür finden. –

Antwort

-1

Danke für das Zeigen des Problems Shashank.

Ich habe mir die Google example angeschaut und folgte, was sie empfehlen, es funktioniert wie ein Charme mit meinem Gerät.

Sie sollten die close() -Funktion in Ihrem onUnbind und die Verbindung trennen(), wenn Sie zum Beispiel beim Beenden Ihrer Anwendung benötigen.

7

Das Problem wurde an Google als Issue 183108: NullPointerException in BluetoothGatt.java when disconnecting and closing gemeldet.

Eine Abhilfe ist disconnect() zu rufen, wenn Sie BLE Verbindung schließen wollen - und dann auch nur close() im onConnectionStateChange Rückruf zu nennen:

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); 
      } 
     } 
     } 

Hier ist meine vollständige Quellcode (eine Klasse normale Scan tun, gerichtet Scan - und zu entdecken, Dienstleistungen):

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<ScanFilter> 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<ScanFilter>(); 

    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<ScanResult> 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<BluetoothDevice> 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); 
    } 
} 

Auf jedem BLE Ereignisse es Absichten über LocalBroadcastManager sendet.

+0

Gibt es irgendeine Garantie, dass das Aufrufen von disconnect() dazu führt, dass onConnectionStateChange (..., newState = BluetoothProfile.STATE_DISCONNECTED) aufgerufen wird? – swooby