1. 程式人生 > >三步走--低功耗藍芽BLE開發實戰

三步走--低功耗藍芽BLE開發實戰

BLE是Android4.3以上加入的新功能,他可以很大程度上節省了裝置的功耗,他會在啟用的時候進入一個快速的廣播段,這時候周圍的裝置可以搜尋到BLE裝置,當匹配成功的時候就會建立一個長連線,如果沒有匹配成功,他就會在一段時間後自動進入相對慢速的廣播段,給周圍裝置傳送的廣播頻率也會大大減少,直到沒有裝置與他匹配成功的時候,會自動停止傳送廣播,處於關閉狀態,周圍的裝置也無法搜尋到此BLE裝置。

1.開始掃描

BluetoothAdapter.getDefaultAdapter().startLeScan(null, leScanCallback);//開始掃描,就可以一直搜尋周圍的藍芽
2.掃描的結果在callback裡,new一個callback,回撥裡的資訊都在device裡,比如name,mac等等,一般我在這裡會去過濾一些mac地址來指定連線哪個BLE裝置
//
掃描的回撥 BluetoothAdapter.LeScanCallback leScanCallback = new BluetoothAdapter.LeScanCallback() { @Override public void onLeScan(BluetoothDevice device, int rssi, byte[] scanRecord) {if (isConnecting || isConnected() || TextUtils.isEmpty(device.getName()) || TextUtils.isEmpty(device.getAddress()) ) { return;
}if (!nameList.contains(device.getName())) {nameList.add(device.getName()); UIUtils.sendBleMes("DEVICENAME", device.getName()); } if(!TextUtils.isEmpty(last_DeviceName)&&last_DeviceName.equals(device.getName())){//如果掃描到上次儲存到sp的裝置,就直接連線 tryConnect(device); return; } if
(tryconnect){ if(device.getName().equals(connectingName)){ connectingName = ""; tryconnect = false; tryConnect(device); } } } };
3.如果上一步中的device經過一系列判斷是我們自己的device,那麼就執行下一步連線的操作
//嘗試連線
private void tryConnect(BluetoothDevice device) {
    isConnecting = true;//正在嘗試連線
device.connectGatt(context, false, bluetoothGattCallback);
}
跟上一步一樣,連線這個BLE裝置的結果依然在一個callback中,也是最重要的一個callback,如下:

1.onConnectionStateChange,名字就是意思,即連線狀態的回撥,可以通過與BluetoothProfile下的狀態值進行判斷來知曉裝置的連線狀態,記住,不要以為連線了裝置就完了,在BLE連線到裝置的時候我們並不認為是真正意義上的連線,BLE裡面有很多個service,只有service判斷是我們自己定義的時候才能真正確定為是我們自己的裝置了

所以下面我在判斷連線裝置狀態ok的時候,還會discovewServices(),這個方法會回撥在同樣的這個callback的OnServiceDiscovered方法中

2.OnServiceDiscovered,即判斷service的回撥,如果在這裡判斷服務狀態OK,並且service的uuid跟我們自己BLE裝置service定義的uuid相同,此時恭喜你,你已經找到了自己的裝置了,找到自己的裝置後你還有兩步非常重要的事,1)停止掃描,因為BLE即使成功連線了裝置依然會不停的掃描周圍的裝置,2)我們要開啟這個service下的Characteristic,為什麼要開啟?你可以把他想象成是一個開關,他打開了,你的BLE裝置發出的指令(實際上就是一些Characteristic中包括的value和Descriptor)才能傳送到連線的裝置上

4.onCharacteristicChanged,當上一步你打開了開關後,以後你的BLE裝置傳送的指令都會在這個方法中回撥給你,你可一定一個map集合,裝載你的鍵值對,更好管理

程式碼如下:

