前提說明:
去年有個項目是關于通過手機控制平衡車(小車)來實現加速檐薯、鎖車等一系列操作躬拢,其實就是通過藍牙來控制的窿侈,項目做了很長時間了,一直想把有關藍牙的這個模塊自己記錄下來方便自己查看鸽心,也算知識的一個積累吧滚局。
自己第一次接觸藍牙開發(fā)的項目,對藍牙這塊不太熟悉顽频,然后經過一番搜索找到的都是以下的藍牙開發(fā)藤肢,自己也開始做起來,以為很簡單糯景,直接上代碼了嘁圈。
步驟(不支持BLE):
1、搜索藍牙設備莺奸;
<pre>
// 1丑孩、獲取系統(tǒng)的藍牙適配器
bluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
// 2冀宴、搜索藍牙設備(搜索過程中會發(fā)出廣播灭贷,可以注冊廣播,從廣播中搜索到我們需要的設備)
bluetoothAdapter.startDiscovery();
</pre>
簡單說明:
- bluetoothAdapter.startDiscovery():是異步方法略贮,它會對其他藍牙設備進行搜索甚疟,持續(xù)時間為12秒;
- 搜索過程其實是在System Service中進行逃延,我們可以通過cancelDiscovery()方法來停止這個搜索览妖;
- 在系統(tǒng)搜索藍牙設備的過程中,系統(tǒng)可能會發(fā)送以下三個廣播:
ACTION_DISCOVERY_START(開始搜索)
ACTION_DISCOVERY_FINISHED(搜索結束)
ACTION_FOUND(找到設備) - ACTION_FOUND這個才是我們想要的揽祥,這個Intent中包含兩個extra fields:EXTRA_DEVICE讽膏、EXTRA_CLASS,包含的分別是BluetoothDevice和BluetoothClass拄丰,BluetoothDevice中的EXTRA_DEVICE就是我們搜索到的設備對象府树;確認搜索到設備后俐末,我們可以從得到的BluetoothDevice對象中獲得設備的名稱和地址;
2奄侠、獲取設備卓箫、配對、連接垄潮;
<pre>
// 3烹卒、注冊系統(tǒng)廣播,得到我們需要的設備
IntentFilter filter = new IntentFilter(BluetoothDevice.ACTION_FOUND);
receiver = new BluetoothReceiver();
registerReceiver(receiver, filter);
// 廣播
private class BluetoothReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
if (BluetoothDevice.ACTION_FOUND.equals(action)) {
BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
if (isLock(device)) {
devices.add(device.getName());
}
deviceList.add(device);
}
showDevices();
}
}
// 4弯洗、獲取設備旅急,并配對(只有配對好之后才能連接)
// 在連接設備之前停掉搜索設備,否則連接可能會變得非常慢并且容易失敗
bluetoothAdapter.cancelDiscovery();</pre>
3涂召、連接后獲取輸入輸出流和藍牙設備進行通信坠非;
4、斷開連接果正、關閉藍牙炎码;
<pre>
這兩個步驟不說明,因為到這里我就發(fā)現問題了秋泳,
開發(fā)文檔上說明找到uuid潦闲,在通道上發(fā)送和接收命令,
我卻連個通道都沒有見到過迫皱,找了很久都是以上這樣的文章歉闰,
后來再從頭閱讀文檔,發(fā)現了藍牙BLE卓起,
呵呵~和敬,懵逼了吧,再重新去搜索戏阅,又是另一番景象了昼弟。
</pre>
藍牙BLE介紹(摘抄)
- 在BLE協議中,有兩個角色奕筐,周邊(Periphery)和中央(Central)舱痘;
- 周邊是數據提供者,中央是數據使用/處理者离赫;
- 在Android SDK里面芭逝,在Android4.4.2,Android手機只能作為中央來使用和處理數據渊胸;那數據從哪兒來旬盯?從BLE設備來,現在的很多可穿戴設備都是用BLE來提供數據的。
- 一個中央可以同時連接多個周邊胖翰,但是一個周邊某一時刻只能連接一個中央频丘。
藍牙BLE相關API:
- BluetoothAdapter(系統(tǒng)默認藍牙適配器)
- BluetoothManager(藍牙管理類)
- BluetoothDevice
- BluetoothGattServer作為周邊來提供數據、BluetoothGattServerCallback返回周邊的狀態(tài)泡态;
- BluetoothGattService(服務):每一個周邊BluetoothGattServer搂漠,包含多個服務Service;
- BluetoothGattCharacteristic(特征):每一個Service包含多個特征Characteristic某弦;
- BluetoothGatt作為中央來使用和處理數據桐汤、BluetoothGattCallback返回中央的狀態(tài)和周邊提供的數據;
步驟:
1靶壮、開啟手機藍牙怔毛、搜索設備、取消搜索設備和以上相同腾降;
2拣度、藍牙連接,發(fā)現服務螃壤、讀寫數據全部在回調里可以監(jiān)聽到:
<pre>
mBluetoothGatt = device.connectGatt(context, false, mGattCallback);
// 回調
private final BluetoothGattCallback mGattCallback = new BluetoothGattCallback() {
@Override
public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) {
final String intentAction;
if (newState == BluetoothProfile.STATE_CONNECTED)
{
intentAction = ACTION_GATT_CONNECTED;
// 開啟查找服務
mBluetoothGatt.discoverServices();
} else if (newState == BluetoothProfile.STATE_DISCONNECTED) {
// 斷開連接
// Log.d(TAG, "Disconnected from GATT server.");
intentAction = ACTION_GATT_DISCONNECTED;
}
if (status == 133) { // 藍牙連接自動斷開的原因->需要清除所有的連接抗果,重連機制
// Log.e(TAG, "藍牙連接自動斷開,status=" + status);
close();
sleep();
connect(mBluetoothDeviceAddress);
}
}
@Override
public void onServicesDiscovered(BluetoothGatt gatt, int status) {
// Log.d(TAG, "onServicesDiscovered received: " + status);
if (status == BluetoothGatt.GATT_SUCCESS) {
// 解析服務
displayGattServices(getSupportedGattServices());
}
}
@Override
public void onCharacteristicRead(BluetoothGatt gatt,
BluetoothGattCharacteristic characteristic,
int status) {
}
@Override
public void onCharacteristicChanged(BluetoothGatt gatt,
BluetoothGattCharacteristic characteristic) {
}
@Override public void onCharacteristicWrite(BluetoothGatt gatt,
BluetoothGattCharacteristic characteristic, int status) {
}
};
</pre>
解析服務,因為我是在FFE0奸晴、FFE5通道上操作的冤馏,所以只需要找到這兩個通道即可:
<pre>
private void displayGattServices(List<BluetoothGattService> gattServices) {
if (gattServices == null) {
return;
}
for (BluetoothGattService gattService : gattServices) {
//-----Service的字段信息-----//
// Log.e(TAG,"-->service uuid:"+gattService.getUuid());
String serviceUUID = gattService.getUuid().toString().toUpperCase();
if (serviceUUID.contains(DefaultConstant.SERVICE_UUID_FFE0))
{
List<BluetoothGattCharacteristic> gattCharacteristics = gattService.getCharacteristics();
for (final BluetoothGattCharacteristic gattCharacteristic : gattCharacteristics) {
// Log.e(TAG, "---->char uuid:" + gattCharacteristic.getUuid());
String charUUID = gattCharacteristic.getUuid().toString().toUpperCase();
if (charUUID.contains(DefaultConstant.CHAR_UUID_FFE4)) {
mCharacFFE4 = gattCharacteristic;
// 打開通道,只有打開了通道寄啼,才能在通道上進行讀和寫
mBluetoothGatt.setCharacteristicNotification(gattCharacteristic, true);
break;
}
}
}
if (serviceUUID.contains(DefaultConstant.SERVICE_UUID_FFE5))
{
List<BluetoothGattCharacteristic> gattCharacteristics = gattService.getCharacteristics();
for (final BluetoothGattCharacteristic gattCharacteristic : gattCharacteristics) {
// Log.e(TAG, "---->char uuid:" + gattCharacteristic.getUuid());
String charUUID = gattCharacteristic.getUuid().toString().toUpperCase();
if (charUUID.contains(DefaultConstant.CHAR_UUID_FFE9)) {
mCharacFFE9 = gattCharacteristic;
break;
}
}
}
if (mCharacFFE4 != null && mCharacFFE9 != null) {
break;
}
}
}
</pre>
實際的值如下所示:
3逮光、讀寫完成后記得關閉連接;
總結:最后總算順利的完成了項目的開發(fā)墩划,但是也還是遇到了很多的問題涕刚,這里自己做下記錄:
- 某些手機不支持藍牙4.0設備,記得做事先的判斷乙帮;(一般4.3及以上手機才支持)
- 搜索藍牙設備過程很緩慢杜漠,且項目要求一直都需要開啟搜索,但是我還是在連接成功后取消了搜索蚣旱,一個是為了加快藍牙的連接碑幅,一個也是為了省電戴陡,畢竟是很耗電的塞绿,既然已經連接了為啥還要一直在搜索呢;為了讓用戶視覺上覺得搜索很快恤批,記得搜索到一個設備就立即展示异吻,否則可能搜到你需要的設備會等很長時間,這樣也能起到緩沖作用;
-
藍牙的連接诀浪,最好能對非BLE設備或者不是你需要的設備連接的時候做迅速的處理棋返,因為我試過,故意去連接某些電腦或者不知名設備的時候會有一個很長時間的響應雷猪,最后我自己處理是找到了BluetoothDevice的type
bluetooth device type.png簡單的判斷了下睛竣,是否是BLE設備,直接斷開不去連接求摇;
- 藍牙的連接射沟、讀寫過程我都是通過發(fā)送廣播進行處理,項目需要在所有頁面都能監(jiān)聽到設備的連接的狀態(tài)以及在某些頁面需要進行讀寫与境,干脆我改成了單例+廣播的形式验夯,這里要注意內存的泄漏哦;
-
藍牙的斷開不徹底問題:斷開小車的時候總是斷開的不徹底摔刁,導致別人手機搜索不到挥转,自己也不能再去搜索連接,據說是小車有反應的時間共屈,經過多次測試后绑谣,發(fā)現每次斷開不徹底會返回133狀態(tài),至今沒弄明白拗引,只能這樣處理了域仇,表示很無奈,徹底關閉寺擂、沉睡500毫秒暇务,再次重連;
徹底斷開處理.png
或者再多加層判斷:
徹底斷開重連處理.png - 還漏寫了一個藍牙讀寫全是二進制數據挡逼,這里面的轉換也要注意括改,避免溢出;
好不容易鼓起自己去寫了家坎,一定會再接再勵嘱能,代碼自己會抽時間把之前寫在項目中的代碼抽離出來寫個demo放github上去,知識需要沉淀虱疏。
添加github demo地址惹骂,寫的較簡單,后續(xù)逐漸完善https://github.com/ywqian/BluetoothDemo做瞪;
參考和好的鏈接文章:
http://www.pinnace.cn/bluetooth/tech/1940.shtml
http://www.cnblogs.com/savagemorgan/p/3722657.html
一個不錯的github列子:https://github.com/alt236/Bluetooth-LE-Library---Android