5 Tips for Bluetooth Low Energy (BLE) on Android

Using Bluetooth Low Energy (or short BLE) to communicate between smartphones and other devices has many benefits. However if your job is to implement this communication on Android chances are high that some stuff just won’t work (tm).

The last two years we have done many BLE-enabled apps for our customers and here are a couple of things we learned.

  • The BLE stack on iOS is generally more stable and reliable. If you think the peripheral might be buggy try to use an iOS device to counter-check.
  • BLE on Android 4.3 and 4.4 is really really buggy. If possible target Android >= 5.0. Really. On Android 4.4 we often were able to crash the Bluetooth stack so hard that we had to reboot the phone to get it working again.
  • BLE on Android 6.0 has really improved. Also the new scanner introduced in 5.0 is way better then the old one. So there is hope :)

Now on to the specifics.

  • The BLE-API is asynchronous. You may call BluetoothGatt#readCharacteristic() and immediately after that do another read. You might expect that there is a queue and that eventually you will receive two callbacks one for each read. Well, that won’t happen. You always have to wait for one GATT operation to finish before you can start another one. Otherwise the result is undefined. Use a state machine - it will save you a lot of trouble. Note: You won’t receive an Exception if you do several GATT operations in parallel.
  • Always make sure your connections are closed if no longer needed. Use BluetoothGatt#close() for that. Otherwise your app might work some time but after reaching a device specific connection limit stuff will get flaky. Same as above: You won’t get an exception if you hit that limit - new connections will simply fail without proper error message.
  • Filtering for specific devices while scanning is totally broken in Android < 5.0. Even with Android 5.0 it doesn’t work for all filter criteria. So filter manually and later carefully optimize on real devices.
  • Doing BluetoothGatt#writeCharacteristic() with more then 20 Bytes payload won’t work with all devices. To be on the save side only write chunks of 20 Bytes. Again use a state machine for that.
  • Do all calls to the BLE stack on the main thread or you might get unexpected behavior. Especially don’t do calls from inside the onLeScan() callback. The calls are fast and shouldn’t have a negative effect on UI performance if done on the main thread.

That’s all for now. If you have other tips, please leave them inside the comments.