【安卓相關(guān)】藍(lán)牙開發(fā)疼阔、掃描、通知适瓦、發(fā)送指令

最近在做藍(lán)牙相關(guān)的開發(fā)竿开,雖然說(shuō) FastBleLib 這樣的第三方藍(lán)牙連接庫(kù)非常方便谱仪,但是原生開發(fā)還是有必要了解一下的。

首先說(shuō)明

1.不需要問(wèn)硬件工程師UUID
2.不需要寫 UI
3.本案例只有一個(gè)類文件
4.沒有寫動(dòng)態(tài)請(qǐng)求藍(lán)牙權(quán)限相關(guān)的否彩,自己測(cè)試過(guò)程中疯攒,手動(dòng)開啟所有權(quán)限即可。
5.代碼解釋全部寫在注釋列荔,和日志

<uses-permission android:name="android.permission.BLUETOOTH"/>
<!--此權(quán)限不開敬尺,可能搜不到藍(lán)牙設(shè)備-->
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN"/>
public class Main8Activity extends AppCompatActivity {

    //  藍(lán)牙適配器對(duì)象,用于掃描藍(lán)牙設(shè)備
    private BluetoothAdapter bluetoothAdapter;

    // GATT 通信贴浙,連接藍(lán)牙砂吞,發(fā)送指令,以及接收藍(lán)牙通知 ( 這里所謂的通知崎溃,就是藍(lán)牙傳過(guò)來(lái)的數(shù)據(jù) )
    private BluetoothGatt bluetoothGatt;

    //  如果藍(lán)牙沒有開啟
    private final static int REQUEST_ENABLE = 100;

    //  掃描到的藍(lán)牙設(shè)備蜻直,因?yàn)?Set 不會(huì)重復(fù),所以用這個(gè)數(shù)據(jù)結(jié)構(gòu)
    private Set<BluetoothDevice> bluetoothDeviceSet;

    //  用戶發(fā)送指令的操作
    private BluetoothGattCharacteristic writeBluetoothGattCharacteristic;

    //  用于開啟通知的操作
    private BluetoothGattCharacteristic notificationBluetoothGattCharacteristic;


    //  服務(wù)和特征值
    private UUID write_UUID_service;
    private UUID write_UUID_chara;

    private UUID read_UUID_service;
    private UUID read_UUID_chara;

    private UUID notify_UUID_service;
    private UUID notify_UUID_chara;

