最近在做藍(lán)牙相關(guān)的開發(fā)竿开,雖然說(shuō) FastBleLib 這樣的第三方藍(lán)牙連接庫(kù)非常方便谱仪,但是原生開發(fā)還是有必要了解一下的。
首先說(shuō)明
1.不需要問(wèn)硬件工程師UUID
2.不需要寫 UI
3.本案例只有一個(gè)類文件
4.沒有寫動(dòng)態(tài)請(qǐng)求藍(lán)牙權(quán)限相關(guān)的否彩,自己測(cè)試過(guò)程中疯攒,手動(dòng)開啟所有權(quán)限即可。
5.代碼解釋全部寫在注釋列荔,和日志
<uses-permission android:name="android.permission.BLUETOOTH"/>
<!--此權(quán)限不開敬尺,可能搜不到藍(lán)牙設(shè)備-->
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN"/>
public class Main8Activity extends AppCompatActivity {
// 藍(lán)牙適配器對(duì)象,用于掃描藍(lán)牙設(shè)備
private BluetoothAdapter bluetoothAdapter;
// GATT 通信贴浙,連接藍(lán)牙砂吞,發(fā)送指令,以及接收藍(lán)牙通知 ( 這里所謂的通知崎溃,就是藍(lán)牙傳過(guò)來(lái)的數(shù)據(jù) )
private BluetoothGatt bluetoothGatt;
// 如果藍(lán)牙沒有開啟
private final static int REQUEST_ENABLE = 100;
// 掃描到的藍(lán)牙設(shè)備蜻直,因?yàn)?Set 不會(huì)重復(fù),所以用這個(gè)數(shù)據(jù)結(jié)構(gòu)
private Set<BluetoothDevice> bluetoothDeviceSet;
// 用戶發(fā)送指令的操作
private BluetoothGattCharacteristic writeBluetoothGattCharacteristic;
// 用于開啟通知的操作
private BluetoothGattCharacteristic notificationBluetoothGattCharacteristic;
// 服務(wù)和特征值
private UUID write_UUID_service;
private UUID write_UUID_chara;
private UUID read_UUID_service;
private UUID read_UUID_chara;
private UUID notify_UUID_service;
private UUID notify_UUID_chara;
private UUID indicate_UUID_service;
private UUID indicate_UUID_chara;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main8);
log("初始化藍(lán)牙掃描 的 set 集合");
bluetoothDeviceSet = new HashSet<>();
log("開始獲取藍(lán)牙適配器");
bluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
if(bluetoothAdapter != null){
log("bluetoothAdapter 不為 null 當(dāng)前設(shè)備支持藍(lán)牙");
if(!bluetoothAdapter.isEnabled()){
log("藍(lán)牙未啟動(dòng),正在使用 intent 啟用藍(lán)牙");
Intent intent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
startActivityForResult(intent,REQUEST_ENABLE);
}else{
log("藍(lán)牙已經(jīng)啟動(dòng)袁串,不需要使用 intent 啟動(dòng)藍(lán)牙");
bluetoothAdapter.getBluetoothLeScanner().startScan(scanCallback);
log("開始掃描藍(lán)牙設(shè)備");
}
}else{
log("bluetoothAdapter 為 null 所以當(dāng)前設(shè)備不支持藍(lán)牙");
}
}
@Override
protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if(requestCode == REQUEST_ENABLE){
if(resultCode == RESULT_OK){
log("用戶允許了開啟藍(lán)牙的請(qǐng)求");
log("開始掃描藍(lán)牙設(shè)備");
bluetoothAdapter.getBluetoothLeScanner().startScan(scanCallback);
}else if(resultCode == RESULT_CANCELED){
log("用戶拒絕了開啟藍(lán)牙的請(qǐng)求");
}
}
}
// 藍(lán)牙開始掃描回調(diào)
private ScanCallback scanCallback = new ScanCallback() {
@Override
public void onScanResult(int callbackType, ScanResult result) {
super.onScanResult(callbackType, result);
// bluetoothDevice 為掃描到的藍(lán)牙設(shè)備
BluetoothDevice bluetoothDevice = result.getDevice();
// 添加掃描到的藍(lán)牙設(shè)備到 set 集合
boolean addResult = bluetoothDeviceSet.add(bluetoothDevice);
// 如果 set 集合中未存在該藍(lán)牙對(duì)象概而,則返回true
if(addResult){
//log("onScanResult() 發(fā)現(xiàn)藍(lán)牙設(shè)備" + bluetoothDevice.getName() + "," + bluetoothDevice.getAddress());
//log("目前已經(jīng)發(fā)現(xiàn)" + bluetoothDeviceSet.size() + "個(gè)藍(lán)牙設(shè)備");
// 如果當(dāng)前添加的設(shè)備 藍(lán)牙名稱為 HTC_38908 MAC地址為 88:25:83:F1:04:58,則發(fā)起連接
// 懶得寫 列表 UI 了囱修,這里先直接指定好自己用來(lái)測(cè)試藍(lán)牙的設(shè)備
if("HTC_38908".equals(bluetoothDevice.getName()) && "88:25:83:F1:04:58".equals(bluetoothDevice.getAddress()) ){
log("發(fā)現(xiàn)需要連接的設(shè)備");
log("因?yàn)樗{(lán)牙掃描比較消耗資源赎瑰,所以連接前就關(guān)閉掃描");
// bluetoothAdapter.stopScan(scanCallback);
bluetoothAdapter.cancelDiscovery();
log("開始 GATT 通信");
bluetoothGatt = bluetoothDevice.connectGatt(Main8Activity.this,true,bluetoothGattCallback);
}
}
}
@Override
public void onBatchScanResults(List<ScanResult> results) {
super.onBatchScanResults(results);
}
@Override
public void onScanFailed(int errorCode) {
super.onScanFailed(errorCode);
}
};
private BluetoothGattCallback bluetoothGattCallback = new BluetoothGattCallback() {
@Override
public void onPhyUpdate(BluetoothGatt gatt, int txPhy, int rxPhy, int status) {
super.onPhyUpdate(gatt, txPhy, rxPhy, status);
}
@Override
public void onPhyRead(BluetoothGatt gatt, int txPhy, int rxPhy, int status) {
super.onPhyRead(gatt, txPhy, rxPhy, status);
}
@Override
public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) {
super.onConnectionStateChange(gatt, status, newState);
if(BluetoothGatt.STATE_CONNECTED == newState){ // 藍(lán)牙連接狀態(tài)改變 為 -- 已連接
// 將會(huì)回調(diào) 本類中的 onServicesDiscovered() 這個(gè)方法
boolean result = gatt.discoverServices();
if(result){
log("藍(lán)牙連接成功");
}else{
log("藍(lán)牙連接失敗");
}
}else if(BluetoothGatt.STATE_DISCONNECTED == newState){ // 藍(lán)牙連接狀態(tài)改變?yōu)?-- 連接斷開
log("斷開連接");
}
}
@Override
public void onServicesDiscovered(BluetoothGatt gatt, int status) {
super.onServicesDiscovered(gatt, status);
// 這里初始化 各種 服務(wù) 和 描述 ,用來(lái)發(fā)送藍(lán)牙指令的對(duì)象破镰,還有接收藍(lán)牙數(shù)據(jù)對(duì)象
initServiceAndChara();
}
@Override
public void onCharacteristicRead(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) {
super.onCharacteristicRead(gatt, characteristic, status);
log("onCharacteristicRead");
}
@Override
public void onCharacteristicWrite(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) {
super.onCharacteristicWrite(gatt, characteristic, status);
log("onCharacteristicWrite");
}
@Override
public void onCharacteristicChanged(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) {
super.onCharacteristicChanged(gatt, characteristic);
// 這里將藍(lán)牙傳過(guò)來(lái)的數(shù)據(jù)轉(zhuǎn)換為 16 進(jìn)制格式的字符串餐曼,便于閱讀
byte[] bytes = characteristic.getValue();
StringBuilder stringBuilder = new StringBuilder();
for (byte b : bytes) {
stringBuilder.append(String.format("%02x", b));
}
log("藍(lán)牙傳回來(lái)的內(nèi)容 ( 16進(jìn)制 ) : " + stringBuilder.toString());
}
@Override
public void onDescriptorRead(BluetoothGatt gatt, BluetoothGattDescriptor descriptor, int status) {
super.onDescriptorRead(gatt, descriptor, status);
log("onDescriptorRead");
}
@Override
public void onDescriptorWrite(BluetoothGatt gatt, BluetoothGattDescriptor descriptor, int status) {
super.onDescriptorWrite(gatt, descriptor, status);
log("onDescriptorWrite");
}
@Override
public void onReliableWriteCompleted(BluetoothGatt gatt, int status) {
super.onReliableWriteCompleted(gatt, status);
log("onReliableWriteCompleted");
}
@Override
public void onReadRemoteRssi(BluetoothGatt gatt, int rssi, int status) {
super.onReadRemoteRssi(gatt, rssi, status);
log("onReadRemoteRssi");
}
@Override
public void onMtuChanged(BluetoothGatt gatt, int mtu, int status) {
super.onMtuChanged(gatt, mtu, status);
log("onMtuChanged");
}
};
private void initServiceAndChara(){
List<BluetoothGattService> bluetoothGattServices= bluetoothGatt.getServices();
for (BluetoothGattService bluetoothGattService:bluetoothGattServices){
List<BluetoothGattCharacteristic> characteristics=bluetoothGattService.getCharacteristics();
for (BluetoothGattCharacteristic characteristic:characteristics){
int charaProp = characteristic.getProperties();
if ((charaProp & BluetoothGattCharacteristic.PROPERTY_READ) > 0) {
read_UUID_chara=characteristic.getUuid();
read_UUID_service=bluetoothGattService.getUuid();
log("read_chara="+read_UUID_chara+"----read_service="+read_UUID_service);
}
if ((charaProp & BluetoothGattCharacteristic.PROPERTY_INDICATE) > 0) {
indicate_UUID_chara=characteristic.getUuid();
indicate_UUID_service=bluetoothGattService.getUuid();
log("indicate_chara="+indicate_UUID_chara+"----indicate_service="+indicate_UUID_service);
}
if ((charaProp & BluetoothGattCharacteristic.PROPERTY_WRITE) > 0) {
write_UUID_chara=characteristic.getUuid();
write_UUID_service=bluetoothGattService.getUuid();
log("write_chara="+write_UUID_chara+"----write_service="+write_UUID_service);
}
if ((charaProp & BluetoothGattCharacteristic.PROPERTY_WRITE_NO_RESPONSE) > 0) {
write_UUID_chara=characteristic.getUuid();
write_UUID_service=bluetoothGattService.getUuid();
log("write_chara="+write_UUID_chara+"----write_service="+write_UUID_service);
// 寫入服務(wù)
BluetoothGattService service = bluetoothGatt.getService(write_UUID_service);
writeBluetoothGattCharacteristic = service.getCharacteristic(write_UUID_chara);
// 發(fā)送指令
sendBleData("*".getBytes());
}
if ((charaProp & BluetoothGattCharacteristic.PROPERTY_NOTIFY) > 0) {
notify_UUID_chara=characteristic.getUuid();
notify_UUID_service=bluetoothGattService.getUuid();
log("notify_chara="+notify_UUID_chara+"----notify_service="+notify_UUID_service);
// 初始化通知服務(wù)
BluetoothGattService service = bluetoothGatt.getService(notify_UUID_service);
notificationBluetoothGattCharacteristic = service.getCharacteristic(notify_UUID_chara);
// 開啟通知
bluetoothGatt.setCharacteristicNotification(notificationBluetoothGattCharacteristic,true);
}
}
}
}
/**
* 發(fā)送數(shù)據(jù)給藍(lán)牙
* @param data 數(shù)據(jù)byte[]
* */
private void sendBleData(byte[] data){
writeBluetoothGattCharacteristic.setValue(data);
bluetoothGatt.writeCharacteristic(writeBluetoothGattCharacteristic);
}
/**
* 日志
* @param log 日志內(nèi)容
* */
private void log(String log){
Log.i("藍(lán)牙",log);
}
}