事务的难度远远低于对事物的恐惧
0.前言
在 Android BLE蓝牙配置全流程(一) 附APP源码 中已经完成了前期的准备工作,在这里我们得到了需要连接的蓝牙设备的名字和地址,需要完成蓝牙设备的连接和数据传输功能。
1.初始化界面
首先需要完成对这个界面包含的控件进行初始化,代码如下:
private TextView text1, text2;
private EditText editText;
private Button btn_send;
private String status = "Disconnected";
private final static String TAG = "hello";
//界面初始化
private void initView() {
text1 = findViewById(R.id.text1);
text2 = findViewById(R.id.text2);
editText = findViewById(R.id.edit_text);
btn_send = findViewById(R.id.btn_send);
text1.setText("状态:" + status);
btn_send.setOnClickListener(this);
}
控件text1
中显示目前的连接状态,控件text2
中显示接收到的数据,控件edittext
中输入要发送的数据,控件btn_send
是发送按钮
2.获取前一个界面传来的数据
首先得到前一个界面传来的数据,代码如下:
public static String EXTRAS_DEVICE_NAME = "DEVICE_NAME";
public static String EXTRAS_DEVICE_ADDRESS = "DEVICE_ADDRESS";
private Bundle bundle;
private String name;
private String address;
bundle = getIntent().getExtras();
name = bundle.getString(EXTRAS_DEVICE_NAME);
address = bundle.getString(EXTRAS_DEVICE_ADDRESS);
name
中存放蓝牙设备的名字,address
中存放蓝牙设备的地址。
3.创建一个服务类并继承Service
本文中创建了一个BlutoothBLEService
并继承了Service
,完成蓝牙设备的初始化、连接、断开连接、读取特征值、写入特征值、设置特征值变化通知以及获取已连接蓝牙的所有服务等操作。
public class BlutoothBLEService extends Service {
...............................
}
3.1 蓝牙设备初始化
public final static String ACTION_GATT_CONNECTED = "com.example.bluebledemo.ACTION_GATT_CONNECTED";
public final static String ACTION_GATT_DISCONNECTED = "com.example.bluebledemo.ACTION_GATT_DISCONNECTED";
public final static String ACTION_GATT_SERVICES_DISCOVERED = "com.example.bluebledemo.ACTION_GATT_SERVICES_DISCOVERED";
public final static String ACTION_DATA_AVAILABLE = "com.example.bluebledemo.ACTION_DATA_AVAILABLE";
public final static String EXTRA_DATA = "com.example.bluebledemo.EXTRA_DATA";
private final static int STATE_DISCONNECTED = 0;
private final static int STATE_CONNECTING = 1;
private final static int STATE_CONNECTED = 2;
private final static String TAG = "hello";
private BluetoothManager mBluetoothManager;
private BluetoothAdapter mBluetoothAdapter;
//蓝牙初始化 在第二个界面中的ServiceConnection中调用
public boolean initialize() {
if (mBluetoothManager == null) {
mBluetoothManager = (BluetoothManager) getSystemService(BLUETOOTH_SERVICE);
if (mBluetoothManager == null) {
Log.i(TAG, "initialize: mBluetoothManager 初始化失败");
return false;
}
}
mBluetoothAdapter = mBluetoothManager.getAdapter();
if (mBluetoothAdapter == null) {
Log.i(TAG, "initialize: mBluetoothAdapter 初始化失败");
return false;
}
return true;
}
上面代码是在第二个界面成功绑定这个服务之后,在回调函数ServiceConnection
中被执行,下文将详细介绍,这里先把这个服务详细的说清楚。
3.2 蓝牙设备的连接
private BluetoothGatt mBluetoothGatt;
//蓝牙连接外围设备
public boolean connect(final String address) {
if (mBluetoothAdapter == null || address == null) {
Log.i(TAG, "connect: BLE not init");
return false;
}
if (mBluetoothDeviceAddress != null && mBluetoothGatt != null && mBluetoothDeviceAddress.equals(address)) {
Log.i(TAG, "connect: Trying to use an existing mBluetoothGatt for connection");
if (mBluetoothGatt.connect()) {
mConnectionState = STATE_CONNECTING;
return true;
} else {
return false;
}
}
final BluetoothDevice device = mBluetoothAdapter.getRemoteDevice(address);
if (device == null) {
Log.i(TAG, "connect: device not found");
return false;
}
mBluetoothGatt = device.connectGatt(this, false, mGattCallback);
Log.i(TAG, "connect: Trying to create a connection");
mBluetoothDeviceAddress = address;
mConnectionState = STATE_CONNECTING;
return true;
}
在第二个界面中会创建一个BlutoothBLEService
类的对象,然后调用这个方法并传入地址,即可连接成功。mBluetoothGatt = device.connectGatt(this, false, mGattCallback)
通过绑定回调函数,本节的后续操作都可在回调函数中完成相应的操作。
3.3 蓝牙设备取消连接
//取消连接
public void disconnect() {
if (mBluetoothAdapter == null || mBluetoothGatt == null) {
Log.i(TAG, "disconnect: BLE not init");
return;
}
mBluetoothGatt.disconnect();
}
//关闭所有蓝牙连接
public void close() {
if (mBluetoothGatt == null) {
return;
}
mBluetoothGatt.close();
mBluetoothGatt = null;
}
同样通过BlutoothBLEService
类的对象调用这个方法即可取消连接。
3.4 读取特征值
//读取特征值
public void readCharacteristic(BluetoothGattCharacteristic characteristic) {
if (mBluetoothAdapter == null || mBluetoothGatt == null) {
Log.i(TAG, "readCharacteristic: BLE not init");
return;
}
mBluetoothGatt.readCharacteristic(characteristic);
}
在蓝牙设备连接成功后自动读一次特征值。
3.5 写入特征值
//写入特征值
public void writeCharacteristic(BluetoothGattCharacteristic characteristic) {
if (mBluetoothAdapter == null || mBluetoothGatt == null) {
Log.i(TAG, "writeCharacteristic: BLE not init");
return;
}
mBluetoothGatt.writeCharacteristic(characteristic);
}
这是完成手机APP向蓝牙设备写数据的操作。
3.6 设置特征值变化通知
这个很重要,如果不设置话,手机APP是接收不到蓝牙设备发送的数据。
//设置特征值变化通知
public void setCharacteristicNotification(BluetoothGattCharacteristic characteristicNotification, boolean enabled) {
if (mBluetoothAdapter == null || mBluetoothGatt == null) {
Log.i(TAG, "setCharacteristicNotification: BLE not init");
return;
}
mBluetoothGatt.setCharacteristicNotification(characteristicNotification, enabled);
BluetoothGattDescriptor descriptor = characteristicNotification.getDescriptor(UUID.fromString("00002902-0000-1000-8000-00805f9b34fb"));
if (enabled) {
descriptor.setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE);
} else {
descriptor.setValue(BluetoothGattDescriptor.DISABLE_NOTIFICATION_VALUE);
}
mBluetoothGatt.writeDescriptor(descriptor);
}
这个方法同样也是在第二个界面中调用的,其中UUID.fromString("00002902-0000-1000-8000-00805f9b34fb"
是HC-08
蓝牙设备的监听UUID,如果蓝牙设备不是一个型号,请更改为自己所用的蓝牙设备的监听UUID
,
3.7 获取已连接蓝牙的所有服务
//获取已连接蓝牙的所有服务
public List<BluetoothGattService> getDupportedGattServices() {
if (mBluetoothGatt == null) {
return null;
}
return mBluetoothGatt.getServices();
}
返回已经连接蓝牙设备的所有服务。
3.8 读取蓝牙设备的RSSI值
//读取RSSI
public void readRssi() {
if (mBluetoothAdapter == null || mBluetoothGatt == null) {
Log.i(TAG, "readRssi: BLE not init");
return;
}
mBluetoothGatt.readRemoteRssi();
}
该方法返回的是已连接的蓝牙设备的信号值(RSSI
)。
3.9 连接外围设备的回调函数
private final BluetoothGattCallback mGattCallback = new BluetoothGattCallback() {
@Override
//重写 蓝牙连接状态
public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) {
if (status == BluetoothGatt.GATT_SUCCESS) {
String intentAction;
if (newState == BluetoothProfile.STATE_CONNECTED) {
intentAction = ACTION_GATT_CONNECTED;
mConnectionState = STATE_CONNECTED;
broadcastUpdate(intentAction);
mBluetoothGatt.discoverServices(); //发现服务
} else if (newState == BluetoothProfile.STATE_DISCONNECTED) {
intentAction = ACTION_GATT_DISCONNECTED;
mConnectionState = STATE_DISCONNECTED;
broadcastUpdate(intentAction);
}
}
}
//重写 蓝牙发现服务
@Override
public void onServicesDiscovered(BluetoothGatt gatt, int status) {
if (status == BluetoothGatt.GATT_SUCCESS) {
broadcastUpdate(ACTION_GATT_SERVICES_DISCOVERED);
Log.i(TAG, "onServicesDiscovered: 蓝牙发现服务");
} else {
Log.i(TAG, "onServicesDiscovered: 蓝牙发现服务失败" + status);
}
}
//重写 蓝牙读特征
@Override
public void onCharacteristicRead(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) {
if (status == BluetoothGatt.GATT_SUCCESS) {
Log.i(TAG, "onCharacteristicRead: is called");
byte[] sucString = characteristic.getValue();
broadcastUpdate(ACTION_DATA_AVAILABLE, characteristic);
}
}
//重写 蓝牙写特征
@Override
public void onCharacteristicWrite(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) {
Log.i(TAG, "onCharacteristicWrite: 写数据成功");
}
//重写 蓝牙特征改变
@Override
public void onCharacteristicChanged(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) {
Log.i(TAG, "onCharacteristicChanged: changed changed changed changed changed ");
broadcastUpdate(ACTION_DATA_AVAILABLE, characteristic);
}
//重写 蓝牙读描述值
@Override
public void onDescriptorRead(BluetoothGatt gatt, BluetoothGattDescriptor descriptor, int status) {
if (status == BluetoothGatt.GATT_SUCCESS) {
Log.i(TAG, "onDescriptorRead: Read Read Read");
byte[] desc = descriptor.getValue();
if (desc == null) {
Log.i(TAG, "onDescriptorRead: desc is null null null");
}
}
}
//重写 蓝牙写描述值
@Override
public void onDescriptorWrite(BluetoothGatt gatt, BluetoothGattDescriptor descriptor, int status) {
if (status == BluetoothGatt.GATT_SUCCESS) {
Log.i(TAG, "onDescriptorWrite: Write Write Write");
}
}
@Override
public void onReliableWriteCompleted(BluetoothGatt gatt, int status) {
if (status == BluetoothGatt.GATT_SUCCESS) {
Log.i(TAG, "onReliableWriteCompleted: onReliableWriteCompleted onReliableWriteCompleted onReliableWriteCompleted");
}
}
//重写 获取蓝牙信号值
@Override
public void onReadRemoteRssi(BluetoothGatt gatt, int rssi, int status) {
if (status == BluetoothGatt.GATT_SUCCESS) {
Log.i(TAG, "onReliableWriteCompleted: RSSI RSSI RSSI");
broadcastUpdate(ACTION_DATA_AVAILABLE, rssi);
}
}
};
在这个回调函数中可以得到已连接的蓝牙设备的所有状态,包括:是否连接成功、是否发送数据成功以及是否读取数据成功等等,然后在这个服务中通过BroadCast
(广播)将信息发出去,在第二个界面中设置广播的监听接收器,进而完成数据的传输。
3.10 发送广播
//更新广播
private void broadcastUpdate(final String action) {
final Intent intent = new Intent(action);
sendBroadcast(intent);
}
//更新广播
private void broadcastUpdate(final String action, int rssi) {
final Intent intent = new Intent(action);
intent.putExtra(EXTRA_DATA, rssi);
sendBroadcast(intent);
}
//更新广播
private void broadcastUpdate(final String action, final BluetoothGattCharacteristic characteristic) {
final Intent intent = new Intent(action);
final byte[] data = characteristic.getValue();
//将data的数据传输给主空间中保存
if (data != null && data.length > 0) {
final StringBuilder stringBuilder = new StringBuilder(data.length);
for (byte byteChar : data) {
stringBuilder.append(String.format("%02X", byteChar));
Log.i(TAG, "broadcastUpdate: byteChar is:" + byteChar);
}
intent.putExtra("BLE_BYTE_DATA", data);
intent.putExtra(EXTRA_DATA, new String(data));
}
sendBroadcast(intent);
}
通过广播的形式将数据发出去,在第二个界面中通过设置过滤器接收对应的广播。
3.11 获取服务的对象
当在第二个界面中成功绑定了该服务,通过调用这个方法即可返回一个该服务的对象。
public class LocalBinder extends Binder {
public BlutoothBLEService getService() {
return BlutoothBLEService.this;
}
}
@Nullable
@Override
public IBinder onBind(Intent intent) {
return mBinder;
}
public boolean onUnbind(Intent intent) {
close();
return super.onUnbind(intent);
}
4.绑定服务
在完成服务的代码编写之后,接着需要在第二个界面中绑定该服务,在onCreat
方法中进行绑定,代码如下:
Intent getServiceIntent = new Intent(this, BlutoothBLEService.class);
bindService(getServiceIntent, mServiceConnection, BIND_AUTO_CREATE);
5.绑定服务的回调函数
是否成功的绑定了服务,我们可以在回调函数中获知,代码如下:
private BlutoothBLEService mBlutoothBLEService;
//服务(BlutoothBLEService.class)连接 回调函数
private final ServiceConnection mServiceConnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
mBlutoothBLEService = ((BlutoothBLEService.LocalBinder) service).getService();
if (!mBlutoothBLEService.initialize()) {
Log.i(TAG, "onServiceConnected: MainActivity BLE not init");
finish();
}
Log.i(TAG, "onServiceConnected: 8888888888888");
mBlutoothBLEService.connect(address);
}
@Override
public void onServiceDisconnected(ComponentName name) {
mBlutoothBLEService = null;
}
};
如果绑定成功,首先执行mBlutoothBLEService = ((BlutoothBLEService.LocalBinder) service).getService()
获取这个服务的对象,然后执行mBlutoothBLEService.initialize()
对服务进行初始化。
6.注册/取消注册广播
//注册广播和IntentFilter
@Override
protected void onResume() {
registerReceiver(mBroadCastReceiver, makeGattUpdateIntentFilter());
if (mBlutoothBLEService != null) {
Log.i(TAG, "onResume: 99999999999999");
boolean res = mBlutoothBLEService.connect(address);
Log.i(TAG, "onResume: " + res);
}
super.onResume();
}
//取消注册广播和IntentFilter
@Override
protected void onDestroy() {
unregisterReceiver(mBroadCastReceiver);
mBlutoothBLEService = null;
super.onDestroy();
}
上述代码利用registerReceiver(mBroadCastReceiver, makeGattUpdateIntentFilter())
和unregisterReceiver(mBroadCastReceiver)
方法完成注册和取消注册广播,下面的代码是设置广播接收和过滤器。
//IntentFilter 设置过滤 与广播进行注册
public IntentFilter makeGattUpdateIntentFilter() {
IntentFilter filter = new IntentFilter();
filter.addAction(BlutoothBLEService.ACTION_GATT_CONNECTED);
filter.addAction(BlutoothBLEService.ACTION_GATT_DISCONNECTED);
filter.addAction(BlutoothBLEService.ACTION_GATT_SERVICES_DISCOVERED);
filter.addAction(BlutoothBLEService.ACTION_DATA_AVAILABLE);
return filter;
}
//设置广播接收 服务(BlutoothBLEService.class)传过来得信息
BroadcastReceiver mBroadCastReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
String actionString = intent.getAction();
if (BlutoothBLEService.ACTION_GATT_CONNECTED.equals(actionString)) { //蓝牙设备连接成功
Log.i(TAG, "onReceive: " + name + " 连接成功");
status = "Connected";
updateConnectionState(status);
} else if (BlutoothBLEService.ACTION_GATT_DISCONNECTED.equals(actionString)) { //蓝牙设备连接失败
Log.i(TAG, "onReceive: " + name + " 断开连接");
status = "Disconnected";
updateConnectionState(status);
} else if (BlutoothBLEService.ACTION_GATT_SERVICES_DISCOVERED.equals(actionString)) { //蓝牙设备设置服务
Log.i(TAG, "onReceive: 广播接收到服务");
displayGattServices(mBlutoothBLEService.getDupportedGattServices());
} else if (BlutoothBLEService.ACTION_DATA_AVAILABLE.equals(actionString)) { //蓝牙设备有数据提供
Log.i(TAG, "onReceive: 有数据");
displayData(intent.getExtras().getString(BlutoothBLEService.EXTRA_DATA), intent);
}
}
};
其中displayGattServices(mBlutoothBLEService.getDupportedGattServices())
进一步完成发现服务,displayData(intent.getExtras().getString(BlutoothBLEService.EXTRA_DATA), intent)
进一步完成数据接收的处理,updateConnectionState(status)
是完成蓝牙状态的更新操作,这三个代码将在下文进行说明。
7.发现已连接蓝牙设备服务
public static String CHARACTERISTIC_UUID = "0000ffe1-0000-1000-8000-00805f9b34fb";
private BluetoothGattCharacteristic target_chara;
private Handler mHandler = new Handler();
//服务
public void displayGattServices(List<BluetoothGattService> gattServices) {
if (gattServices == null) {
return;
}
for (BluetoothGattService service : gattServices) {
List<BluetoothGattCharacteristic> gattCharacteristics = service.getCharacteristics();
for (final BluetoothGattCharacteristic characteristic : gattCharacteristics) {
if (characteristic.getUuid().toString().equals(CHARACTERISTIC_UUID)) {
mHandler.postDelayed(new Runnable() {
@Override
public void run() {
mBlutoothBLEService.readCharacteristic(characteristic);
}
}, 200);
mBlutoothBLEService.setCharacteristicNotification(characteristic, true);
target_chara = characteristic;
}
}
}
}
其中CHARACTERISTIC_UUID
时HC-08
蓝牙设备的读写特征UUID
,如果使用的蓝牙设备型号不一样请改为自己所用的蓝牙设备的读写特征UUID
,根据这个UUID
找到蓝牙设备的读写特征,从而设置特征变化通知和记录这个特征。
8.数据的接收
//接收的数据
public void displayData(String rev_string, Intent intent) {
try {
byte[] data = intent.getByteArrayExtra("BLE_BYTE_DATA");
if (data == null) {
Log.i(TAG, "displayData: data is empty");
return;
}
rev_string = new String(data, 0, data.length, "GB2312");
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
rev_str = rev_str + rev_string;
runOnUiThread(new Runnable() {
@Override
public void run() {
text2.setText(rev_str);
}
});
}
上面的代码完成数据的接收,并将接收到的数据放入控件text2
中进行追加显示。
9.更新蓝牙连接状态
private tempHandler myHandler = new tempHandler();
private String rev_str = "";
//更新蓝牙连接状态
private void updateConnectionState(String Status) {
Message msg = new Message();
msg.what = 1;
Bundle bundle = new Bundle();
bundle.putString("connect_state", status);
msg.setData(bundle);
myHandler.sendMessage(msg);
}
private class tempHandler extends Handler {
@Override
public void handleMessage(@NonNull Message msg) {
switch (msg.what) {
case 1:
String str;
str = msg.getData().getString("connect_state");
text1.setText("状态:" + str);
break;
}
super.handleMessage(msg);
}
}
在控件text1
中更新显示当前蓝牙的连接状态。
10.发送数据
//发送数据线程
public class sendDataThread implements Runnable {
public sendDataThread() {
new Thread(this).start();
}
@Override
public void run() {
if (editText.getText() != null) {
byte[] buff = null;
try {
buff = editText.getText().toString().getBytes("GB2312");
Log.i(TAG, "run: " + buff.length);
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
int[] sendDataLens = dataSparate(buff.length);
for (int i = 0; i < sendDataLens[0]; i++) {
byte[] data20 = new byte[20];
for (int j = 0; j < 20; j++) {
data20[j] = buff[i * 20 + j];
}
target_chara.setValue(data20);
mBlutoothBLEService.writeCharacteristic(target_chara);
}
if (sendDataLens[1] != 0) {
byte[] lastData = new byte[sendDataLens[1]];
for (int i = 0; i < sendDataLens[1]; i++) {
lastData[i] = buff[sendDataLens[0] * 20 + i];
}
if (lastData != null) {
target_chara.setValue(lastData);
mBlutoothBLEService.writeCharacteristic(target_chara);
} else {
Log.i(TAG, "run: last是空的");
}
}
}
}
}
//发送数据实现点击事件
@Override
public void onClick(View v) {
new sendDataThread();
}
//数据分包处理
private int[] dataSparate(int len) {
int[] lens = new int[2];
lens[0] = len / 20;
lens[1] = len % 20;
return lens;
}
通过点击发送按钮,创建一个发送数据的线程,发送的数据将进行分包处理依次发送,确保即使数据太大也能成功的发送。
至此Android的BLE蓝牙(HC-08)的配置全流程都已详细的说明,如有任何问题可以留言或者私信我,看到会及时回复,上述的内容如果有误,还请各位大佬不吝赐教,在此,祝愿各位学习顺利,前途无限~