private BluetoothAdapter bluetoothAdapter;
private BluetoothManager bluetoothManager;
@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// bluetoothAdapter
bluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
// bluetoothManager BLE需要这个类
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) {
bluetoothManager = (BluetoothManager) getSystemService(BLUETOOTH_SERVICE);
}
}
public void checkBLESupport() {
if (!getPackageManager().hasSystemFeature(PackageManager.FEATURE_BLUETOOTH_LE)) {
Toast.makeText(this, "不支持BLE", Toast.LENGTH_LONG).show();
Log.d("kaikai", "不支持BLE");
} else {
Toast.makeText(this, "支持BLE", Toast.LENGTH_LONG).show();
Log.d("kaikai", "支持BLE");
}
}
public void onEnableBlueTooth() {
if (!bluetoothAdapter.isEnabled()) {
Intent enableIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
startActivityForResult(enableIntent, 0);
} else {
Toast.makeText(this, "蓝牙已开启", Toast.LENGTH_LONG).show();
}
}
public void onDiscoverable() {
Intent discoverableIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_DISCOVERABLE);
startActivity(discoverableIntent);
}
BluetoothGattService service = new BluetoothGattService(YOUR_SERVICE_UUID, BluetoothGattService.SERVICE_TYPE_PRIMARY);
BluetoothGattCharacteristic characteristic = new BluetoothGattCharacteristic(YOUR_CHARACTERISTIC_UUID,
// 注意,如果要使特征值可写,PROPERTY需设置PROPERTY_WRITE(服务端需要返回响应)或PROPERTY_WRITE_NO_RESPONSE(服务端无需返回响应),
BluetoothGattCharacteristic.PROPERTY_READ | BluetoothGattCharacteristic.PROPERTY_NOTIFY | BluetoothGattCharacteristic.PROPERTY_WRITE,
BluetoothGattCharacteristic.PERMISSION_READ | BluetoothGattCharacteristic.PERMISSION_WRITE);
注意,BluetoothGattCharacteristic构造函数的第二个参数,如果要使特征值可被客户端进行反写,需要有BluetoothGattCharacteristic.PROPERTY_WRITE或PROPERTY_WRITE_NO_RESPONSE标志位,其中,PROPERTY_WRITE标志位要求服务端在接收写请求时,返回响应,而PROPERTY_WRITE_NO_RESPONSE则不需要响应,但数据可能会被截断。
BluetoothGattDescriptor descriptor = new BluetoothGattDescriptor(YOUR_DESCRIPTER_UUID, BluetoothGattDescriptor.PERMISSION_READ | BluetoothGattDescriptor.PERMISSION_WRITE);
characteristic.addDescriptor(descriptor);
service.addCharacteristic(characteristic);
// 使用一个类成员保存BluetoothGattServer引用
private BluetoothGattServer bluetoothGattServer;
// onCreate方法中或某个按钮触发的回调函数中
bluetoothGattServer = bluetoothManager.openGattServer(this, new BluetoothGattServerCallback() {
@Override
public void onConnectionStateChange(BluetoothDevice device, int status, int newState) {
// 连接状态改变
if (newState == BluetoothProfile.STATE_CONNECTED) {
Log.d("kaikai", "BLE服务端:BLE Connected!");
} else if (newState == BluetoothProfile.STATE_DISCONNECTED) {
Log.d("kaikai", "BLE服务端:BLE Disconnected!");
}
}
@Override
public void onCharacteristicReadRequest(BluetoothDevice device, int requestId, int offset, BluetoothGattCharacteristic characteristic) {
Log.d("kaikai", "BLE服务端:接收到特征值读请求!" + " requestId=" + requestId + " offset=" + offset);
BluetoothGattCharacteristic gattCharacteristic = bluetoothGattServer.getService(TIME_SERVICE_UUID).getCharacteristic(TIME_CHARACTERISTIC_UUID);
if (characteristic == gattCharacteristic) {
// 证明characteristic与通过gattServer得到的是同一个对象
Log.d("kaikai", "BLE服务端:same characteristic!");
}
byte[] value = characteristic.getValue();
if (value == null) {
value = "init responseData".getBytes();
characteristic.setValue(value);
}
// offset != 0,如果读的偏移不为0,证明是分段读,返回特征值的对应便宜的截断数据就行(每次返回的长度都至末尾)
if (offset != 0) {
int newLen = value.length - offset;
byte[] retVal = new byte[value.length - offset];
System.arraycopy(value, offset, retVal, 0, newLen);
value = retVal;
}
// 请求读特征
if (TIME_CHARACTERISTIC_UUID.equals(characteristic.getUuid())) {
bluetoothGattServer.sendResponse(device, requestId, BluetoothGatt.GATT_SUCCESS, offset, value);
}
}
@Override
public void onCharacteristicWriteRequest(BluetoothDevice device, int requestId, BluetoothGattCharacteristic characteristic, boolean preparedWrite, boolean responseNeeded, int offset, byte[] value) {
// super.onCharacteristicWriteRequest(device, requestId, characteristic, preparedWrite, responseNeeded, offset, value);
Log.d("kaikai", "BLE Server端 onCharacteristicWriteRequest"
+ " requestId:" + requestId + " offset:" + offset + " prepareWrite:" + preparedWrite
+ " responseNeeded:" + responseNeeded + " value:" + new String(value));
// 不是分段写
if (!preparedWrite) {
characteristic.setValue(value);
}
// 分段写
else {
if (offset == 0) {
bleDataByteArray = new ByteArrayOutputStream();
}
try {
bleDataByteArray.write(value);
} catch (IOException e) {
throw new RuntimeException(e);
}
writingObj = characteristic;
}
// 尝试用更改后的数据作为响应,发现客户端的回调的状态值为133,不为0
byte[] valueWrong = new byte[value.length];
System.arraycopy(value, 0, valueWrong, 0, value.length);
valueWrong[0]++;
if (responseNeeded) {
// 注意,如果写特征值需要响应(特征值的属性是PROPERTY_WRITE不是PROPERTY_WRITE_NO_RESPONSE),必需发送value作为响应数据
bluetoothGattServer.sendResponse(device, requestId, BluetoothGatt.GATT_SUCCESS, offset, value);
// 此处为使用错误的数据做响应
// bluetoothGattServer.sendResponse(device, requestId, BluetoothGatt.GATT_SUCCESS, offset, valueWrong);
}
}
@Override
public void onExecuteWrite(BluetoothDevice device, int requestId, boolean execute) {
super.onExecuteWrite(device, requestId, execute);
// 当分段写时,才会回调此方法
Log.d("kaikai", "onExecuteWrite called! requestId=" + requestId + " execute=" + execute);
if (execute) {
byte[] data = bleDataByteArray.toByteArray();
String dataStr = new String(data);
Log.d("kaikai", "onExecuteWrite 拼接数据:" + dataStr);
// BluetoothGattCharacteristic characteristic1 = bluetoothGattServer.getService(TIME_SERVICE_UUID).getCharacteristic(TIME_CHARACTERISTIC_UUID);
// characteristic1.setValue(data);
if (writingObj != null) {
if (writingObj instanceof BluetoothGattCharacteristic) {
((BluetoothGattCharacteristic) writingObj).setValue(data);
} else if (writingObj instanceof BluetoothGattDescriptor) {
((BluetoothGattDescriptor) writingObj).setValue(data);
} else {
throw new RuntimeException("writingObj类型不明");
}
} else {
throw new RuntimeException("writingObj为空");
}
// 注意,当写数据过长时,会自动分片,多次调用完onCharacteristicWriteRequest后,便会调用此方法,要在此方法中发送响应,execute参数指示是否执行成功,可按照此参数发送响应的状态
bluetoothGattServer.sendResponse(device, requestId, BluetoothGatt.GATT_SUCCESS, 0, null);
} else {
Log.d("kaikai", "BLE SERVER: onExecuteWrite发送失败响应");
// 发送一个失败响应
bluetoothGattServer.sendResponse(device, requestId, BluetoothGatt.GATT_FAILURE, 0, null);
}
}
@Override
public void onServiceAdded(int status, BluetoothGattService service) {
super.onServiceAdded(status, service);
Log.d("kaikai", "onServiceAdded");
}
@Override
public void onMtuChanged(BluetoothDevice device, int mtu) {
super.onMtuChanged(device, mtu);
Log.d("kaikai", "BLE SERVER:onMTUChanged! mtu=" + mtu);
// bluetoothGattServer.sendResponse(device,0,0,0,null);
}
@Override
public void onDescriptorReadRequest(BluetoothDevice device, int requestId, int offset, BluetoothGattDescriptor descriptor) {
super.onDescriptorReadRequest(device, requestId, offset, descriptor);
Log.d("kaikai", "BLE服务端:onDescriptorReadRequest requestId=" + requestId + " offset=" + offset);
byte[] value = descriptor.getValue();
if (value == null) {
value = "descriptor1 value".getBytes();
}
// 与characteristic的处理一样
if (offset > 0) {
int newLen = value.length - offset;
byte[] newValue = new byte[newLen];
System.arraycopy(value, offset, newValue, 0, newLen);
value = newValue;
}
bluetoothGattServer.sendResponse(device, requestId, BluetoothGatt.GATT_SUCCESS, offset, value);
}
@Override
public void onDescriptorWriteRequest(BluetoothDevice device, int requestId, BluetoothGattDescriptor descriptor, boolean preparedWrite, boolean responseNeeded, int offset, byte[] value) {
super.onDescriptorWriteRequest(device, requestId, descriptor, preparedWrite, responseNeeded, offset, value);
Log.d("kaikai", "BLE服务端:onDescriptorWriteRequest requestId=" + requestId + " preparedWrite=" + preparedWrite + " responseNeeded=" + responseNeeded
+ " value=" + new String(value));
if (!preparedWrite) {
descriptor.setValue(value);
} else {
if (offset == 0) {
bleDataByteArray = new ByteArrayOutputStream();
}
try {
bleDataByteArray.write(value);
} catch (IOException e) {
throw new RuntimeException(e);
}
writingObj = descriptor;
}
if (responseNeeded) {
bluetoothGattServer.sendResponse(device, requestId, BluetoothGatt.GATT_SUCCESS, offset, value);
}
}
});
有几点需要注意:
bluetoothGattServer.addService(service);
/**
* 通知服务开启(发广告)
*/
@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
private void startAdvertising() {
BluetoothLeAdvertiser advertiser = bluetoothAdapter.getBluetoothLeAdvertiser();
if (advertiser == null) {
Log.d("kaikai", "advertiser为空");
return;
}
AdvertiseSettings settings = new AdvertiseSettings.Builder().setAdvertiseMode(AdvertiseSettings.ADVERTISE_MODE_BALANCED)
.setConnectable(true)
.setTimeout(0)
.setTxPowerLevel(AdvertiseSettings.ADVERTISE_TX_POWER_MEDIUM)
.build();
AdvertiseData data = new AdvertiseData.Builder().setIncludeDeviceName(true)
.setIncludeTxPowerLevel(false)
.addServiceUuid(new ParcelUuid(TIME_SERVICE_UUID))
.build();
advertiser.startAdvertising(settings, data, new AdvertiseCallback() {
@Override
public void onStartSuccess(AdvertiseSettings settingsInEffect) {
Log.d("kaikai", "BLE Advertise started!");
}
@Override
public void onStartFailure(int errorCode) {
Log.d("kaikai", "BLE Advertise fail!errorCode=" + errorCode);
}
});
}
此处有几点需要注意:
AdvertiseCallback回调接口用于判断是否成功开启BLE广播
至此,BLE服务端成功开启。
public void onBLEScan(View view) {
bluetoothAdapter.getBluetoothLeScanner().startScan(scanCallback);
}
scanCallback = new ScanCallback() {
@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
@Override
public void onScanResult(int callbackType, ScanResult result) {
super.onScanResult(callbackType, result);
BluetoothDevice device = result.getDevice();
Log.d("kaikai", "BLE发现设备:" + device.getName() + " " + device.getAddress());
deviceAdapter.add("设备名:" + device.getName() + "地址:" + device.getAddress());
// 这里我添加了自己特定的处理逻辑,大家按回自己的逻辑进行处理
if (device.getName() != null && device.getName().equalsIgnoreCase("kk")) {
// 搜索到设备名为 “kk”,即可停止搜索了
Log.d("kaikai", "match!");
bluetoothAdapter.getBluetoothLeScanner().stopScan(scanCallback);
// 这里可接着写连接BluetoothGATT的逻辑,下面会介绍
}
}
@Override
public void onBatchScanResults(List results) {
super.onBatchScanResults(results);
Log.d("kaikai", "BatchScanResult" + results.toString());
}
@Override
public void onScanFailed(int errorCode) {
super.onScanFailed(errorCode);
Log.d("kaikai", "BLE Scan failed!");
}
};
当搜索到你想要的设备对象(BluetoothDevice)时,就可进行连接了,使用BluetoothGATT对象进行
// 连接
blueToothGatt = device.connectGatt(MainActivity.this, false, new BluetoothGattCallback() {
@Override
public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) {
super.onConnectionStateChange(gatt, status, newState);
if (newState == BluetoothProfile.STATE_CONNECTED) {
Log.d("kaikai", "BLE客户端:BLE连接成功");
blueToothGatt.discoverServices();
if (blueToothGatt == gatt) {
Log.d("kaikai", "BLE客户端:same gatt object!");
}
Log.d("kaikai", "BLE客户端:开始搜索外围服务");
} else if (newState == BluetoothProfile.STATE_DISCONNECTED) {
Log.d("kaikai", "BLE客户端:断开BLE连接");
}
}
@Override
public void onServicesDiscovered(BluetoothGatt gatt, int status) {
super.onServicesDiscovered(gatt, status);
if (status == BluetoothGatt.GATT_SUCCESS) {
Log.d("kaikai", "BLE客户端:成功搜索到服务");
List services = gatt.getServices();
BluetoothGattService service = gatt.getService(TIME_SERVICE_UUID);
if (service == null) {
Log.d("kaikai", "service为空");
return;
}
// 对应的特征
BluetoothGattCharacteristic characteristic = service.getCharacteristic(TIME_CHARACTERISTIC_UUID);
gatt.setCharacteristicNotification(characteristic, true);
boolean b;
// 更改特性描述
// 读一下特性
// b = gatt.readCharacteristic(characteristic);
// Log.d("kaikai", "b:" + b);
// 如果调用了readCharacteristic,则睡眠数秒后再调用readDescriptor也会返回false,应该是同一线程内不能与服务端交互多次
// try {
// Thread.sleep(5000);
// } catch (InterruptedException e) {
// e.printStackTrace();
// }
// 注意,前面如果调用了readCharacteristic,这里就不能直接进行descriptor读取,会返回false。如果没有调用则可
BluetoothGattDescriptor descriptor = characteristic.getDescriptor(TIME_DESCRIPTER_UUID);
b = gatt.readDescriptor(descriptor);
Log.d("kaikai", "descriptor b:" + b);
}
}
@Override
public void onCharacteristicRead(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) {
super.onCharacteristicRead(gatt, characteristic, status);
if (status == BluetoothGatt.GATT_SUCCESS) {
Log.d("kaikai", "BLE客户端:成功读取到特性");
byte[] value = characteristic.getValue();
try {
String val = new String(value, "utf8");
Log.d("kaikai", val);
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
// 注意,descriptor的读取须要在完成characteristic读取后执行
// BluetoothGattDescriptor descriptor = characteristic.getDescriptor(TIME_DESCRIPTER_UUID);
// boolean b = gatt.readDescriptor(descriptor);
// Log.d("kaikai", "b2:" + b);
}
}
@Override
public void onCharacteristicWrite(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) {
super.onCharacteristicWrite(gatt, characteristic, status);
Log.d("kaikai", "BLE客户端:onCharacteristicWrite called!status=" + status);
if (status == BluetoothGatt.GATT_SUCCESS) {
Log.d("kaikai", "BLE客户端:写特征值成功");
byte[] value = characteristic.getValue();
Log.d("kaikai", "value:" + new String(value));
}
}
@Override
public void onCharacteristicChanged(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) {
super.onCharacteristicChanged(gatt, characteristic);
Log.d("kaikai", "BLE客户端:onCharacteristicChanged");
byte[] newValue = characteristic.getValue();
try {
Log.d("kaikai", new String(newValue, "utf8"));
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
}
@Override
public void onDescriptorRead(BluetoothGatt gatt, BluetoothGattDescriptor descriptor, int status) {
super.onDescriptorRead(gatt, descriptor, status);
Log.d("kaikai", "BLE客户端:onDescriptorRead status=" + status);
Log.d("kaikai", "BLE客户端:成功读取descriptor:" + new String(descriptor.getValue()));
}
@Override
public void onDescriptorWrite(BluetoothGatt gatt, BluetoothGattDescriptor descriptor, int status) {
super.onDescriptorWrite(gatt, descriptor, status);
Log.d("kaikai", "BLE客户端:onDescriptorWrite status=" + status);
}
@Override
public void onMtuChanged(BluetoothGatt gatt, int mtu, int status) {
super.onMtuChanged(gatt, mtu, status);
Log.d("kaikai", "BLE客户端: onMTUChanged. mtu=" + mtu + " status=" + status);
if (status != BluetoothGatt.GATT_SUCCESS) {
Log.d("kaikai", "BLE客户端: onMTUChanged status不为0");
}
}
});
客户端不需要处理分段问题。
onCharacteristicChanged方法可监听服务端推送的特征值改变请求
/**
* BLE客户端读取特征值1
*
* @param view
*/
@RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN_MR2)
public void onBLEClientReadCharacteristic1(View view) {
BluetoothGattCharacteristic characteristic = blueToothGatt.getService(TIME_SERVICE_UUID).getCharacteristic(TIME_CHARACTERISTIC_UUID);
blueToothGatt.readCharacteristic(characteristic);
}
/**
* BLE客户端读取descriptor
*
* @param view
*/
@RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN_MR2)
public void onClientReadDescriptor(View view) {
BluetoothGattDescriptor descriptor = blueToothGatt.getService(TIME_SERVICE_UUID).getCharacteristic(TIME_CHARACTERISTIC_UUID).getDescriptor(TIME_DESCRIPTER_UUID);
boolean readDescriptorBool = blueToothGatt.readDescriptor(descriptor);
Log.d("kaikai", "readDescriptorBool:" + readDescriptorBool);
}
读取成功后,会回调BluetoothGattCallback的相关方法
/**
* BLE客户端写特征值1
*
* @param view
*/
@RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN_MR2)
public void onBLEClientWriteCharacteristic1(View view) throws UnsupportedEncodingException {
BluetoothGattCharacteristic characteristic = blueToothGatt.getService(TIME_SERVICE_UUID).getCharacteristic(TIME_CHARACTERISTIC_UUID);
byte[] characteristicData = characteristic.getValue();
if (characteristicData != null) {
Log.d("kaikai", "BLE客户端写特征值前的特征值为:" + new String(characteristicData, "utf8"));
} else {
Log.d("kaikai", "characteristic value为空");
}
characteristic.setValue("client write charrrrrrrrrrrrrr 9999999994444".getBytes());
blueToothGatt.writeCharacteristic(characteristic);
}
/**
* BLE客户端读写descriptor
*
* @param view
*/
@RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN_MR2)
public void onClientWriteDescriptor(View view) {
byte[] dataToWrite = "1111111111222222222233333333334444444444".getBytes();
BluetoothGattDescriptor descriptor = blueToothGatt.getService(TIME_SERVICE_UUID).getCharacteristic(TIME_CHARACTERISTIC_UUID)
.getDescriptor(TIME_DESCRIPTER_UUID);
descriptor.setValue(dataToWrite);
boolean result = blueToothGatt.writeDescriptor(descriptor);
Log.d("kaikai", "descriptorWriteBool:" + result);
}
写操作成功后,会回调BluetoothGattCallback的相关方法