12

Attualmente ho un metodo che scrive sui dispositivi BLE per emetterlo. La mia richiamata Bluetooth è la seguente:BLU Bluetooth Callback non funziona con la nuova API per 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(); 

mia implementazione ReadCharacteristic è la seguente:

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


} 

Questo metodo corrente funziona perfettamente bene per i dispositivi che eseguono KitKat e al di sotto. Ma quando eseguo la stessa funzione su Lollipop, emette un segnale acustico sul dispositivo un paio di volte e poi smette di funzionare. Da allora in poi, ogni volta che provo a connettermi, dice che il dispositivo è disconnesso e mi dà un codice di errore di 257 nel metodo OnConnectionStateChanged.

Inoltre ottengo questo errore ogni volta che io chiamo questo metodo -

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) 

C'è qualcuno che ha affrontato lo stesso problema? Non ho mai incontrato l'oggetto come null quando mai ho provato il debug.

+0

Ho lo stesso problema nella mia app su> = 5.0. Niente di nuovo dalla tua parte? – Hrk

+3

Ho appena capito che questo errore si verifica quando chiamo disconnect seguito da close perché l'oggetto onConnectionStateChange è impostato su null prima di chiamare disconnect. Questo errore non viene visualizzato quando chiamo solo chiudi. Ma questo non ha risolto il problema. Sto ancora cercando di rompere la testa per il problema. Ricomincia a funzionare quando riavvio il Bluetooth, ma ancora una volta questo problema persiste dopo un po '. Per favore fatemi sapere se trovate una soluzione per questo –

risposta

-1

Grazie per aver indicato il problema Shashank.

Ho guardato il Google example e ho seguito quello che loro raccomandano, funziona come un incantesimo con il mio dispositivo.

È necessario chiamare la funzione close() in onUnbind e disconnect() quando è necessario, ad esempio quando si esce dall'applicazione.

7

Il problema è stato segnalato a Google come Issue 183108: NullPointerException in BluetoothGatt.java when disconnecting and closing.

Una soluzione è quella di chiamare disconnect() quando si desidera chiudere la connessione BLE - e quindi chiamare solo close() nel 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); 
      } 
     } 
     } 

Ecco il mio codice sorgente completo (una classe facendo scansione normale, scansione diretta - e servizi scoperta):

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

Su ogni evento BLE trasmette intenti via LocalBroadcastManager.

+0

È garantito che la chiamata a disconnect() comporterà la chiamata a onConnectionStateChange (..., newState = BluetoothProfile.STATE_DISCONNECTED)? – swooby

Problemi correlati