关键术语和概念
- 蓝牙有传统蓝牙(3.0以下)和低功耗蓝牙(BLE,又称蓝牙4.0)之分,而蓝牙4.0开发需要android4.3版本(API 18)及以上才支持BLE API。相比传统的蓝牙,BLE更显著的特点是低功耗。这一优点使android App可以与具有低功耗要求的BLE设备通信,如近距离传感器、心脏速率监视器、健身设备等。
- BLE 全称 Bluetooth Low Energy
- Generic Attribute Profile(GATT)—GATT配置文件是一个通用规范,用于在BLE链路上发送和接收被称为“属性”的数据块。目前所有的BLE应用都基于GATT。 蓝牙SIG规定了许多低功耗设备的配置文件。配置文件是设备如何在特定的应用程序中工作的规格说明。注意一个设备可以实现多个配置文件。例如,一个设备可能包括心率监测仪和电量检测。
- Attribute Protocol(ATT)—GATT在ATT协议基础上建立,也被称为GATT/ATT。ATT对在BLE设备上运行进行了优化,为此,它使用了尽可能少的字节。每个属性通过一个唯一的的统一标识符(UUID)来标识,每个String类型UUID使用128 bit标准格式。属性通过ATT被格式化为characteristics和services。
- Characteristic 一个characteristic包括一个单一变量和0-n个用来描述characteristic变量的descriptor,characteristic可以被认为是一个类型,类似于类。
- Descriptor Descriptor用来描述characteristic变量的属性。例如,一个descriptor可以规定一个可读的描述,或者一个characteristic变量可接受的范围,或者一个characteristic变量特定的测量单位。
- Service service是characteristic的集合。例如,你可能有一个叫“Heart Rate Monitor(心率监测仪)”的service,它包括了很多characteristics,如“heart rate measurement(心率测量)”等。你可以在bluetooth.org 找到一个目前支持的基于GATT的配置文件和服务列表。
具体步骤
一、开启蓝牙
//1、获取蓝牙管理器
BluetoothManager manager = (BluetoothManager) getSystemService(BLUETOOTH_SERVICE);
//2、获得蓝牙适配器
btAdapter = manager.getAdapter();
//3、开启蓝牙
if (!btAdapter.isEnabled()) {
btAdapter.enable();
Toast.makeText(this, "蓝牙已开启", Toast.LENGTH_SHORT).show();
} else {
Toast.makeText(this, "蓝牙已经处于开启状态!", Toast.LENGTH_SHORT).show();
}
二、扫描设备
//扫描设备的回调
private BluetoothAdapter.LeScanCallback leScanCallback = new BluetoothAdapter.LeScanCallback() {
@Override
public void onLeScan(BluetoothDevice bluetoothDevice, int i, byte[] bytes) {
if (bluetoothDevice != null && bluetoothDevice.getName() != null) {
if (!bluetoothDevice.getName().isEmpty() && !mList.contains(bluetoothDevice))
mList.add(bluetoothDevice);
}
}
};
//开启扫描
btAdapter.startLeScan(leScanCallback);
Toast.makeText(this, "正在为您查询。。3秒后显示查询结果", Toast.LENGTH_SHORT).show();
new Handler().postDelayed(new Runnable() {
@Override
public void run() {
Toast.makeText(MainActivity.this, "共查找到" + mList.size() + "条数据!", Toast.LENGTH_SHORT).show();
superAdapter.notifyDataSetChanged();
//查找15秒后停止扫描
btAdapter.stopLeScan(leScanCallback);
}
}, 15000);
三、连接设备
连接的方法:
//通过蓝牙适配器的getRemoteDevice获得具体的设备对象
device = btAdapter.getRemoteDevice(mList.get(position).getAddress());
Log.e("tag", "device= " + device + "addr= " + mList.get(position).getAddress());
//关闭已有连接
if (bluetoothGatt != null) {
bluetoothGatt.close();
}
//通过connectGatt方法连接蓝牙
bluetoothGatt = device.connectGatt(getApplicationContext(), false, gattCallback);
连接的回调:
//中心,操作具体设备的结果都会回调到这个方法中
private BluetoothGattCallback gattCallback = new BluetoothGattCallback() {
//连接的回调方法
@Override
public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) {
String name = Thread.currentThread().getName();
Log.e("tag", "thread" + name);
//连接成功
if (newState == BluetoothProfile.STATE_CONNECTED) {
runOnUiThread(new Runnable() {
@Override
public void run() {
Toast.makeText(MainActivity.this, "设备连接成功...", Toast.LENGTH_SHORT).show();
if(alertDialog.isShowing()){
alertDialog.dismiss();
}
//发送数据的方案
sendMsg(imeiCode, (byte) 0x80, false);
}
//查找该设备的服务并回调到onServiceDiscovered()方法中
bluetoothGatt.discoverServices();
});
}
四、传输数据
发送数据的方法
new Thread(new Runnable() {
//公司的加密和转码方法我注释掉了,只是练手的话连接到设备就足够了
//需要加密的按具体公司的规则来就可以了
// bytes为具体的byte数组
b = writeRXCharacteristic(bytes);
//蓝牙一次最多只能传输20bytes数据,所以要睡一会儿
Log.e("ZCL", "发送数据结果 " + b);
while (!b && retry < maxRetry) {
try {
new Thread().sleep(200);
} catch (InterruptedException e) {
e.printStackTrace();
}
retry++;
b = writeRXCharacteristic(cut);
Log.e("ZCL", i + "个子包第" + retry + "次重试---重试结果" + b);
}
}
}
}).start();
写入数据的回调
//建立通道的回调
@Override
public void onServicesDiscovered(BluetoothGatt gatt, int status) {
super.onServicesDiscovered(gatt, status);
if (status == BluetoothGatt.GATT_SUCCESS) {
//通过设备对象的getService()方法获取到到具体的服务
service = bluetoothGatt.getService(RX_SERVICE_UUID);
//通过服务的哟窜方法
characteristic = service.getCharacteristic(TX_CHAR_UUID);
//第二个参数是建立通道用的,启用或禁用通知
bluetoothGatt.setCharacteristicNotification(characteristic, true);
//获取一个描述符
BluetoothGattDescriptor descriptor = characteristic.getDescriptor(CCCD);
descriptor.setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE);
bluetoothGatt.readCharacteristic(characteristic);
bluetoothGatt.writeDescriptor(descriptor);
runOnUiThread(new Runnable() {
@Override
public void run() {
Toast.makeText(MainActivity.this, "建立通道成功", Toast.LENGTH_SHORT).show();
}
});
} else {
runOnUiThread(new Runnable() {
@Override
public void run() {
Toast.makeText(MainActivity.this, "未在该设备中查找到数据。。", Toast.LENGTH_SHORT).show();
}
});
}
}
//蓝牙返回数据的回调
@SuppressLint("LongLogTag")
@Override
public void onCharacteristicChanged(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) {
super.onCharacteristicChanged(gatt, characteristic);
byte[] txValue = characteristic.getValue();
Log.e("onCharacteristicChanged连接成功的回调", "onCharacteristicRead-->" + txValue);
}
//数据写入的回调
@Override
public void onCharacteristicWrite(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) {
super.onCharacteristicWrite(gatt, characteristic, status);
Log.d(TAG, "写入成功" + characteristic.getValue());
}
开发流程图
![](https://img.haomeiwen.com/i11089762/25a61c4256c4bf80.jpg)
网友评论