概述
- 中心角色和外圍角色
在BLE中存在兩個(gè)角色苟径,一個(gè)是中心角色(Central),一個(gè)是外圍角色(Peripheral),藍(lán)牙設(shè)備或手機(jī)都可以單獨(dú)作為Central或Peripheral角色倾贰。外設(shè)角色的作用是為中心角色提供各種數(shù)據(jù)水评,中心角色可以?huà)呙璨⒔邮斩鄠€(gè)外設(shè)角色數(shù)據(jù)( 外圍角色中的設(shè)備進(jìn)行廣播浑玛,中心角色的設(shè)備掃描尋找廣播)趾断,數(shù)據(jù)以服務(wù)(Service)和特征(Characteristic)的形式呈現(xiàn)拒名。其中Ble中心角色的API在Android 4.3
得到支持,而外圍角色的API在Android 5.0
才得到支持 - 協(xié)議芋酌、服務(wù)增显、特征、描述符
一份協(xié)議(BluetoothGatt)由一個(gè)或多個(gè)服務(wù)(BluetoothGattService)構(gòu)成脐帝,一個(gè)服務(wù)由零個(gè)或多個(gè)特征(BluetoothGattCharacteristic)構(gòu)成同云,一個(gè)特征可以包含零個(gè)或多個(gè)描述符(BluetoothGattDescriptor)。每一個(gè)服務(wù)堵腹、特征炸站、描述符都有一個(gè)UUID作為唯一識(shí)別符,識(shí)別符有通用的疚顷,也可以自定義旱易,也可以隨機(jī)生成,固定格式00000000-0000-0000-0000-000000000000(8-4-4-4-12)腿堤,一般來(lái)說(shuō)自定義的UUID只有前8位有變化阀坏,后面的基本是固定的0000-1000-8000-00805f9b34fb,所以一個(gè)自定義的UUID一般看起來(lái)就像這樣 “0000????-0000-1000-8000-00805f9b34fb” 通配符就表示4個(gè)16進(jìn)制數(shù)笆檀。每一個(gè)特征都有其屬性和權(quán)限(Read | Write | Notify | Indicate)忌堂,特征根據(jù)屬性可讀可寫(xiě)。在每個(gè)Ble藍(lán)牙設(shè)備中酗洒,都會(huì)有兩個(gè)默認(rèn)的服務(wù)如下:
//Generic Access(Generic Attribute Profile 通用屬性規(guī)范GATT)
service:00001801-0000-1000-8000-00805f9b34fb
characteristic:00002a05-0000-1000-8000-00805f9b34fb
//Generic Attribute (Generic Access Profile 通用接入規(guī)范GAP)
service:00001800-0000-1000-8000-00805f9b34fb
characteristic:00002a00-0000-1000-8000-00805f9b34fb
characteristic:00002a01-0000-1000-8000-00805f9b34fb
characteristic:00002aa6-0000-1000-8000-00805f9b34fb
- 適配器士修,掃描器:
每一臺(tái)支持藍(lán)牙的手機(jī)中都會(huì)有一個(gè)藍(lán)牙適配器,由藍(lán)牙管理器管理著樱衷,從其中獲得藍(lán)牙適配器棋嘲。適配器中自帶掃描器,使用掃描器可以?huà)呙柚苓叺乃{(lán)牙設(shè)備箫老。
Ble中常用類(lèi)
- BluetoothDeivce:藍(lán)牙設(shè)備封字,代表一個(gè)具體的藍(lán)牙外設(shè)
- BluetoothManager:藍(lán)牙管理器,主要用于獲取藍(lán)牙適配器和管理所有和藍(lán)牙相關(guān)的東西
- BluetoothAdapter:藍(lán)牙適配器耍鬓,每一臺(tái)支持藍(lán)牙功能的手機(jī)都有一個(gè)藍(lán)牙適配器阔籽,一般來(lái)說(shuō),只有一個(gè)牲蜀,可以通過(guò)BluetoothManager獲取
- BluetoothLeScanner:藍(lán)牙適配器里面的掃描器笆制,用于掃描BLE外設(shè),可以通過(guò)BluetoothAdapter獲取
- BluetoothGatt:通用屬性協(xié)議涣达,定義了BLE通訊的基本規(guī)則在辆,就是通過(guò)把數(shù)據(jù)包裝成服務(wù)和特征的約定過(guò)程证薇,可以通過(guò)建立連接獲取
- BluetoothGattService:服務(wù),描述了一個(gè)BLE設(shè)備的一項(xiàng)基本功能匆篓,由零或多個(gè)特征組構(gòu)成浑度,可以通過(guò)BluetoothGatt獲取并自定義(現(xiàn)有的約定服務(wù)可以在bluetooth.org上找到)
- BluetoothGattCallback:作為中央的回調(diào)類(lèi),用于回調(diào)GATT通信的各種狀態(tài)和結(jié)果
- BluetoothGattServerCallback:作為周邊的回調(diào)類(lèi)鸦概,用于回調(diào)GATT通信的各種狀態(tài)和結(jié)果
- BluetoothGattCharacteristic:特征箩张,里面包含了一組或多組數(shù)據(jù),是GATT通信中的最小數(shù)據(jù)單元窗市。
- BluetoothGattDescriptor:特征描述符先慷,對(duì)特征的額外描述,包括但不僅限于特征的單位咨察,屬性等论熙。
- BluetoothProfile:一個(gè)通用的規(guī)范,按照這個(gè)規(guī)范來(lái)收發(fā)數(shù)據(jù)
使用流程
中心設(shè)備
:判斷藍(lán)牙是否可用->打開(kāi)藍(lán)牙->開(kāi)始掃描->獲取被掃描到的設(shè)備->連接設(shè)備->發(fā)現(xiàn)服務(wù)->獲取到指定特征->寫(xiě)入特征值
外圍設(shè)備
:判斷藍(lán)牙是否可用->打開(kāi)藍(lán)牙->創(chuàng)建廣播數(shù)據(jù)->發(fā)送廣播->添加服務(wù)至廣播->根據(jù)監(jiān)聽(tīng)獲取寫(xiě)入的數(shù)據(jù)
下圖是中心設(shè)備的使用流程圖 來(lái)源
權(quán)限聲明
<uses-permission android:name="android.permission.BLUETOOTH"/>
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN"/>
//獲取掃描結(jié)果權(quán)限摄狱,Android6.0以上需要?jiǎng)討B(tài)權(quán)限(這兩個(gè)權(quán)限是在經(jīng)典藍(lán)牙的掃描中需要聲明的)
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
//或者
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
//當(dāng)android:required為true的時(shí)候脓诡,app只能強(qiáng)制運(yùn)行在支持BLE功能的設(shè)備商,為false的時(shí)候二蓝,可以運(yùn)行在所有設(shè)備上誉券,但某些方法需要手動(dòng)檢測(cè),否則可能存在隱性BUG
<uses-feature android:name="android.hardware.bluetooth_le" android:required="false"/>
Ble可用性判斷
不是任何設(shè)備都支持BLE刊愚,最開(kāi)始要確定設(shè)備是否支持踊跟,還要確定藍(lán)牙已經(jīng)打開(kāi)。
- Android系統(tǒng)從4.3以后才支持鸥诽,這是先決條件
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2){
... ...
}
- 判斷藍(lán)牙是否可用
//小于等于API17時(shí)直接使用BluetoothAdapter.getDefaultAdapter()來(lái)獲取Adapter
private BluetoothAdapter getAdapter(){
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.JELLY_BEAN_MR2) {
mBluetoothManager = (BluetoothManager) getSystemService(Context.BLUETOOTH_SERVICE);
mBluetoothAdapter = mBluetoothManager.getAdapter();
} else {
mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
}
return mBluetoothAdapter;
}
if(mBluetoothAdapter == null){
//藍(lán)牙不可用
}
//Ble不可用
private boolean checkIfSupportBle(){
return getPackageManager().hasSystemFeature(PackageManager.FEATURE_BLUETOOTH_LE);
}
藍(lán)牙開(kāi)啟
//開(kāi)啟藍(lán)牙功能需要一小段時(shí)間商玫,所以不能執(zhí)行開(kāi)啟藍(lán)牙立即執(zhí)行其他操作,這時(shí)藍(lán)牙實(shí)際還沒(méi)有開(kāi)啟牡借,回出現(xiàn)異常拳昌,所以后續(xù)操作應(yīng)該在藍(lán)牙狀態(tài)廣播中處理
private void enableBluetooth(){
if (mBluetoothAdapter == null && !mBluetoothAdapter.isEnabled()) {
//使用系統(tǒng)彈框來(lái)啟動(dòng)藍(lán)牙,REQUEST_ENABLE_BT為自定義的開(kāi)啟藍(lán)牙請(qǐng)求碼
Intent enableBtIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
startActivityForResult(enableBtIntent, REQUEST_ENABLE_BT);
//或者靜默打開(kāi)
bluetoothAdapter.enable();
}
}
//靜默關(guān)閉
bluetoothAdapter.disable();
藍(lán)牙開(kāi)關(guān)廣播 更多藍(lán)牙相關(guān)廣播
//廣播的Action
BluetoothAdapter.ACTION_STATE_CHANGED
//四種狀態(tài)值
BluetoothAdapter.STATE_ON //已開(kāi)啟
BluetoothAdapter.STATE_OFF //已關(guān)閉
BluetoothAdapter.STATE_TURNING_ON //正在開(kāi)啟
BluetoothAdapter.STATE_TURNING_OFF //正在關(guān)閉
//當(dāng)前狀態(tài)值的獲取
int curState = intent.getIntExtra(BluetoothAdapter.EXTRA_STATE, BluetoothAdapter.STATE_ON);
掃描具體使用流程
- 經(jīng)典通用方式
//此過(guò)程大概持續(xù)10秒钠龙,當(dāng)掃描到藍(lán)牙設(shè)備后炬藤,會(huì)發(fā)出廣播,只要在需要的地方注冊(cè)接收廣播碴里,就可以獲得掃描結(jié)果沈矿。這種方法可以?huà)呙璩鏊兴{(lán)牙設(shè)備,包括BLE咬腋,但貌似不同手機(jī)有不同體驗(yàn)羹膳。
private void startDiscover(){
mBluetoothAdapter.startDiscover();
}
//注冊(cè)此廣播,監(jiān)聽(tīng)BluetoothDevice.ACTION_FOUND根竿,以接收系統(tǒng)消息取得掃描結(jié)果
private class DeviceReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
if(BluetoothDevice.ACTION_FOUND.equals(action)){
//這個(gè)就是所獲得的藍(lán)牙設(shè)備陵像。
BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
mDevices.add(device );
}
}
}
- Ble掃描方式
//Ble藍(lán)牙掃描方式在5.0之后發(fā)生了變更
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP){
BluetoothLeScanner bluetoothLeScanner = bluetoothAdapter.getBluetoothLeScanner();
//過(guò)濾器設(shè)置
ArrayList<ScanFilter> filters = new ArrayList<>();
ScanFilter.Builder filterBuilder = new ScanFilter.Builder();
filters.add(filterBuilder);
//掃描結(jié)果回調(diào)設(shè)置
ScanCallback scanCallback = new ScanCallback(){
public void onScanResult(int callbackType, ScanResult result){
//scanResult.getDevice()取得device
}
};
//掃描參數(shù)設(shè)置
ScanSettings.Builder settingsBuilder = new ScanSettings.Builder();
//這種掃描方式占用資源比較高就珠,建議當(dāng)應(yīng)用處于前臺(tái)時(shí)使用該模式
settingsBuilder.setScanMode(SCAN_MODE_LOW_LATENCY);
// 開(kāi)啟掃描,第三項(xiàng)參數(shù)見(jiàn)下面醒颖,該操作為異步操作的妻怎,但是會(huì)消耗大量資源,一般掃描時(shí)長(zhǎng)為12秒图贸,建議找到需要的設(shè)備后蹂季,執(zhí)行取消掃描
bluetoothLeScanner.startScan(scanCallback, filter, settingsBuilder.build());
}else{
//掃描結(jié)果回調(diào)設(shè)置
BluetoothAdapter.LeScanCallback leScanCallback = new BluetoothAdapter.LeScanCallback(){
public void onLeScan(BluetoothDevice device, int rssi, byte[] scanRecord){}
};
//該方法已被聲明為過(guò)時(shí)方法
adapter.startLeScan(leScanCallback)
}
- 停止掃描
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP){
bluetoothLeScanner.stopScan(mScanCallback);
}else{
bluetoothAdapter.stopLeScan(mLeScanCallback);
}
設(shè)備信息獲取
- 根據(jù)藍(lán)牙地址獲取藍(lán)牙設(shè)備
BluetoothDevice bluetoothDevice = bluetoothAdapter.getRemoteDevice(address);
- 已綁定設(shè)備獲取
private void getBoundDevices(){
Set<BluetoothDevice> boundDevices = mBluetoothAdapter.getBondedDevices();
for(BluetoothDevice device : boundDevices){
//對(duì)device進(jìn)行其他操作,比如連接等疏日。
}
}
- 設(shè)備詳細(xì)信息獲取
private void showDetailOfDevice(){
//獲得設(shè)備名稱(chēng),多個(gè)設(shè)備可以有同一個(gè)名稱(chēng)撒汉。
String deviceName = bluetoothDevice.getName();
//獲取設(shè)備物理地址沟优,一個(gè)設(shè)備只能有一個(gè)物理地址,每個(gè)設(shè)備都有每個(gè)設(shè)備的物理地址睬辐,無(wú)法相同挠阁。
String deviceMacAddress =bluetoothDevice.getAddress();
//綁定設(shè)備
bluetoothDevice.createBond();
}
連接
- 建立連接
//掃描回調(diào)的BluetoothDevice用于建立GATT連接,參數(shù)2是控制是否自動(dòng)鏈接溯饵,為true的時(shí)候侵俗,當(dāng)設(shè)備進(jìn)入中心范圍,會(huì)進(jìn)行自動(dòng)連接丰刊,為false時(shí)是主動(dòng)連接隘谣,一般推薦使用主動(dòng)連接,因?yàn)檫@樣連接速度更快啄巧。
BluetoothGatt bluetoothGatt = bluetoothDevice.connectGatt(context, false, bluetoothGattCallback);
//如果已經(jīng)連接后寻歧,但是又?jǐn)嚅_(kāi)了連接,需要重新連接時(shí)
bluetoothGatt.connect();
- 連接狀態(tài)的獲取
//連接狀態(tài)改變會(huì)在BluetoothGattCallback的onConnectionStateChange方法中回調(diào)秩仆,同時(shí)也可以隨時(shí)主動(dòng)去查詢(xún)
int state = bluetoothManager.getConnectionState(bluetoothDevice, BluetoothGatt.GATT);
- 發(fā)現(xiàn)服務(wù)
//在建立連接成功后進(jìn)行發(fā)現(xiàn)服務(wù)码泛,onServicesDiscovered為發(fā)現(xiàn)服務(wù)的回調(diào)
bluetoothGatt.discoverServer();
- 特征改變
//該方法一般是在發(fā)現(xiàn)服務(wù)后,進(jìn)行設(shè)置的澄耍,設(shè)置該方法的目的是讓硬件在特征數(shù)據(jù)改變的時(shí)候噪珊,發(fā)送數(shù)據(jù)給app,app則通過(guò)onCharacteristicChanged方法回調(diào)給用戶(hù)齐莲,從參數(shù)中可獲取到回調(diào)回來(lái)的數(shù)據(jù)
bluetoothGatt.setCharacteristicNotification(characteristic, true);
- 連接數(shù)據(jù)回調(diào)(
在非UI線(xiàn)程
)
//mBluetoothGattCallback 為所有藍(lán)牙數(shù)據(jù)回調(diào)的處理者痢站,也是整個(gè)藍(lán)牙操作當(dāng)中最為核心的一部分。它里面有很多方法铅搓,但并非所有都需要在開(kāi)發(fā)當(dāng)中用到瑟押,這里列出來(lái)只是作為部分解析,需要哪個(gè)方法星掰,就重寫(xiě)哪個(gè)方法多望,不需要的嫩舟,直接去掉
private BluetoothGattCallback mBluetoothGattCallback = new BluetoothGattCallback() {
@Override
public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) {
super.onConnectionStateChange(gatt, status, newState);
//當(dāng)設(shè)備與中心連接狀態(tài)發(fā)生改變時(shí),下面是已連接的判斷
if (status == BluetoothGatt.GATT_SUCCESS && newState== BluetoothProfile.STATE_CONNECTED){
...
}
}
@Override
public void onServicesDiscovered(BluetoothGatt gatt, int status) {
super.onServicesDiscovered(gatt, status);
//當(dāng)發(fā)現(xiàn)設(shè)備服務(wù)時(shí)怀偷,會(huì)回調(diào)到此處家厌,下面是遍歷所有發(fā)現(xiàn)的服務(wù)
if (status == BluetoothGatt.GATT_SUCCESS) {
//gatt.getServices()可以獲得外設(shè)的所有服務(wù)
for (BluetoothGattService service : gatt.getServices()) {
//每發(fā)現(xiàn)一個(gè)服務(wù),我們?cè)俅伪闅v服務(wù)當(dāng)中所包含的特征椎工,service.getCharacteristics()可以獲得當(dāng)前服務(wù)所包含的所有特征
for (BluetoothGattCharacteristic characteristic : service.getCharacteristics()) {
//通撤褂冢可以把所發(fā)現(xiàn)的特征放進(jìn)一個(gè)列表當(dāng)中以便后續(xù)操作,如果你想知道每個(gè)特征都包含哪些描述符维蒙,很簡(jiǎn)單掰吕,再用一個(gè)循環(huán)去遍歷每一個(gè)特征的getDescriptor()方法。
mCharacteristics.add(characteristic);
//UUID獲取颅痊,或者根據(jù)UUID判斷是否是自己需要的服務(wù)/特征
Log.i("", characteristic.getUuid().toString());//打印特征的UUID殖熟。
}
}
}
}
@Override
public void onCharacteristicRead(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) {
//讀取特征后回調(diào)到此處。
}
@Override
public void onCharacteristicWrite(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) {
//寫(xiě)入特征后回調(diào)到此處斑响,status == BluetoothGatt.GATT_SUCCESS代表寫(xiě)入成功
}
@Override
public void onCharacteristicChanged(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) {
//當(dāng)特征(值)發(fā)生變法時(shí)回調(diào)到此處菱属。
}
@Override
public void onDescriptorRead(BluetoothGatt gatt, BluetoothGattDescriptor descriptor, int status) {
//讀取描述符后回調(diào)到此處。
}
@Override
public void onDescriptorWrite(BluetoothGatt gatt, BluetoothGattDescriptor descriptor, int status) {
//寫(xiě)入描述符后回調(diào)到此處
}
@Override
public void onReliableWriteCompleted(BluetoothGatt gatt, int status) {
//暫時(shí)沒(méi)有用過(guò)舰罚。
}
@Override
public void onReadRemoteRssi(BluetoothGatt gatt, int rssi, int status) {
//Rssi表示設(shè)備與中心的信號(hào)強(qiáng)度纽门,發(fā)生變化時(shí)回調(diào)到此處。
}
@Override
public void onMtuChanged(BluetoothGatt gatt, int mtu, int status) {
//暫時(shí)沒(méi)有用過(guò)营罢。
}
};
數(shù)據(jù)傳輸
- 寫(xiě)入特征值赏陵,會(huì)回調(diào)onCharacteristicWrite
BluetoothGattService service = bluetoothGatt.getService(SERVICE_UUID);
BluetoothGattCharacteristic characteristic = service.getCharacteristic(CHARACTERISTIC_UUID);
characteristic.setValue(writeString);
//設(shè)置回復(fù)方式
bluetoothGattCharacteristic.setWriteType(BluetoothGattCharacteristic.WRITE_TYPE_NO_RESPONSE);
bluetoothGatt.writeCharacteristic(characteristic);
- 寫(xiě)入描述符
bluetoothGattDescriptor.setValue(writeContent.getBytes());
bluetoothGatt.writeDescriptor(bluetoothGattDescriptor);
- 讀取特征值
bluetoothGatt.readCharacteristic(characteristic);
外圍設(shè)備實(shí)現(xiàn)(被連接者/服務(wù)端 )
- 廣播的設(shè)定
private AdvertiseSettings buildAdvertiseSettings() {
AdvertiseSettings.Builder builder = new AdvertiseSettings.Builder()
//設(shè)置廣播模式:低功耗、平衡愤钾、低延時(shí)瘟滨,廣播間隔時(shí)間依次越來(lái)越短
.setAdvertiseMode(AdvertiseSettings.ADVERTISE_MODE_BALANCED)
//設(shè)置是否可以連接,一般不可連接廣播應(yīng)用在iBeacon設(shè)備上
.setConnectable(true)
//設(shè)置廣播的最長(zhǎng)時(shí)間能颁,最大時(shí)長(zhǎng)為L(zhǎng)IMITED_ADVERTISING_MAX_MILLIS(180秒)
.setTimeout(10*1000)
//設(shè)置廣播的信號(hào)強(qiáng)度 ADVERTISE_TX_POWER_ULTRA_LOW, ADVERTISE_TX_POWER_LOW,
//ADVERTISE_TX_POWER_MEDIUM, ADVERTISE_TX_POWER_HIGH 信號(hào)強(qiáng)度依次增強(qiáng)
.setTxPowerLevel(AdvertiseSettings.ADVERTISE_TX_POWER_HIGH);
return builder.build();
}
- 對(duì)外廣播的數(shù)據(jù)(數(shù)據(jù)限制 31 Bytes)
private AdvertiseData buildAdvertiseData() {
AdvertiseData.Builder dataBuilder = new AdvertiseData.Builder();
//添加廠家信息杂瘸,第一個(gè)參數(shù)為廠家ID(不足兩個(gè)字節(jié)會(huì)自動(dòng)補(bǔ)0,例如這里為0x34伙菊,實(shí)際數(shù)據(jù)則為34,00)
//一般情況下無(wú)需設(shè)置败玉,否則會(huì)出現(xiàn)無(wú)法被其他設(shè)備掃描到的情況
.addManufacturerData(0x34, new byte[]{0x56})
//添加服務(wù)進(jìn)行廣播,即對(duì)外廣播本設(shè)備擁有的服務(wù)
.addServiceData(...);
//是否廣播信號(hào)強(qiáng)度
.setIncludeTxPowerLevel(true)
//是否廣播設(shè)備名稱(chēng)
.setIncludeDeviceName(true);
return dataBuilder.build();
}
- 對(duì)外廣播(即允許被掃描到)
private void advertise() {
BluetoothManager bluetoothManager = (BluetoothManager) getSystemService(BLUETOOTH_SERVICE);
BluetoothAdapter mAdapter = bluetoothManager.getAdapter();
AdvertiseCallback advertiseCallback = new AdvertiseCallback() {
@Override
public void onStartSuccess(AdvertiseSettings settingsInEffect) {
//廣播成功镜硕,建議在這里開(kāi)啟服務(wù)
}
@Override
public void onStartFailure(int errorCode) {
//廣播失敗
}
};
BluetoothLeAdvertiser mAdvertiser = mAdapter.getBluetoothLeAdvertiser();
mAdvertiser.startAdvertising(buildAdvertiseSettings(), buildAdvertiseData(), mAdvertiseCallback);
}
- 開(kāi)啟服務(wù)
//聲明需要廣播的服務(wù)的UUID和特征的UUID运翼,注意不要占用藍(lán)牙設(shè)備默認(rèn)的UUID
UUID UUID_SERVICE = UUID.fromString("00001354-0000-1000-8000-00805f9b34fb");
UUID UUID_CHARACTERISTIC = UUID.fromString("00001355-0000-1000-8000-00805f9b34fb");
UUID UUID_DESCRIPTOR = UUID.fromString("00001356-0000-1000-8000-00805f9b34fb");
//外圍設(shè)備狀態(tài)、數(shù)據(jù)回調(diào)兴枯,詳情見(jiàn)后面
BluetoothGattServerCallback serverCallback = new BluetoothGattServerCallback() {...};
//GATT協(xié)議服務(wù)
BluetoothGattServer server = bluetoothManager.openGattServer(this, serverCallback);
//創(chuàng)建一個(gè)服務(wù)
BluetoothGattService service = new BluetoothGattService(UUID_SERVICE,
BluetoothGattService.SERVICE_TYPE_PRIMARY);
//創(chuàng)建一個(gè)特征血淌,首先此characteristic屬性滿(mǎn)足BluetoothGattCharacteristic.PROPERTY_WRITY或BluetoothGattCharacteristic.PROPERTY_WRITY_NO_RESPONSE,
//如果其property都不包含這兩個(gè),寫(xiě)特征值writeCharacteristic()函數(shù)直接返回false,什么都不做處理悠夯。其次此characteristic權(quán)限應(yīng)滿(mǎn)足
//BluetoothGattCharacteristic.PERMISSION_WRITE,否則onCharacteristicWrite()回調(diào)收到GATT_WRITE_NOT_PERMITTED回應(yīng)
//如果需要既能讀癌淮,又能寫(xiě),則可以參考如下寫(xiě)法
BluetoothGattCharacteristic characteristic = new BluetoothGattCharacteristic(Constants.UUID_CHARACTERISTIC,
BluetoothGattCharacteristic.PROPERTY_READ | BluetoothGattCharacteristic.PROPERTY_WRITE,
BluetoothGattCharacteristic.PERMISSION_WRITE | BluetoothGattCharacteristic.PERMISSION_READ);
//創(chuàng)建一個(gè)描述符
BluetoothGattDescriptor descriptor = new BluetoothGattDescriptor(UUID_DESCRIPTOR,
BluetoothGattDescriptor.PERMISSION_READ);
//將描述符添加到特征中沦补,一個(gè)特征可以包含0至多個(gè)描述符
characteristic.addDescriptor(descriptor);
//將特征添加到服務(wù)中乳蓄,一個(gè)服務(wù)可以包含1到多個(gè)特征
service.addCharacteristic(characteristic);
server.addService(service);
- BluetoothGattServerCallback
public abstract class BluetoothGattServerCallback {
public BluetoothGattServerCallback() {
}
public void onConnectionStateChange(BluetoothDevice device, int status, int newState) {
//連接狀態(tài)被改變
}
public void onServiceAdded(int status, BluetoothGattService service) {
//添加服務(wù)
}
public void onCharacteristicReadRequest(BluetoothDevice device, int requestId, int offset, BluetoothGattCharacteristic characteristic) {
//被讀取特征
}
public void onCharacteristicWriteRequest(BluetoothDevice device, int requestId, BluetoothGattCharacteristic characteristic, boolean preparedWrite, boolean responseNeeded, int offset, byte[] value) {
//被寫(xiě)入數(shù)據(jù),其中device是寫(xiě)入的設(shè)備夕膀,value是寫(xiě)入的值虚倒,responseNeeded指是否需要恢復(fù),如果需要恢復(fù)則調(diào)用gattServer.sendResponse()方法回復(fù)
}
public void onDescriptorReadRequest(BluetoothDevice device, int requestId, int offset, BluetoothGattDescriptor descriptor) {
//被讀取描述符
}
public void onDescriptorWriteRequest(BluetoothDevice device, int requestId, BluetoothGattDescriptor descriptor, boolean preparedWrite, boolean responseNeeded, int offset, byte[] value) {
//被寫(xiě)入描述符
}
public void onExecuteWrite(BluetoothDevice device, int requestId, boolean execute) {
}
@Override
public void onNotificationSent(BluetoothDevice device, int status) {
}
@Override
public void onMtuChanged(BluetoothDevice device, int mtu) {
}
@Override
public void onPhyUpdate(BluetoothDevice device, int txPhy, int rxPhy, int status) {
}
@Override
public void onPhyRead(BluetoothDevice device, int txPhy, int rxPhy, int status) {
}
}
-
廣播數(shù)據(jù)格式
廣播數(shù)據(jù)(或者掃描應(yīng)答數(shù)據(jù))由一個(gè)一個(gè)的AD Structure組成产舞,對(duì)于未滿(mǎn)31bytes的其它數(shù)據(jù)魂奥,則填充為0;每個(gè)AD Structure由兩部分組成:1byte的長(zhǎng)度信息(Data的長(zhǎng)度)庞瘸,和剩余的Data信息捧弃;
Data信息又由兩部分組成:AD Type(長(zhǎng)度不定)指示該AD Structure的類(lèi)型,以及具體的AD Data擦囊。
例如:
02 01 06 03 03 aa fe 17 16 aa fe 00 -10 00 01 02 03 04 05 06 07 08 09 0a 0b 0e 0f 00 00 00 00
02 01 06
是一個(gè)AD Structure:Data的長(zhǎng)度是02;Data是01 06嘴办;AD Type是01(Flags)瞬场;AD Data是06,表明支持General Discoverable Mode涧郊、不支持BR/EDR贯被。
03 03 aa fe
是一個(gè)AD Structure:Data的長(zhǎng)度是03;Data是03 aa fe妆艘;AD Type是03(16 bits的Service UUID)彤灶;AD Data是aa fe,是Eddystone profile的Service UUID批旺。AD Type查詢(xún)
注意
- 外圍設(shè)備廣播的數(shù)據(jù)最多31 byte
- 寫(xiě)特征一次最多寫(xiě)入20 byte
- 單次掃描時(shí)間不宜過(guò)長(zhǎng)(建議10~15秒)
- BluetoothGattCallback的回調(diào)是在非UI線(xiàn)程
- 每個(gè)設(shè)備所能使用的Gatt連接是有限個(gè)數(shù)的幌陕,所以應(yīng)該斷開(kāi)連接時(shí)也關(guān)閉Gatt
bluetoothGatt.close()
,然后重新連接 - 外圍設(shè)備藍(lán)牙關(guān)閉后汽煮,中心設(shè)備的BluetoothGattCallback的onConnectionStateChange會(huì)回調(diào)狀態(tài)碼
status = 19
搏熄,newState = 0
(斷開(kāi)連接) - 外圍設(shè)備藍(lán)牙關(guān)閉后,中心設(shè)備再進(jìn)行連接暇赤,BluetoothGattCallback的onConnectionStateChange會(huì)回調(diào)狀態(tài)碼
133
心例,并且當(dāng)外圍設(shè)備藍(lán)牙開(kāi)啟后再調(diào)用中心設(shè)備的bluetoothGatt.connect()不能重新連接,仍然是133錯(cuò)誤鞋囊,所以建議針對(duì)133錯(cuò)誤進(jìn)行關(guān)閉gatt并釋放資源