Android 藍牙4.0開發
1、 權限和相關屬性
“android:required="true"表示apk只有在具有bluetooth_le屬性的系統裏運行,這個4.3之前android系統沒有
<uses-featureandroid:name="android.hardware.bluetooth_le"android:required="true"/>
<uses-permissionandroid:name="android.permission.BLUETOOTH"/>
<uses-permissionandroid:name="android.permission.BLUETOOTH_ADMIN"/>
2、 程序開媽操作藍牙之前,先判斷ble是否支持
if(!getPackageManager().hasSystemFeature(PackageManager.FEATURE_BLUETOOTH_LE)) {
Toast.makeText(this,R.string.ble_not_supported, Toast.LENGTH_SHORT).show();
finish();
}
3、 打開、關閉藍牙
先獲取藍牙的一個代理
final BluetoothManager bluetoothManager =
(BluetoothManager)getSystemService(Context.BLUETOOTH_SERVICE);
mBluetoothAdapter = bluetoothManager.getAdapter();
發intent通知系統打開藍牙
if(!mBluetoothAdapter.isEnabled()) {
if (!mBluetoothAdapter.isEnabled()){
Intent enableBtIntent = newIntent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
startActivityForResult(enableBtIntent, REQUEST_ENABLE_BT);
}
}
也可以使用 enable 和disable函數來打開關閉
4、 搜索ble設備
mHandler.postDelayed(newRunnable() {
@Override
public void run() {
mScanning = false;
mBluetoothAdapter.stopLeScan(mLeScanCallback);
}
}, SCAN_PERIOD);
mScanning = true;
mBluetoothAdapter.startLeScan(mLeScanCallback);
//SCAN_PERIOD是10000,表示每次的搜索時間爲10秒
需要注意的是mLeScanCallback,在4.3之前的api是通過註冊廣播來處理搜索時發生的一些事件,而支持ble的新的api中,是通過回調的方式來處理的,mLeScanCallback就是一個接口對象,看一下實現:
privateBluetoothAdapter.LeScanCallback mLeScanCallback =
newBluetoothAdapter.LeScanCallback() {
@Override
public void onLeScan(finalBluetoothDevice device, int rssi, byte[] scanRecord) {
runOnUiThread(new Runnable() {
@Override
public void run() {
mLeDeviceListAdapter.addDevice(device);
mLeDeviceListAdapter.notifyDataSetChanged();
}
});
}
};
5、 連接
4.3之前的api是通過socket方式在藍牙之間互相通信,連接的結果是返回一個socket對象
在支持4.0藍牙的新的api中,返回的是BluetoothGatt對象
可以將BluetoothGatt對象看成是遠程設備的一個代理
mBluetoothGatt = device.connectGatt(this, false,mGattCallback);
mGattCallback是一個抽象類對象,之前的廣播形式,在新的api中都改成了回調
BluetoothGattCallback抽象類,只有9個方法,字面意思就都可以看懂,在處理連接事件上,需要處理方法:
public voidonConnectionStateChange(BluetoothGatt gatt, int status,
intnewState)
條件:if (newState ==BluetoothProfile.STATE_CONNECTED)
else if (newState ==BluetoothProfile.STATE_DISCONNECTED)分別表示已連接和已斷開
6、 通訊
這一點與之前形式上完全不一樣了
BLE分爲三部分Service、Characteristic、Descriptor,這三部分都由UUID作爲唯一標示符。一個藍牙4.0的終端可以包含多個Service,一個Service可以包含多個Characteristic,一個Characteristic包含一個Value和多個Descriptor,一個Descriptor包含一個Value。
在連接上某個終端後,可以將其每個結點的UUID全部打印出來,但每個結點不是都能讀寫。
一般來說,Characteristic是手機與BLE終端交換數據的關鍵,Characteristic有較多的跟權限相關的字段,例如PERMISSION和PROPERTY,而其中最常用的是PROPERTY,本文所用的BLE藍牙模塊竟然沒有標準的Characteristic的PERMISSION。Characteristic的PROPERTY可以通過位運算符組合來設置讀寫屬性,例如READ|WRITE、READ|WRITE_NO_RESPONSE|NOTIFY,因此讀取PROPERTY後要分解成所用的組合
我是這麼去和ble終端通信的:
得到某個service的對象
BluetoothGattService linkLossService =mBluetoothGatt
.getService(UUID.fromString("49535343-fe7d-4ae5-8fa9-9fafd205e455"));
一般說來,ble設備都帶有幾個標準的服務,其UUID已經定義好了,這些結點裏的值只能讀了,因爲我一個一個試過了,終於找到了我的設備裏可以讀寫的服務,其中49535343-fe7d-4ae5-8fa9-9fafd205e455就是對應這個服務的
獲取此服務結點下的某個Characteristic對象
BluetoothGattCharacteristic alertLevel =linkLossService.getCharacteristic(UUID.fromString("49535343-8841-43f4-a8d4-ecbe34729bb3"));
一般供應商會給出多個Characteristic,你需要找到到底哪個纔是讓你去寫的,怎麼找需要看對應的終端的一些開發文檔之類的,在這裏我經過測試已經找到我要的了
設置要寫的值
alertLevel.setValue(values_on);
這裏的values_on是一個byte數組
寫
status = mBluetoothGatt.writeCharacteristic(alertLevel);
status如果爲true,表示寫操作已經成功執行,BluetoothGattCallback抽象類的一個方法會被執行,如果剛好你又重寫了這個方法,就可以打印一些消息了
public void onCharacteristicWrite(BluetoothGatt gatt,
BluetoothGattCharacteristiccharacteristic, int status)
讀某個Characteristic
public void readCharacteristic(BluetoothGattCharacteristiccharacteristic) {
if (mBluetoothAdapter == null || mBluetoothGatt == null) {
Log.w(TAG, "BluetoothAdapter not initialized");
return;
}
mBluetoothGatt.readCharacteristic(characteristic);
}
如果成功,數據會在下面的方法回調中傳進來
public voidonCharacteristicRead(BluetoothGatt gatt,
BluetoothGattCharacteristic characteristic,
int status)
當終端有數據要傳過來的時候,表面上正常的話,手機這邊下面的方法會被調用
public voidonCharacteristicRead(BluetoothGatt gatt,
BluetoothGattCharacteristic characteristic,
intstatus)
這個也是可以控制的,設置descriptor的value不同,可以控制這個重寫的方法是否會被調用,沒有測試其他的設備,感覺這個應該是會對應不同的設備,具體設置的地方會有不同,在我這邊是這麼操作的:
public void enableNotification(boolean b)
{
if(b)
{
BluetoothGattService service =mBluetoothGatt
.getService(UUID.fromString("49535343-fe7d-4ae5-8fa9-9fafd205e455"));
BluetoothGattCharacteristicale =service.getCharacteristic(UUID.fromString("49535343-1E4D-4BD9-BA61-23C647249616"));
booleanset = mBluetoothGatt.setCharacteristicNotification(ale, true);
Log.d(TAG," setnotification = " + set);
BluetoothGattDescriptordsc =ale.getDescriptor(UUID.fromString("00002902-0000-1000-8000-00805f9b34fb"));
byte[]bytes = {0x01,0x00};
dsc.setValue(bytes);
boolean success =mBluetoothGatt.writeDescriptor(dsc);
Log.d(TAG, "writing enabledescriptor:" + success);
}
else
{
BluetoothGattService service =mBluetoothGatt
.getService(UUID.fromString("49535343-fe7d-4ae5-8fa9-9fafd205e455"));
BluetoothGattCharacteristicale =service.getCharacteristic(UUID.fromString("49535343-1E4D-4BD9-BA61-23C647249616"));
booleanset = mBluetoothGatt.setCharacteristicNotification(ale, false);
Log.d(TAG," setnotification = " + set);
BluetoothGattDescriptordsc =ale.getDescriptor(UUID.fromString("00002902-0000-1000-8000-00805f9b34fb"));
byte[]bytes = {0x00, 0x00};
dsc.setValue(bytes);
boolean success =mBluetoothGatt.writeDescriptor(dsc);
Log.d(TAG, "writing enabledescriptor:" + success);
}
}
7、 總結
網上的一些資料大都以上面的命名來標識自己的文檔,有必要解釋一下,應該分開來看這個命題:
android指的安裝的4.3及以上版本的android系統的設備
4.0藍牙指的藍牙芯片使用4.0協議的設備
這種開發的一種標準用處是:用4.3以上android版本的手機,與4.0藍牙穿戴式設備進行通信
按網上的一種中央與周邊的說法,手機就是中央,4.0藍牙設備就中周邊
如果要開發4.0藍牙,應該知道4.0藍牙具有高速、低功耗的優點,這些優點對手機的提升不大,但對其他一些終端設備的提升就比較大。
有意思的是,android對與4.0藍牙通信的封裝,不需要本身設備的藍牙芯片是4.0協議的藍牙芯片
於是android 藍牙4.0開發的這麼一個“大環境”下的真實情景就是:一個沒有必要擁有藍牙4.0協議的藍牙芯片的android4.3以上系統的手機,與一些4.0藍牙協議的藍牙芯片設備終端的故事
以上是一些事實,以下是一些猜想
1、 藍牙4.0與之前版本協議之間可以通訊,說明:4.0藍牙協議並不是修改的無線波的調製與解調,而是修改的數據的組成
2、 對藍牙4.0協議的支持,是由google提出的,而不是各個手機廠商提出的,說明:android系統在軟件上可以一致對待不同的藍牙芯片,不同的藍牙芯片對同一段數據的調製解調結果是一樣的,於是在這段數據通過串口傳到手機主控的時候,也是一樣的,在這個環境裏,藍牙芯片只是一個調制解調器,android封裝了對數據全部的處理。