    private UUID indicate_UUID_service;
    private UUID indicate_UUID_chara;


    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main8);


        log("初始化藍(lán)牙掃描 的 set 集合");
        bluetoothDeviceSet = new HashSet<>();

        log("開始獲取藍(lán)牙適配器");
        bluetoothAdapter = BluetoothAdapter.getDefaultAdapter();

        if(bluetoothAdapter != null){

            log("bluetoothAdapter 不為 null 當(dāng)前設(shè)備支持藍(lán)牙");

            if(!bluetoothAdapter.isEnabled()){
                log("藍(lán)牙未啟動(dòng),正在使用 intent 啟用藍(lán)牙");
                Intent intent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
                startActivityForResult(intent,REQUEST_ENABLE);
            }else{
                log("藍(lán)牙已經(jīng)啟動(dòng)袁串,不需要使用 intent 啟動(dòng)藍(lán)牙");

                bluetoothAdapter.getBluetoothLeScanner().startScan(scanCallback);
                log("開始掃描藍(lán)牙設(shè)備");
            }

        }else{
            log("bluetoothAdapter 為 null 所以當(dāng)前設(shè)備不支持藍(lán)牙");
        }


    }

    @Override
    protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
        super.onActivityResult(requestCode, resultCode, data);


        if(requestCode == REQUEST_ENABLE){
            if(resultCode == RESULT_OK){

                log("用戶允許了開啟藍(lán)牙的請(qǐng)求");

                log("開始掃描藍(lán)牙設(shè)備");
                bluetoothAdapter.getBluetoothLeScanner().startScan(scanCallback);

            }else if(resultCode == RESULT_CANCELED){
                log("用戶拒絕了開啟藍(lán)牙的請(qǐng)求");
            }
        }


    }




    //  藍(lán)牙開始掃描回調(diào)
    private ScanCallback scanCallback = new ScanCallback() {
        @Override
        public void onScanResult(int callbackType, ScanResult result) {
            super.onScanResult(callbackType, result);

            //  bluetoothDevice 為掃描到的藍(lán)牙設(shè)備
            BluetoothDevice bluetoothDevice = result.getDevice();


            //  添加掃描到的藍(lán)牙設(shè)備到 set 集合
            boolean addResult = bluetoothDeviceSet.add(bluetoothDevice);


            //  如果 set 集合中未存在該藍(lán)牙對(duì)象概而,則返回true
            if(addResult){
                //log("onScanResult() 發(fā)現(xiàn)藍(lán)牙設(shè)備" + bluetoothDevice.getName() + "," + bluetoothDevice.getAddress());

                //log("目前已經(jīng)發(fā)現(xiàn)" + bluetoothDeviceSet.size() + "個(gè)藍(lán)牙設(shè)備");

                //  如果當(dāng)前添加的設(shè)備 藍(lán)牙名稱為 HTC_38908 MAC地址為  88:25:83:F1:04:58,則發(fā)起連接
                //  懶得寫 列表 UI 了囱修,這里先直接指定好自己用來(lái)測(cè)試藍(lán)牙的設(shè)備
                if("HTC_38908".equals(bluetoothDevice.getName()) && "88:25:83:F1:04:58".equals(bluetoothDevice.getAddress()) ){

                    log("發(fā)現(xiàn)需要連接的設(shè)備");

                    log("因?yàn)樗{(lán)牙掃描比較消耗資源赎瑰,所以連接前就關(guān)閉掃描");

                    //  bluetoothAdapter.stopScan(scanCallback);
                    bluetoothAdapter.cancelDiscovery();

                    log("開始 GATT 通信");
                    bluetoothGatt = bluetoothDevice.connectGatt(Main8Activity.this,true,bluetoothGattCallback);

                }
            }
        }

        @Override
        public void onBatchScanResults(List<ScanResult> results) {
            super.onBatchScanResults(results);
        }

        @Override
        public void onScanFailed(int errorCode) {
            super.onScanFailed(errorCode);
        }
    };


    private BluetoothGattCallback bluetoothGattCallback = new BluetoothGattCallback() {
        @Override
        public void onPhyUpdate(BluetoothGatt gatt, int txPhy, int rxPhy, int status) {
            super.onPhyUpdate(gatt, txPhy, rxPhy, status);
        }

        @Override
        public void onPhyRead(BluetoothGatt gatt, int txPhy, int rxPhy, int status) {
            super.onPhyRead(gatt, txPhy, rxPhy, status);
        }

        @Override
        public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) {
            super.onConnectionStateChange(gatt, status, newState);


            if(BluetoothGatt.STATE_CONNECTED == newState){      //  藍(lán)牙連接狀態(tài)改變 為 -- 已連接

                // 將會(huì)回調(diào) 本類中的 onServicesDiscovered() 這個(gè)方法
                boolean result = gatt.discoverServices();

                if(result){
                    log("藍(lán)牙連接成功");
                }else{
                    log("藍(lán)牙連接失敗");
                }

            }else if(BluetoothGatt.STATE_DISCONNECTED == newState){     //  藍(lán)牙連接狀態(tài)改變?yōu)?-- 連接斷開

                log("斷開連接");

            }
        }

        @Override
        public void onServicesDiscovered(BluetoothGatt gatt, int status) {
            super.onServicesDiscovered(gatt, status);

            //  這里初始化 各種 服務(wù) 和 描述 ,用來(lái)發(fā)送藍(lán)牙指令的對(duì)象破镰,還有接收藍(lán)牙數(shù)據(jù)對(duì)象
            initServiceAndChara();

        }

        @Override
        public void onCharacteristicRead(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) {
            super.onCharacteristicRead(gatt, characteristic, status);

            log("onCharacteristicRead");
        }

        @Override
        public void onCharacteristicWrite(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) {
            super.onCharacteristicWrite(gatt, characteristic, status);

            log("onCharacteristicWrite");
        }

        @Override
        public void onCharacteristicChanged(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) {
            super.onCharacteristicChanged(gatt, characteristic);


            //  這里將藍(lán)牙傳過(guò)來(lái)的數(shù)據(jù)轉(zhuǎn)換為 16 進(jìn)制格式的字符串餐曼,便于閱讀
            byte[] bytes = characteristic.getValue();

            StringBuilder stringBuilder = new StringBuilder();
            for (byte b : bytes) {
                stringBuilder.append(String.format("%02x", b));
            }

            log("藍(lán)牙傳回來(lái)的內(nèi)容 ( 16進(jìn)制 ) : " + stringBuilder.toString());
        }

        @Override
        public void onDescriptorRead(BluetoothGatt gatt, BluetoothGattDescriptor descriptor, int status) {
            super.onDescriptorRead(gatt, descriptor, status);

            log("onDescriptorRead");
        }

        @Override
        public void onDescriptorWrite(BluetoothGatt gatt, BluetoothGattDescriptor descriptor, int status) {
            super.onDescriptorWrite(gatt, descriptor, status);

            log("onDescriptorWrite");
        }

        @Override
        public void onReliableWriteCompleted(BluetoothGatt gatt, int status) {
            super.onReliableWriteCompleted(gatt, status);

            log("onReliableWriteCompleted");
        }

        @Override
        public void onReadRemoteRssi(BluetoothGatt gatt, int rssi, int status) {
            super.onReadRemoteRssi(gatt, rssi, status);

            log("onReadRemoteRssi");
        }

        @Override
        public void onMtuChanged(BluetoothGatt gatt, int mtu, int status) {
            super.onMtuChanged(gatt, mtu, status);

            log("onMtuChanged");
        }
    };


    private void initServiceAndChara(){

        List<BluetoothGattService> bluetoothGattServices= bluetoothGatt.getServices();

        for (BluetoothGattService bluetoothGattService:bluetoothGattServices){

            List<BluetoothGattCharacteristic> characteristics=bluetoothGattService.getCharacteristics();

            for (BluetoothGattCharacteristic characteristic:characteristics){


                int charaProp = characteristic.getProperties();


                if ((charaProp & BluetoothGattCharacteristic.PROPERTY_READ) > 0) {
                    read_UUID_chara=characteristic.getUuid();
                    read_UUID_service=bluetoothGattService.getUuid();
                    log("read_chara="+read_UUID_chara+"----read_service="+read_UUID_service);
                }


                if ((charaProp & BluetoothGattCharacteristic.PROPERTY_INDICATE) > 0) {
                    indicate_UUID_chara=characteristic.getUuid();
                    indicate_UUID_service=bluetoothGattService.getUuid();
                    log("indicate_chara="+indicate_UUID_chara+"----indicate_service="+indicate_UUID_service);

                }


                if ((charaProp & BluetoothGattCharacteristic.PROPERTY_WRITE) > 0) {
                    write_UUID_chara=characteristic.getUuid();
                    write_UUID_service=bluetoothGattService.getUuid();
                    log("write_chara="+write_UUID_chara+"----write_service="+write_UUID_service);
                }


                if ((charaProp & BluetoothGattCharacteristic.PROPERTY_WRITE_NO_RESPONSE) > 0) {
                    write_UUID_chara=characteristic.getUuid();
                    write_UUID_service=bluetoothGattService.getUuid();
                    log("write_chara="+write_UUID_chara+"----write_service="+write_UUID_service);

                    //  寫入服務(wù)
                    BluetoothGattService service = bluetoothGatt.getService(write_UUID_service);
                    writeBluetoothGattCharacteristic = service.getCharacteristic(write_UUID_chara);

                    //  發(fā)送指令
                    sendBleData("*".getBytes());

                }


                if ((charaProp & BluetoothGattCharacteristic.PROPERTY_NOTIFY) > 0) {
                    notify_UUID_chara=characteristic.getUuid();
                    notify_UUID_service=bluetoothGattService.getUuid();
                    log("notify_chara="+notify_UUID_chara+"----notify_service="+notify_UUID_service);

                    //  初始化通知服務(wù)
                    BluetoothGattService service = bluetoothGatt.getService(notify_UUID_service);
                    notificationBluetoothGattCharacteristic = service.getCharacteristic(notify_UUID_chara);

                    //  開啟通知
                    bluetoothGatt.setCharacteristicNotification(notificationBluetoothGattCharacteristic,true);
                }




            }
        }
    }


    /**
     * 發(fā)送數(shù)據(jù)給藍(lán)牙
     * @param data 數(shù)據(jù)byte[]
     * */
    private void sendBleData(byte[] data){
        writeBluetoothGattCharacteristic.setValue(data);
        bluetoothGatt.writeCharacteristic(writeBluetoothGattCharacteristic);
    }


    /**
     * 日志
     * @param log 日志內(nèi)容
     * */
    private void log(String log){
        Log.i("藍(lán)牙",log);
    }




}

