android ble藍芽開發略解
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封裝了對資料全部的處理。