本文針對一對一的藍(lán)牙進(jìn)行通訊,適合沒有開發(fā)過藍(lán)牙的同學(xué)來看,也適合大部分物聯(lián)網(wǎng)簡單開發(fā),沒有深入藍(lán)牙的開發(fā)。對于沒有開發(fā)過藍(lán)牙的來說,我先說下邏輯。比如先拿自己手機(jī)的藍(lán)牙來說,打開藍(lán)牙,列出列表,包含已經(jīng)配對過的和可用設(shè)備, 點(diǎn)擊其中一個(gè)進(jìn)行配對,配對完成就可以進(jìn)行文件傳輸?shù)韧ㄐ殴δ芰?。所以對于藍(lán)牙開發(fā),大致以下步驟
1.打開藍(lán)牙
2.藍(lán)牙掃描,列出可用設(shè)備
3.關(guān)閉藍(lán)牙掃描(不關(guān)閉會(huì)一直掃描)
4.找到目標(biāo)藍(lán)牙設(shè)備進(jìn)行連接
5.連接成功,進(jìn)行通信
6.關(guān)閉藍(lán)牙釋放資源
接下來我們要根據(jù)上面6個(gè)步驟進(jìn)行API的說明,在說明前,我先說明一下
(1)Service藍(lán)牙功能集合,每一個(gè)Service都有一個(gè)UUID,
(2)Characteristic 在service中也有好多個(gè)Characteristic 獨(dú)立數(shù)據(jù)項(xiàng),其中也有獨(dú)立UUID
上面的兩個(gè)uuid需要從硬件工程師中獲取,這樣你才能匹配到你要的。
(3)BluetoothAdapter 藍(lán)牙的打開關(guān)閉等基本操作
(4)BluetoothDevice 藍(lán)牙設(shè)備,掃描到的
(5)BluetoothGatt 藍(lán)牙連接重連斷開連接等操作的類
(6)BluetoothGattCharacteristic 數(shù)據(jù)通信操作類,讀寫等操作
1.打開藍(lán)牙
需要權(quán)限
<uses-permission android:name="android.permission.BLUETOOTH" />
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
BluetoothAdapter 這個(gè)類就是藍(lán)牙的基本操作,比如打開關(guān)閉等
初始化藍(lán)牙,得到BluetoothAdapter
private void initBlueTooth() {
BluetoothManager manager = (BluetoothManager) getSystemService(BLUETOOTH_SERVICE);
if (manager != null) {
bluetoothAdapter = manager.getAdapter();
if (bluetoothAdapter != null) {
//藍(lán)牙沒有打開
if (!bluetoothAdapter.isEnabled()) {
openBle();
} else {
Toast.makeText(MainActivity.this, "藍(lán)牙已打開", Toast.LENGTH_SHORT).show();
scanLeDevice(true);
}
} else {
openBle();
}
}
}
打開藍(lán)牙
private void openBle() {
//以下兩種方式 第二種方式在onActivityResult處理回調(diào)
// boolean enable = bluetoothAdapter.enable();//打開藍(lán)牙'直接打開,用戶不知權(quán),用于定制系統(tǒng)'
// Toast.makeText(MainActivity.this, "正在打開藍(lán)牙", Toast.LENGTH_SHORT).show();
// if (enable) {
// Log.e("open",enable+"");
// new Handler().postDelayed(new Runnable() {
// @Override
// public void run() {
// scanLeDevice(true);
// }
// },2000);
//
// }
?
//提示用戶正在打開藍(lán)牙
Intent enableBtIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
startActivityForResult(enableBtIntent, REQUEST_ENABLE_BT);
?
?
}
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (resultCode == RESULT_OK && requestCode == REQUEST_ENABLE_BT) {
scanLeDevice(true);
}
}
2.3.打開或者停止掃描,放在同一個(gè)方法中
/**
* 打開或者停止掃描
*
* @param enable
*/
private void scanLeDevice(final boolean enable) {
?
if (enable) {
mScanning = true;
// 定義一個(gè)回調(diào)接口供掃描結(jié)束處理
bluetoothAdapter.startLeScan(mLeScanCallback);
// 預(yù)先定義停止藍(lán)牙掃描的時(shí)間(因?yàn)樗{(lán)牙掃描需要消耗較多的電量)
new Handler().postDelayed(new Runnable() {
@Override
public void run() {
mScanning = false;
bluetoothAdapter.stopLeScan(mLeScanCallback);
}
}, SCAN_PERIOD);
?
} else {
mScanning = false;
bluetoothAdapter.stopLeScan(mLeScanCallback);
}
}
掃描回調(diào),回調(diào)之后得到 BluetoothDevice 的集合,可以放到列表中去
/**
* 掃描回調(diào)
*/
private BluetoothAdapter.LeScanCallback mLeScanCallback = new BluetoothAdapter.LeScanCallback() {
@Override
public void onLeScan(BluetoothDevice bluetoothDevice, int i, byte[] bytes) {
if (bluetoothDevice.getName() != null) {
if (!bluetoothDeviceArrayList.contains(bluetoothDevice)) {//去下重
bluetoothDeviceArrayList.add(bluetoothDevice);
}
Log.e(TAG, "scan--" + bluetoothDevice.getName());
}
}
};
4.5.進(jìn)行藍(lán)牙連接
/**
* 連接藍(lán)牙 參數(shù)為目標(biāo)設(shè)備
/
public void connectBle(BluetoothDevice bluetoothDevice) {
mBluetoothDevice = bluetoothDevice;
if (bluetoothDevice != null) {
//第二個(gè)參數(shù) 是否重連
mBluetoothGatt = bluetoothDevice.connectGatt(MainActivity.this, false, bluetoothGattCallback);
}
?
}
連接回調(diào)
/**
* 藍(lán)牙連接成功回調(diào)
*/
?
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);
}
?
//不要執(zhí)行耗時(shí)操作
@Override
public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) {
super.onConnectionStateChange(gatt, status, newState);
if (newState == BluetoothProfile.STATE_CONNECTED) {//連接成功
Log.e(TAG, "onConnectionStateChange 藍(lán)牙連接");
//這里要執(zhí)行以下方法,會(huì)在onServicesDiscovered這個(gè)方法中回調(diào),如果在 //onServicesDiscovered方法中回調(diào)成功,設(shè)備才真正連接起來,正常通信
gatt.discoverServices();
} else if (newState == BluetoothProfile.STATE_DISCONNECTED) {
Log.e(TAG, "onConnectionStateChange 藍(lán)牙斷連");
if (mBluetoothDevice != null) {
//關(guān)閉當(dāng)前新的連接
gatt.close();
characteristic = null;
}
?
}
?
}
?
@Override
public void onServicesDiscovered(BluetoothGatt gatt, int status) {
super.onServicesDiscovered(gatt, status);
//回調(diào)之后,設(shè)備之間才真正通信連接起來
if (status == BluetoothGatt.GATT_SUCCESS) {
Log.e(TAG, "onServicesDiscovered 藍(lán)牙連接正常");
BluetoothGattService service = gatt.getService(UUID.fromString(BleConstantValue.serverUuid));//uuid從硬件工程師獲取
characteristic = service.getCharacteristic(UUID.fromString(BleConstantValue.charaUuid));
gatt.readCharacteristic(characteristic);//執(zhí)行之后,會(huì)執(zhí)行下面的 onCharacteristicRead的回調(diào)方法
//設(shè)置通知,一般設(shè)備給手機(jī)發(fā)送數(shù)據(jù),需要以下監(jiān)聽
setCharacteristicNotification(characteristic, true);
//耗時(shí)操作,如果有ui操作,需要用到handler
adapterFreshHandler.sendEmptyMessage(0);
} else {
Log.e(TAG, "onServicesDiscovered 藍(lán)牙連接失敗");
}
?
}
?
//這個(gè)方法一般用不到
@Override
public void onCharacteristicRead(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) {
super.onCharacteristicRead(gatt, characteristic, status);
Log.e(TAG, "callback characteristic read status " + status
+ " in thread " + Thread.currentThread());
if (status == BluetoothGatt.GATT_SUCCESS) {
Log.e(TAG, "read value: " + characteristic.getValue());
}
?
?
}
?
//這個(gè)方法是寫入數(shù)據(jù)時(shí)的回調(diào),可以和你寫入的數(shù)據(jù)做對比
@Override
public void onCharacteristicWrite(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) {
super.onCharacteristicWrite(gatt, characteristic, status);
Log.e(TAG, "write value: " + FormatUtil.bytesToHexString(characteristic.getValue()));
}
?
//設(shè)備發(fā)出通知時(shí)會(huì)調(diào)用到該接口,藍(lán)牙設(shè)備給手機(jī)發(fā)送數(shù)據(jù),在這個(gè)方法接收
@Override
public void onCharacteristicChanged(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) {
super.onCharacteristicChanged(gatt, characteristic);
Log.e(TAG, "接收:" + FormatUtil.bytesToHexString(characteristic.getValue()));//byte[]轉(zhuǎn)為16進(jìn)制字符串
bleWriteReceiveCallback();
}
};
?
/**
* 設(shè)置通知
/
public void setCharacteristicNotification(BluetoothGattCharacteristic characteristic, boolean enabled) {
if (bluetoothAdapter == null || mBluetoothGatt == null) {
return;
}
mBluetoothGatt.setCharacteristicNotification(characteristic, enabled);
}
參考寫入指令
/**
* 寫入命令
*/
private void write(byte[] cmd) {
if (characteristic != null) {
// 發(fā)出數(shù)據(jù)
characteristic.setValue(cmd);
if (mBluetoothGatt.writeCharacteristic(characteristic)) {
Log.e(TAG, "寫入成功");
} else {
Log.e(TAG, "寫入失敗");
}
} else {
Toast.makeText(MainActivity.this, "藍(lán)牙未連接", Toast.LENGTH_SHORT).show();
}
}
6.斷開連接 釋放資源
/**
* 斷開藍(lán)牙設(shè)備
*/
public void bleDisConnectDevice(BluetoothDevice device) {
if (mBluetoothGatt != null) {
mBluetoothGatt.disconnect();
}
}
?
/**
* 釋放資源
*/
?
private void releaseResource() {
Log.e(TAG, "斷開藍(lán)牙連接,釋放資源");
if (mBluetoothGatt != null) {
mBluetoothGatt.disconnect();
mBluetoothGatt.close();
}
}
最后別忘了藍(lán)牙廣播:
/**
* 注冊藍(lán)牙監(jiān)聽廣播
*/
private void registerBleListenerReceiver() {
IntentFilter intentFilter = new IntentFilter();
intentFilter.addAction(BluetoothAdapter.ACTION_STATE_CHANGED);
intentFilter.addAction(BluetoothDevice.ACTION_ACL_CONNECTED);
intentFilter.addAction(BluetoothDevice.ACTION_ACL_DISCONNECTED);
registerReceiver(bleListenerReceiver, intentFilter);
}
/**
* 藍(lán)牙監(jiān)聽廣播接受者
*/
private BroadcastReceiver bleListenerReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
//連接的設(shè)備信息
BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
Log.e(TAG, "藍(lán)牙廣播" + action);
if (mBluetoothDevice != null && mBluetoothDevice.equals(device)) {
Log.e(TAG, "收到廣播-->是當(dāng)前連接的藍(lán)牙設(shè)備");
if (BluetoothDevice.ACTION_ACL_CONNECTED.equals(action)) {
Log.e(TAG,"廣播 藍(lán)牙已經(jīng)連接");
} else if (BluetoothDevice.ACTION_ACL_DISCONNECTED.equals(action)) {
Log.e(TAG,"廣播 藍(lán)牙斷開連接");
}
} else {
Log.e(TAG, "收到廣播-->不是當(dāng)前連接的藍(lán)牙設(shè)備");
}
if (BluetoothAdapter.ACTION_STATE_CHANGED.equals(action)) {
int state = intent.getIntExtra(BluetoothAdapter.EXTRA_STATE, BluetoothAdapter.ERROR);
switch (state) {
case BluetoothAdapter.STATE_OFF:
Log.e(TAG, "STATE_OFF 藍(lán)牙關(guān)閉");
adapter.clear();
releaseResource();
break;
case BluetoothAdapter.STATE_TURNING_OFF:
Log.e(TAG, "STATE_TURNING_OFF 藍(lán)牙正在關(guān)閉");
//停止藍(lán)牙掃描
scanLeDevice(false);
break;
case BluetoothAdapter.STATE_ON:
Log.d(TAG, "STATE_ON 藍(lán)牙開啟");
//掃描藍(lán)牙設(shè)備
scanLeDevice(true);
break;
case BluetoothAdapter.STATE_TURNING_ON:
Log.e(TAG, "STATE_TURNING_ON 藍(lán)牙正在開啟");
break;
}
}
}
};
所有完成,基本的藍(lán)牙操作及通信功能,通信協(xié)議需要和藍(lán)牙硬件廠商工程師獲取。本文適合藍(lán)牙入門開發(fā),如有錯(cuò)誤請指正 https://github.com/Leaderpaking/ble