以上,相信注釋里面已經(jīng)寫的很清楚了鲜漩。

然后打開日志看就行了

參考

藍(lán)牙概覽官方文檔參考
藍(lán)牙參考文章

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末源譬,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子宇整,更是在濱河造成了極大的恐慌瓶佳,老刑警劉巖芋膘,帶你破解...
    沈念sama閱讀 219,270評(píng)論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件鳞青,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡为朋,警方通過(guò)查閱死者的電腦和手機(jī)臂拓,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,489評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)习寸,“玉大人胶惰,你說(shuō)我怎么就攤上這事∠枷” “怎么了孵滞?”我有些...
    開封第一講書人閱讀 165,630評(píng)論 0 356
  • 文/不壞的土叔 我叫張陵中捆,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我坊饶,道長(zhǎng)泄伪,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,906評(píng)論 1 295
  • 正文 為了忘掉前任匿级,我火速辦了婚禮蟋滴,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘痘绎。我一直安慰自己津函,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,928評(píng)論 6 392
  • 文/花漫 我一把揭開白布孤页。 她就那樣靜靜地躺著尔苦,像睡著了一般。 火紅的嫁衣襯著肌膚如雪行施。 梳的紋絲不亂的頭發(fā)上蕉堰,一...
    開封第一講書人閱讀 51,718評(píng)論 1 305
  • 那天,我揣著相機(jī)與錄音悲龟,去河邊找鬼屋讶。 笑死,一個(gè)胖子當(dāng)著我的面吹牛须教,可吹牛的內(nèi)容都是我干的皿渗。 我是一名探鬼主播,決...
    沈念sama閱讀 40,442評(píng)論 3 420
  • 文/蒼蘭香墨 我猛地睜開眼轻腺,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼乐疆!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起贬养,我...
    開封第一講書人閱讀 39,345評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤挤土,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后误算,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體仰美,經(jīng)...
    沈念sama閱讀 45,802評(píng)論 1 317
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,984評(píng)論 3 337
  • 正文 我和宋清朗相戀三年儿礼,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了咖杂。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,117評(píng)論 1 351
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡蚊夫,死狀恐怖诉字,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤壤圃,帶...
    沈念sama閱讀 35,810評(píng)論 5 346
  • 正文 年R本政府宣布陵霉,位于F島的核電站,受9級(jí)特大地震影響伍绳,放射性物質(zhì)發(fā)生泄漏撩匕。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,462評(píng)論 3 331
  • 文/蒙蒙 一墨叛、第九天 我趴在偏房一處隱蔽的房頂上張望止毕。 院中可真熱鬧,春花似錦漠趁、人聲如沸扁凛。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,011評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)谨朝。三九已至,卻和暖如春甥绿,著一層夾襖步出監(jiān)牢的瞬間字币,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,139評(píng)論 1 272
  • 我被黑心中介騙來(lái)泰國(guó)打工共缕, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留洗出,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,377評(píng)論 3 373
  • 正文 我出身青樓图谷,卻偏偏與公主長(zhǎng)得像翩活,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子便贵,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,060評(píng)論 2 355