//連線的回撥
BluetoothGattCallback bluetoothGattCallback = new BluetoothGattCallback() {

    @Override
public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) {
        super.onConnectionStateChange(gatt, status, newState);
LogUtils.i("BluetoothGattCallback" + "--onConnectionStateChange()--" + gatt.getDevice().getName() + "," + newState);
        if (newState == BluetoothProfile.STATE_CONNECTED) {
            LogUtils.i("onConnectionStateChange:已連線到裝置" + gatt.getDevice().getName() + ",正在搜尋服務");
isConnecting = false;//從未連線的集合中去掉
gatt.discoverServices();
} else if (newState == BluetoothProfile.STATE_DISCONNECTED) {
            LogUtils.i("onConnectionStateChange:裝置斷開連線:" + gatt.getDevice().getName());
isConnecting = false;
isConnected = false;
ShowToast.show(UIUtils.getContext(), "裝置斷開連線:" + gatt.getDevice().getName());
            if (Values.whoistop[0]) {
                UIUtils.sendBleMes("FOUND_DEVICES", "UNFOUNDED");
}
            //context.sendBroadcast(new Intent(BROADCAST_ACTION_CONNECTED_CHANGED));//傳送一個連線改變的廣播
/*if(BluetoothAdapter.getDefaultAdapter().getState() == BluetoothAdapter.STATE_ON){
                close();
                startScan();
            }*/
}
    }

    @Override
public void onServicesDiscovered(final BluetoothGatt gatt, int status) {
        LogUtils.i("onServicesDiscovered" + gatt.getDevice().getName()
                + ", status" + status + ", isConnected = " + isConnected());
        if (isConnected()) {
            return;
}
        isConnected = false;
//connectingAddress.remove(getDevieNameAndAddress(gatt.getDevice()));
if (status == BluetoothGatt.GATT_SUCCESS) {//A GATT operation completed successfully
for (final BluetoothGattService server : gatt.getServices()) {
                if (!isConnected) {
                    isConnected = isOwnerUUID(server.getUuid().toString());//判斷這個server是不是使用者定義的uuid,如果是就為真,只有這裡isConnected才為真
LogUtils.i("onServicesDiscovered:服務UUID匹配狀況:" + server.getUuid().toString() + ":連線狀況:" + isConnected);
                    if (isConnected) {
                        bluetoothGatt = gatt;//費勁千辛萬苦找到了gatt
stopScan();
LogUtils.i("onServicesDiscovered:停止掃描,已連線到到裝置:" + gatt.getDevice().getName());
ShowToast.show(UIUtils.getContext(), "找到裝置:" + gatt.getDevice().getName());
                        if (Values.whoistop[0]) {
                            UIUtils.sendBleMes("FOUND_DEVICES", gatt.getDevice().getName());
}
                        setCharacteristicNotification(gatt, server, User_Definition_Characteristic_UUID);
}
                    //context.sendBroadcast(new Intent(BROADCAST_ACTION_CONNECTED_CHANGED));//也傳送一個連線改變的廣播
}
                //setCharacteristicNotification(gatt, server, Battery_Service_UUID);//設定電池
}
        } else {
            isConnected = false;
//context.sendBroadcast(new Intent(BROADCAST_ACTION_CONNECTED_CHANGED));
LogUtils.i("onServicesDiscovered:Gatt未連線成功");
}
    }

    @Override
public void onCharacteristicChanged(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) {
        super.onCharacteristicChanged(gatt, characteristic);
LogUtils.i("onCharacteristicChanged:" + characteristic.getUuid() + ", value=" + bytesToHexString(characteristic.getValue()));
/*if (characteristic.getService().getUuid().toString().equals(Battery_Service_UUID)) {
            LogUtils.i("onCharacteristicChanged:Battery_Service_UUID" + Battery_Service_UUID);
            //此處可以處理電池service資訊            return;
        }*/
        //如果發過來的指令不包含在我們定義的keymap裡面,就不處理
if (uiHander == null || !KEY_MAP.containsKey(bytesToHexString(characteristic.getValue()))) {
            LogUtils.i("傳送的Characteristic指令不存在或者uiHander為空");
            return;
}
        //走到這裡說明是我們自己的鍵值對
Integer what = KEY_MAP.get(bytesToHexString(characteristic.getValue()));
uiHander.removeMessages(what);
uiHander.sendEmptyMessageDelayed(what, 100);
}
};
//開啟Notification,才能接受發過來的Characteristic資料
private void setCharacteristicNotification(BluetoothGatt gatt, BluetoothGattService server, String uuid) {
    BluetoothGattCharacteristic characteristic = server.getCharacteristic(UUID.fromString(uuid));//根據一個UUID,獲得一個服務下的Characteristic
if (characteristic != null) {
        LogUtils.i("進入到setCharacteristicNotification() + " + uuid);
gatt.setCharacteristicNotification(characteristic, true);}
}
//停止掃描
public void stopScan() {BluetoothAdapter.getDefaultAdapter().stopLeScan(leScanCallback);
}