一、蓝牙4.0BLE的优势。
与传统蓝牙相比,蓝牙4.0低功耗,传输速度快,最多可连接27个设备,最大无线范围超过100米,反正就是各种好。
注意:安卓系统要求4.3及以上
二、安卓开发
(一) 扫描设备
注意:这是扫描的Activity,是通过控制界面的Activity的startActivityForResult()方法跳转过来的,功能是扫描设备并显示出来,点击设备时会返回对应设备地址和名称给另一个Activity。
1、扫描前要先判断手机是否支持BLE以及是否开启蓝牙
// 判断手机能不能用BLE
if (!getPackageManager().hasSystemFeature(PackageManager.FEATURE_BLUETOOTH_LE)) {
ToastUtil.showShortToast(mContext, R.string.ble_not_supported);
finish();
}
// 初始化 Bluetooth adapter, 通过蓝牙管理器得到一个参考蓝牙适配器(API必须在以上android4.3或以上和版本)
final BluetoothManager bluetoothManager = (BluetoothManager) getSystemService(Context.BLUETOOTH_SERVICE);
mBluetoothAdapter = bluetoothManager.getAdapter();
// 检查设备上是否支持蓝牙
if (mBluetoothAdapter == null) {
ToastUtil.showShortToast(mContext, R.string.error_bluetooth_not_supported);
finish();
return;
}
//没开启蓝牙的话提示用户是否打开蓝牙
if (!mBluetoothAdapter.isEnabled()) {
Intent enableBtIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
startActivityForResult(enableBtIntent, REQUEST_ENABLE_BT);
}
由于这里调用了startActivityForResult()方法,所以我们要重写onActivityResult()方法
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
// 用户选择不打开蓝牙
if (requestCode == REQUEST_ENABLE_BT && resultCode == Activity.RESULT_CANCELED) {
finish();
return;
}
super.onActivityResult(requestCode, resultCode, data);
}
2、开始扫描
进入Activity和扫描按钮按下都要用到,所以将代码抽取为一个方法。
private void scanLeDevice(final boolean enable) {
if (enable) {
// 10秒后停止扫描
mHandler.postDelayed(new Runnable() {
@Override
public void run() {
mScanning = false;
mBluetoothAdapter.stopLeScan(mLeScanCallback);
}
}, SCAN_PERIOD);
mScanning = true;
mBluetoothAdapter.startLeScan(mLeScanCallback);
} else {
mScanning = false;
mBluetoothAdapter.stopLeScan(mLeScanCallback);
}
}
扫描时有回调
private BluetoothAdapter.LeScanCallback mLeScanCallback = new BluetoothAdapter.LeScanCallback() {
@Override
public void onLeScan(final BluetoothDevice device, int rssi, byte[] scanRecord) {
runOnUiThread(new Runnable() {
@Override
public void run() {
<span style="white-space:pre"> </span> // 添加扫描到的设备
mLeDeviceListAdapter.addDevice(device);
}
});
}
};
在ListView中显示扫描到的设备,并设置点击事件
mListView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
final BluetoothDevice device = mLeDeviceListAdapter.getDevice(position);
if (device == null) return;
mDeviceName = device.getName();
mDeviceAddress = device.getAddress();
if (mScanning) {
mBluetoothAdapter.stopLeScan(mLeScanCallback);
mScanning = false;
}
<span style="white-space:pre"> </span>// finish()方法一执行,intent就发送回去了
Intent intent = new Intent();
intent.putExtra(EXTRA_DEVICE_ADDRESS, mDeviceAddress);
setResult(Activity.RESULT_OK, intent);
finish();
}
});
(二) 控制界面
1、上面说了,控制界面的Activity调用startActivityForResult()方法跳转到扫描界面获得设备地址,接下来就真正的连接了
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
switch (requestCode) {
case REQUEST_CONNECT_DEVICE:
// 如果扫描界面有扫描到设备,则进行连接
if (resultCode == Activity.RESULT_OK) {
mDeviceAddress = data.getExtras().getString(DeviceScanActivity.EXTRA_DEVICE_ADDRESS);
if (mBluetoothLeService != null) {
mBluetoothLeService.connect(mDeviceAddress);
}
} else {
ToastUtil.showShortToast(mContext, "没有连接设备");
}
break;
}
}
2、如果连接成功,则进行回调,这部分是在BluetoothLeService类中,该类继承Service类,对相关操作进行封装,用来管理设备与手机的连接和数据传输。
private final BluetoothGattCallback mGattCallback = new BluetoothGattCallback() {
@Override
public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) {
super.onConnectionStateChange(gatt, status, newState);
String intentAction;
if (status == BluetoothGatt.GATT_SUCCESS) { //首先判断这个status,如果等于 BluetoothGatt.GATT_SUCCESS(value=0)代表这个回调是正常
if (newState == BluetoothGatt.STATE_CONNECTED) {
intentAction = ACTION_GATT_CONNECTED;
mConnectionState = STATE_CONNECTED;
//连接成功的话马上查找服务
gatt.discoverServices();
broadcastUpdate(intentAction);
} else if (newState == BluetoothGatt.STATE_DISCONNECTED) {
intentAction = ACTION_GATT_DISCONNECTED;
mConnectionState = STATE_DISCONNECTED;
gatt.discoverServices();
broadcastUpdate(intentAction);
}
}
}
@Override
public void onServicesDiscovered(BluetoothGatt gatt, int status) {
super.onServicesDiscovered(gatt, status);
if (status == BluetoothGatt.GATT_SUCCESS) {
broadcastUpdate(ACTION_GATT_SERVICES_DISCOVERED);
}
}
//从特征中读取数据
@Override
public void onCharacteristicRead(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) {
super.onCharacteristicRead(gatt, characteristic, status);
if (status == BluetoothGatt.GATT_SUCCESS) {
broadcastUpdate(ACTION_DATA_AVAILABLE, characteristic);
}
}
//向特征中写入数据
@Override
public void onCharacteristicWrite(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) {
super.onCharacteristicWrite(gatt, characteristic, status);
if (status == BluetoothGatt.GATT_SUCCESS) {
broadcastUpdate(ACTION_DATA_AVAILABLE, characteristic);
}
}
@Override
public void onCharacteristicChanged(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) {
super.onCharacteristicChanged(gatt, characteristic);
broadcastUpdate(ACTION_DATA_AVAILABLE, characteristic);
}
@Override
public void onReadRemoteRssi(BluetoothGatt gatt, int rssi, int status) {
super.onReadRemoteRssi(gatt, rssi, status);
}
@Override
public void onDescriptorWrite(BluetoothGatt gatt, BluetoothGattDescriptor descriptor, int status) {
broadcastUpdate(ACTION_DATA_AVAILABLE, descriptor.getCharacteristic());
}
};
private void broadcastUpdate(final String action) {
final Intent intent = new Intent(action);
sendBroadcast(intent);
}
private void broadcastUpdate(final String action, final BluetoothGattCharacteristic characteristic) {
final Intent intent = new Intent(action);
if (UUID_HEART_RATE_MEASUREMENT.equals(characteristic.getUuid())) {
int flag = characteristic.getProperties();
int format = -1;
if ((flag & 0x01) != 0) {
format = BluetoothGattCharacteristic.FORMAT_UINT16;
Log.d(TAG, "Heart rate format UINT16.");
} else {
format = BluetoothGattCharacteristic.FORMAT_UINT8;
Log.d(TAG, "Heart rate format UINT8.");
}
final int heartRate = characteristic.getIntValue(format, 1);
Log.d(TAG, String.format("Received heart rate: %d", heartRate));
intent.putExtra(EXTRA_DATA, String.valueOf(heartRate));
} else {
// For all other profiles, writes the data formatted in HEX.对于所有的文件,写入十六进制格式的文件
<span style="white-space:pre"> </span>//这里读取到数据
final byte[] data = characteristic.getValue();
for (int i = 0; i < data.length; i++) {
<span style="white-space:pre"> </span>System.out.println("data......" + data[i]);
<span style="white-space:pre"> </span> }
if (data != null && data.length > 0) {
final StringBuilder stringBuilder = new StringBuilder(data.length);
for(byte byteChar : data)
<span style="white-space:pre"> </span>//以十六进制的形式输出
stringBuilder.append(String.format("%02X ", byteChar));
// intent.putExtra(EXTRA_DATA, new String(data) + "\n" + stringBuilder.toString());
intent.putExtra(EXTRA_DATA, new String(data));
}
}
//发送广播,广播接收器在控制界面的Activity
sendBroadcast(intent);
}
3、回到控制界面的Activity,在上面连接后的回调中发送了一条广播,而广播接收器在这里写,先注册广播,然后写接收到广播后对应的操作。
这里我只用到读和写,查阅《BLE4.0贴片模块手册》可得
读数据的服务和特征值的UUID分别是
READ_SERVICE = "0000ffe0-0000-1000-8000-00805f9b34fb";
READ_CHARACTERISTIC = "0000ffe4-0000-1000-8000-00805f9b34fb";
写数据的服务和特征值的UUID分别是
WRITE_SERVICE = "0000ffe5-0000-1000-8000-00805f9b34fb";
WRITE_CHARACTERISTIC = "0000ffe9-0000-1000-8000-00805f9b34fb";
// 注册广播
private static IntentFilter makeGattUpdateIntentFilter() {
final IntentFilter intentFilter = new IntentFilter();
intentFilter.addAction(BluetoothLeService.ACTION_GATT_CONNECTED);
intentFilter.addAction(BluetoothLeService.ACTION_GATT_DISCONNECTED);
intentFilter.addAction(BluetoothLeService.ACTION_GATT_SERVICES_DISCOVERED);
intentFilter.addAction(BluetoothLeService.ACTION_DATA_AVAILABLE);
return intentFilter;
}
</pre><pre name="code" class="java">// 广播接收器,处理服务所激发的各种事件
// ACTION_GATT_CONNECTED: 连接一个GATT服务
// ACTION_GATT_DISCONNECTED: 从GATT服务中断开连接
// ACTION_GATT_SERVICES_DISCOVERED: 查找GATT服务
// ACTION_DATA_AVAILABLE: 从服务中接受数据
private final BroadcastReceiver mGattUpdateReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
final String action = intent.getAction();
if (BluetoothLeService.ACTION_GATT_CONNECTED.equals(action)) {
mConnected = true;
ToastUtil.showLongToast(mContext, "设备连接成功");
} else if (BluetoothLeService.ACTION_GATT_DISCONNECTED.equals(action)) {
mConnected = false;
ToastUtil.showLongToast(mContext, "设备连接已断开");
} else if (BluetoothLeService.ACTION_GATT_SERVICES_DISCOVERED.equals(action)) {
// 读数据的服务和characteristic
readMnotyGattService = mBluetoothLeService.getSupportedGattServices(UUID.fromString(READ_SERVICE));
readCharacteristic = readMnotyGattService.getCharacteristic(UUID.fromString(READ_CHARACTERISTIC));
mBluetoothLeService.readCharacteristic(readCharacteristic);
mBluetoothLeService.setCharacteristicNotification(readCharacteristic,true);
} else if (BluetoothLeService.ACTION_DATA_AVAILABLE.equals(action)) {
String data = intent.getStringExtra(BluetoothLeService.EXTRA_DATA);
displayData(data);
}
}
};