低功耗藍(lán)牙Ble的詳細(xì)使用流程

概述

  • 中心角色和外圍角色
    在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)源

image.png

權(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擦囊。


    image.png

    例如:

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)閉GattbluetoothGatt.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并釋放資源

源碼地址

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末止后,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子溜腐,更是在濱河造成了極大的恐慌译株,老刑警劉巖瓜喇,帶你破解...
    沈念sama閱讀 211,376評(píng)論 6 491
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異古戴,居然都是意外死亡欠橘,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,126評(píng)論 2 385
  • 文/潘曉璐 我一進(jìn)店門(mén)现恼,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)肃续,“玉大人,你說(shuō)我怎么就攤上這事叉袍∈济” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 156,966評(píng)論 0 347
  • 文/不壞的土叔 我叫張陵喳逛,是天一觀的道長(zhǎng)瞧捌。 經(jīng)常有香客問(wèn)我,道長(zhǎng)润文,這世上最難降的妖魔是什么姐呐? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 56,432評(píng)論 1 283
  • 正文 為了忘掉前任,我火速辦了婚禮典蝌,結(jié)果婚禮上曙砂,老公的妹妹穿的比我還像新娘。我一直安慰自己骏掀,他們只是感情好鸠澈,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,519評(píng)論 6 385
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著截驮,像睡著了一般笑陈。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上葵袭,一...
    開(kāi)封第一講書(shū)人閱讀 49,792評(píng)論 1 290
  • 那天涵妥,我揣著相機(jī)與錄音,去河邊找鬼眶熬。 笑死妹笆,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的娜氏。 我是一名探鬼主播拳缠,決...
    沈念sama閱讀 38,933評(píng)論 3 406
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼贸弥!你這毒婦竟也來(lái)了窟坐?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 37,701評(píng)論 0 266
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎哲鸳,沒(méi)想到半個(gè)月后臣疑,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,143評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡徙菠,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,488評(píng)論 2 327
  • 正文 我和宋清朗相戀三年讯沈,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片婿奔。...
    茶點(diǎn)故事閱讀 38,626評(píng)論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡缺狠,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出萍摊,到底是詐尸還是另有隱情挤茄,我是刑警寧澤,帶...
    沈念sama閱讀 34,292評(píng)論 4 329
  • 正文 年R本政府宣布冰木,位于F島的核電站穷劈,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏踊沸。R本人自食惡果不足惜歇终,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,896評(píng)論 3 313
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望逼龟。 院中可真熱鬧练湿,春花似錦、人聲如沸审轮。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,742評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)疾渣。三九已至,卻和暖如春崖飘,著一層夾襖步出監(jiān)牢的瞬間榴捡,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 31,977評(píng)論 1 265
  • 我被黑心中介騙來(lái)泰國(guó)打工朱浴, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留吊圾,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 46,324評(píng)論 2 360
  • 正文 我出身青樓翰蠢,卻偏偏與公主長(zhǎng)得像项乒,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子梁沧,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,494評(píng)論 2 348

推薦閱讀更多精彩內(nèi)容

  • 背景 藍(lán)牙歷史說(shuō)到藍(lán)牙檀何,就不得不說(shuō)下藍(lán)牙技術(shù)聯(lián)盟(Bluetooth SIG),它負(fù)責(zé)藍(lán)牙規(guī)范制定和推廣的國(guó)際組織...
    徐正峰閱讀 12,265評(píng)論 6 33
  • 因?yàn)樽约旱捻?xiàng)目中有用到了藍(lán)牙相關(guān)的功能,所以之前也斷斷續(xù)續(xù)地針對(duì)藍(lán)牙通信尤其是BLE通信進(jìn)行了一番探索,整理出了一...
    陳利健閱讀 114,149評(píng)論 172 294
  • 前言: 本文主要描述Android BLE的一些基礎(chǔ)知識(shí)及相關(guān)操作流程频鉴,不牽扯具體的業(yè)務(wù)實(shí)現(xiàn)栓辜,其中提供了針對(duì)廣播包...
    幻影宇寰閱讀 5,320評(píng)論 6 19
  • 藍(lán)牙 藍(lán)牙的波段為2400-2483.5MHz(包括防護(hù)頻帶)。這是全球范圍內(nèi)無(wú)需取得執(zhí)照(但定不是無(wú)管制的)的工...
    蘇永茂閱讀 6,125評(píng)論 0 11
  • 安卓4.3(API 18)為BLE的核心功能提供平臺(tái)支持和API垛孔,App可以利用它來(lái)發(fā)現(xiàn)設(shè)備藕甩、查詢(xún)服務(wù)和讀寫(xiě)特性。...
    風(fēng)雨byt閱讀 14,014評(píng)論 3 43