There are two kinds of Bluetooth development in Android, one is traditional Bluetooth and the other is low power Bluetooth, they are completely different, you have to figure out which one you need to develop before you develop it, you can’t even connect your device to Bluetooth using the traditional way of low power Bluetooth development, don’t ask me why I know, Bluetooth Low Energy Bluetooth Low Energy (BLE) has been introduced in Android4.3 (API level 18) to support Bluetooth Low Energy for health management devices such as sports bracelets and electronic blood pressure monitors. That is to say, the premise of development is that the mobile device supports BLE and the system is above Android4.3, and the bluetooth device that communicates with the mobile phone is bluetooth with low power consumption. If you look at the examples in the official documentation, you will see that the Handler is used to communicate asynchronously with the broadcast. I wrote about bluetooth in the company project as well. Now with RxJava, we can write better (zhaung), hence this article.
If you don’t already know about RxJava, you can read RxJava for Android developers. You can read the official documentation for low-power Bluetooth development or read this article
The overall train of thought
Assuming you already have a BLE enabled phone and a low-power Bluetooth module for communication, follow these steps to get started:
- Bluetooth permissions
- Set and enable bluetooth on your phone
- Find bluetooth Devices
- Connect to Bluetooth service
- Bluetooth communication
The detailed steps
Setting Bluetooth Permission
Declare Bluetooth permissions in the manifest file of your application
<uses-permission android:name="android.permission.BLUETOOTH"/>
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN"/>Copy the code
In addition we can through the PackageManager. HasSystemFeature () method to determine whether the current mobile devices support BLE.
Set up and enable bluetooth device of mobile phone
First of all, BluetoothManager to get the only BluetoothAdapter in the phone (bluetooth send receiver)BluetoothAdapter object. BluetoothAdapter then enabled the bluetooth device of the mobile phone, the code is as follows:
public void initBle(Context context) {
this.mContext = context.getApplicationContext();
BluetoothManager bluetoothManager =
(BluetoothManager) this.mContext.getSystemService(Context.BLUETOOTH_SERVICE);
mBleAdapter = bluetoothManager.getAdapter();
if(mBleAdapter ! =null) {
mBleAdapter.enable();// Enable Bluetooth directly without popping the dialog box}}Copy the code
It is worth mentioning that I simply encapsulated bluetooth functions into a tool class, using the singleton mode of static inner classes. In order to prevent memory leaks, I passed a reference to the Application Context object when initializing Bluetooth
Find bluetooth Devices
A scanBleDevices(Boolean enable) method is defined to enable and disable scanning. Here using the BluetoothAdapter. StartLeScan open scan (LeScanCallback) method, need to pass in a scanning callback:
private BluetoothAdapter.LeScanCallback mBleScanCallback = new BluetoothAdapter.LeScanCallback() {
@Override
public void onLeScan(BluetoothDevice bleDevice, int rssi, byte[] scanRecord) {
if (mIsScanning) {
Log.d(TAG, "OnLeScan: Find the device" + bleDevice.getName());
if (mTargetDeviceName.equals(bleDevice.getName())) {
connectDevice(bleDevice);// Connect bluetooth device}}else {
Log.d(TAG, "OnLeScan: Stop scanning"); }}};Copy the code
This callback is used to close the bluetooth scanning method BluetoothAdapter. StopLeScan (LeScanCallback), so define a Boolean variable mIsScanning judgment bluetooth scanning turned on and off. Having said that, it’s not like RxJava has made its debut yet. During the scanning process, we need to limit the timeout time of Bluetooth scanning, so that the mobile phone can not keep scanning, so we can delay the scanning for a period of time through the timer in RxJava and stop the scanning:
Observable.timer(SCAN_PERIOD, TimeUnit.MILLISECONDS).subscribe(new Action1<Long>() {
@Override
public void call(Long aLong) {
mIsScanning = false; mBleAdapter.stopLeScan(mBleScanCallback); }});Copy the code
Connect to bluetooth service and receive data
When the target Bluetooth device named mTargetDeviceName is found, it can be connected to the GATT service on the device by using the following method:
private void connectDevice(BluetoothDevice bleDevice) {
scanBleDevices(false);
mBleGatt = bleDevice.connectGatt(mContext, true.new BleGattCallback());
mBleGatt.connect();
Log.d(TAG, "Start connecting device:" + mBleGatt.getDevice().getName());
}Copy the code
Disable Bluetooth scan before connecting bluetooth device. The bleDevice.connectgatt (mContext, true, new BleGattCallback()) method returns a Bluetooth GATT object. True in this method stands for automatic connection (the Bluetooth module can be reconnected after it is powered off and restarted). Call GATT’s connect() method to connect, which executes the passed callback BleGattCallback, which inherits BluetoothGattCallback and overwrites the following three methods:
A, onConnectionStateChange
@Override
public void onConnectionStateChange(BluetoothGatt bleGatt, int status, int newState) {
super.onConnectionStateChange(bleGatt, status, newState);
Log.d(TAG, "OnConnectionStateChange: Connection status:" + newState);
if (newState == BluetoothGatt.STATE_CONNECTED) {// The connection succeeded
Log.d(TAG, "OnConnectionStateChange: Device Connection");
bleGatt.discoverServices();// Search services
} else if (newState == BluetoothGatt.STATE_DISCONNECTED) {// Disconnect the connection
Log.d(TAG, "OnConnectionStateChange: Device disconnected"); }}Copy the code
This method listens for changes in the connection state, which has four values:
value | describe |
---|---|
STATE_CONNECTED | The connected |
STATE_CONNECTING | Are connected |
STATE_DISCONNECTED | disconnect |
STATE_DISCONNECTING | Disconnecting |
When the device is connected, you need to find the GATT service through discoverServices(), which executes the overridden second method, onServicesDiscovered.
Second, the onServicesDiscovered
Can obtain a list of GATT services in this method, the service in the list of each service corresponds to a BluetoothGattCharacteristic communications (for) list, To this list by BluetoothGattCharacteristic UUID to filter out what we want, and then you can take this BluetoothGattCharacteristic to communicate.
About UUID A universal unique identifier (UUID) is a 128-bit standardized format for a string ID that uniquely identifies information. The nice thing about UUID is that it’s large enough that you can pick any random value without conflict. In this example, it is used to uniquely identify the application’s Bluetooth service. To get a UUID for your application, you can use one of the many random UUID generators on the network and then initialize a UUID fromString(String). Don’t obsess too much about UUID.
The procedure above, if written in the traditional way, would be list traversal and nested list traversal and nested if judgment. Next time, it would be a bunch of indenting.
@Override
public void onServicesDiscovered(final BluetoothGatt bleGatt, int status) {
Log.d(TAG, "OnServicesDiscovered: Find service:" + bleGatt.getServices().size());
List<BluetoothGattService> serviceList = bleGatt.getServices();
Observable.from(serviceList)
.flatMap(new Func1<BluetoothGattService, Observable<BluetoothGattCharacteristic>>() {
@Override
public Observable<BluetoothGattCharacteristic> call(BluetoothGattService bleGattService) {
return Observable.from(bleGattService.getCharacteristics());
}
})
.filter(new Func1<BluetoothGattCharacteristic, Boolean>() {
@Override
public Boolean call(BluetoothGattCharacteristic bleGattChar) {
return bleGattChar.getUuid().toString().equals(UUID);
}
})
.subscribe(new Action1<BluetoothGattCharacteristic>() {
@Override
public void call(BluetoothGattCharacteristic bleGattChar) {
bleGatt.setCharacteristicNotification(bleGattChar, true);// Enable receiving Bluetooth datamBleGattChar = bleGattChar; }}); }Copy the code
Third, onCharacteristicChanged
This method is used to receive data from the Bluetooth module. It is asynchronous and can be easily switched to the Android main thread using RxJava:
@Override
public void onCharacteristicChanged(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) {
Log.d(TAG, "onCharacteristicChanged");
String receiveData = new String(characteristic.getValue());
Log.d(TAG, "Received bluetooth data:" + receiveData);
Observable.just(receiveData)
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Action1<String>() {
@Override
public void call(String receiveData) {
/ / processing receiveData}}); }Copy the code
As mentioned earlier, the entire Bluetooth function is a tool class. How do we receive this receiveData in the Activity we want? You might say you can write an interface callback here, yeah, that’s fine. After learning that you could implement the EventBus via RxJava, I thought I could write a simple RxBus to send data here, subscribe and receive data where needed.
For RxBus, you can first look at implementation-an-Event-bus-with-rxjava-rxbus, and then you can look at state-Propagandies-in-Android-with-rxJava-subjects. In addition, there are many related articles in China.
Start by defining a Subject that is both the observer and the observed
private Subject<String, String> mBus = new SerializedSubject<>(PublishSubject.<String>create());Copy the code
The data is then transmitted where receiveData is processed
mBus.onNext(receiveData);Copy the code
Define another method to receive data
public Observable<String> receiveData(a) {
return mBus;
}Copy the code
Finally, subscribe where you want to receive data
mRxBle.receiveData().subscribe(new Action1<String>() {
@Override
public void call(String receiveData) {
sendTv.setText(mStringBuffer.append(receiveData).append("\n")); }});Copy the code
Send data to bluetooth device
Is receiving data communication not only, still need to send data, this implementation is very simple, as long as you use before we get BluetoothGattCharacteristic object and object BluetoothGatt related method calls, in the project due to the need to delay to send data, So timer is also used:
Observable.timer(time, TimeUnit.MILLISECONDS)
.subscribe(new Action1<Long>() {
@Override
public void call(Long l) {
if(mBleGatt ! =null&& mBleGattChar ! =null) {
mBleGattChar.setValue(data);// Set the data
boolean isSend = mBleGatt.writeCharacteristic(mBleGattChar);// Write (send) data
Log.d(TAG, "Send:" + (isSend ? "Success" : "Failure")); }}});Copy the code
The power of RxJava is that it has a wide variety of operators that can do a wide variety of processing on published data sources, and there are many application scenarios in real projects.
The last
Let’s write a simple demo to test it. It looks like this:
-
The Android terminal sends Hello,Ble to the Bluetooth module, and receives the Hello,Android!
-
Use debugging assistant to debug bluetooth module, accept Hello,Ble from Android terminal, and send Hello,Android!
All code addresses for this article: RxBleDemo This article is synchronized to my personal blog