安卓BLE藍(lán)牙開發(fā)詳解

前言

BLE藍(lán)牙的興起主要因?yàn)榻陙?lái)可穿戴設(shè)備的流行替蔬。由于傳統(tǒng)藍(lán)牙功耗高不能滿足可穿戴設(shè)備對(duì)于續(xù)航的要求艺晴。所以大部分可穿戴設(shè)備采用藍(lán)牙4.0,即BLE藍(lán)牙技術(shù)。BLE(Bluetooth Low Energy)低功耗藍(lán)牙费薄,主要特點(diǎn)是快速搜索后德,快速連接部宿,超低功耗保持連接和數(shù)據(jù)傳輸。
缺點(diǎn):BLE藍(lán)牙數(shù)據(jù)傳輸速率低瓢湃,特別是在安卓開發(fā)過(guò)程理张,BLE藍(lán)牙一包數(shù)據(jù)最多為20字節(jié),因此安卓系統(tǒng)下最好不要使用BLE藍(lán)牙傳輸大量數(shù)據(jù)绵患。

藍(lán)牙開發(fā)的主要流程

(一)申請(qǐng)權(quán)限

安卓手機(jī)涉及藍(lán)牙權(quán)限問(wèn)題雾叭,藍(lán)牙開發(fā)需要在AndroidManifest.xml文件中添加權(quán)限聲明:

<!-- 藍(lán)牙權(quán)限 -->
<uses-permission android:name="android.permission.BLUETOOTH" />
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />

為適配安卓6.0以及以上版本需要添加一個(gè)模糊定位的權(quán)限

 <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />

手機(jī)權(quán)限管理中允許此權(quán)限,否則會(huì)出現(xiàn)無(wú)法搜索到設(shè)備的情況落蝙。

(二)打開藍(lán)牙

在搜索設(shè)備之前需要詢問(wèn)打開手機(jī)藍(lán)牙织狐,其關(guān)鍵代碼如下:

    //獲取系統(tǒng)藍(lán)牙適配器管理類
    private BluetoothAdapter mBluetoothAdapter = BluetoothAdapter
            .getDefaultAdapter();

    // 詢問(wèn)打開藍(lán)牙
    if (mBluetoothAdapter != null && !mBluetoothAdapter.isEnabled()) {
            Intent enableBtIntent = new Intent(
                    BluetoothAdapter.ACTION_REQUEST_ENABLE);
            startActivityForResult(enableBtIntent, 1);
    }
      // 申請(qǐng)打開藍(lán)牙請(qǐng)求的回調(diào)
    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        // TODO Auto-generated method stub
        super.onActivityResult(requestCode, resultCode, data);
        if (requestCode == 1) {
            if (resultCode == RESULT_OK) {
                Toast.makeText(this, "藍(lán)牙已經(jīng)開啟", Toast.LENGTH_SHORT).show();
            } else if (resultCode == RESULT_CANCELED) {
                Toast.makeText(this, "沒(méi)有藍(lán)牙權(quán)限", Toast.LENGTH_SHORT).show();
                finish();
            }
        }
    }

(三)搜索設(shè)備

本文主要針對(duì)BLE藍(lán)牙開發(fā),因此采用mBluetoothAdapter.startLeScan(LeScanCallback callback)方式掃描BLE藍(lán)牙設(shè)備筏勒。調(diào)用方法如下:

mBluetoothAdapter.startLeScan(callback);
private LeScanCallback callback = new LeScanCallback() {

    @Override
    public void onLeScan(BluetoothDevice device, int arg1, byte[] arg2) {

        //device為掃描到的BLE設(shè)備
        if(device.getName() == "目標(biāo)設(shè)備名稱"){
            //獲取目標(biāo)設(shè)備
            targetDevice = device;
        }
    }
};

(四)連接設(shè)備

通過(guò)掃描BLE設(shè)備赚瘦,根據(jù)設(shè)備名稱區(qū)分出目標(biāo)設(shè)備targetDevice,下一步實(shí)現(xiàn)與目標(biāo)設(shè)備的連接奏寨。在連接設(shè)備之前要停止搜索藍(lán)牙起意。

mBluetoothAdapter.stopLeScan(callback);

注 :停止搜索一般需要一定的時(shí)間來(lái)完成,最好調(diào)用停止搜索函數(shù)之后加以100ms的延時(shí)病瞳,保證系統(tǒng)能夠完全停止搜索藍(lán)牙設(shè)備揽咕。停止搜索之后啟動(dòng)連接過(guò)程。

BLE藍(lán)牙的連接方法相對(duì)簡(jiǎn)單只需調(diào)用connectGatt方法套菜,函數(shù)原型如下:

public BluetoothGatt connectGatt (Context context, boolean autoConnect, BluetoothGattCallback callback)亲善;

參數(shù)說(shuō)明
返回值 BluetoothGatt: BLE藍(lán)牙連接管理類,主要負(fù)責(zé)與設(shè)備進(jìn)行通信逗柴。后續(xù)會(huì)進(jìn)一步介紹該類蛹头。
boolean autoConnect:建議置為false,能夠提升連接速度。
BluetoothGattCallback callback 連接回調(diào)渣蜗,重要參數(shù)屠尊,BLE通信的核心部分。

(五)設(shè)備通信

與設(shè)備建立連接之后與設(shè)備通信耕拷,整個(gè)通信過(guò)程都是在BluetoothGattCallback的異步回調(diào)函數(shù)中完成讼昆。
BluetoothGattCallback中主要回調(diào)函數(shù)如下:

private BluetoothGattCallback gattCallback = new BluetoothGattCallback() {
    
        @Override
        public void onConnectionStateChange(BluetoothGatt gatt, int status,
                int newState) {
        }
        @Override
        public void onCharacteristicWrite(BluetoothGatt gatt,
                BluetoothGattCharacteristic characteristic, int status) {
            
            super.onCharacteristicWrite(gatt, characteristic, status);

        }

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

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


        }

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

        }
    };

上述幾個(gè)回調(diào)函數(shù)是BLE開發(fā)中不可缺少的,每個(gè)函數(shù)的意義以及被調(diào)用的時(shí)機(jī)會(huì)在后續(xù)步驟中一一說(shuō)明骚烧。

(1)等待設(shè)備連接成功

當(dāng)調(diào)用targetdDevice.connectGatt(context, false, gattCallback)后系統(tǒng)會(huì)主動(dòng)發(fā)起與BLE藍(lán)牙設(shè)備的連接浸赫,若成功連接到設(shè)備將回調(diào)onConnectionStateChange方法,其處理過(guò)程如下:

@Override
public void onConnectionStateChange(BluetoothGatt gatt, int status,
                int newState) {
            if (newState == BluetoothGatt.STATE_CONNECTED) {
                Log.e(TAG, "設(shè)備連接上 開始掃描服務(wù)");
                // 開始掃描服務(wù)赃绊,安卓藍(lán)牙開發(fā)重要步驟之一
                mBluetoothGatt.discoverServices();
            }
            if (newState == BluetoothGatt.STATE_DISCONNECTED) {

                // 連接斷開
                /*連接斷開后的相應(yīng)處理*/      
            }
};

判斷newState == BluetoothGatt.STATE_CONNECTED表明此時(shí)已經(jīng)成功連接到設(shè)備既峡。

(2)開啟掃描服務(wù)
mBluetoothGatt.discoverServices();

掃描BLE設(shè)備服務(wù)是安卓系統(tǒng)中關(guān)于BLE藍(lán)牙開發(fā)的重要一步,一般在設(shè)備連接成功后調(diào)用碧查,掃描到設(shè)備服務(wù)后回調(diào)onServicesDiscovered()函數(shù)涧狮,函數(shù)原型如下:。

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

    private List<BluetoothGattService> servicesList;
    //獲取服務(wù)列表
    servicesList = mBluetoothGatt.getServices();

}

BLE藍(lán)牙協(xié)議下數(shù)據(jù)的通信方式采用BluetoothGattService么夫、BluetoothGattCharacteristic和BluetoothGattDescriptor三個(gè)主要的類實(shí)現(xiàn)通信者冤。
BluetoothGattService 簡(jiǎn)稱服務(wù),是構(gòu)成BLE設(shè)備協(xié)議棧的組成單位档痪,一個(gè)藍(lán)牙設(shè)備協(xié)議棧一般由一個(gè)或者多個(gè)BluetoothGattService組成涉枫。
BluetoothGattCharacteristic 簡(jiǎn)稱特征,一個(gè)服務(wù)包含一個(gè)或者多個(gè)特征腐螟,特征作為數(shù)據(jù)的基本單元愿汰。
一個(gè)BluetoothGattCharacteristic特征包含一個(gè)數(shù)據(jù)值和附加的關(guān)于特征的描述BluetoothGattDescriptor。
BluetoothGattDescriptor:用于描述特征的類乐纸,其同樣包含一個(gè)value值衬廷。

(3)獲取負(fù)責(zé)通信的BluetoothGattCharacteristic

BLE藍(lán)牙開發(fā)主要有負(fù)責(zé)通信的BluetoothGattService完成的。當(dāng)且稱為通信服務(wù)汽绢。通信服務(wù)通過(guò)硬件工程師提供的UUID獲取吗跋。獲取方式如下:

BluetoothGattService service = mBluetoothGatt.getService(UUID.fromString("藍(lán)牙模塊提供的負(fù)責(zé)通信UUID字符串"));

通信服務(wù)中包含負(fù)責(zé)讀寫的BluetoothGattCharacteristic,且分別稱為notifyCharacteristic和writeCharacteristic宁昭。其中notifyCharacteristic負(fù)責(zé)開啟監(jiān)聽跌宛,也就是啟動(dòng)收數(shù)據(jù)的通道,writeCharacteristic負(fù)責(zé)寫入數(shù)據(jù)积仗。
具體操作方式如下:

  BluetoothGattService service = mBluetoothGatt.getService(UUID.fromString("藍(lán)牙模塊提供的負(fù)責(zé)通信服務(wù)UUID字符串"));
   // 例如形式如:49535343-fe7d-4ae5-8fa9-9fafd205e455
  notifyCharacteristic = service.getCharacteristic(UUID.fromString("notify uuid"));
  writeCharacteristic =  service.getCharacteristic(UUID.fromString("write uuid"));
(4)開啟監(jiān)聽

開啟監(jiān)聽疆拘,即建立與設(shè)備的通信的首發(fā)數(shù)據(jù)通道,BLE開發(fā)中只有當(dāng)上位機(jī)成功開啟監(jiān)聽后才能與下位機(jī)收發(fā)數(shù)據(jù)寂曹。開啟監(jiān)聽的方式如下:

mBluetoothGatt.setCharacteristicNotification(notifyCharacteristic, true)
BluetoothGattDescriptor descriptor = characteristic
                            .getDescriptor(UUID
                                    .fromString
("00002902-0000-1000-8000-00805f9b34fb"));
descriptor.setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE);

若開啟監(jiān)聽成功則會(huì)回調(diào)BluetoothGattCallback中的onDescriptorWrite()方法哎迄,處理方式如下:

@Override
public void onDescriptorWrite(BluetoothGatt gatt,
                BluetoothGattDescriptor descriptor, int status) {
        if (status == BluetoothGatt.GATT_SUCCESS) {
                
            //開啟監(jiān)聽成功回右,可以像設(shè)備寫入命令了
            Log.e(TAG, "開啟監(jiān)聽成功");
        }
            
};
(5)寫入數(shù)據(jù)

監(jiān)聽成功后通過(guò)向 writeCharacteristic寫入數(shù)據(jù)實(shí)現(xiàn)與下位機(jī)的通信。寫入方式如下:

//value為上位機(jī)向下位機(jī)發(fā)送的指令
writeCharacteristic.setValue(value);
mBluetoothGatt.writeCharacteristic(writeCharacteristic)

其中:value一般為Hex格式指令漱挚,其內(nèi)容由設(shè)備通信的藍(lán)牙通信協(xié)議規(guī)定翔烁。

(6)接收數(shù)據(jù)

若寫入指令成功則回調(diào)BluetoothGattCallback中的onCharacteristicWrite()方法,說(shuō)明將數(shù)據(jù)已經(jīng)發(fā)送給下位機(jī)棱烂。

@Override
public void onCharacteristicWrite(BluetoothGatt gatt,
            BluetoothGattCharacteristic characteristic, int status) {
            
            if (status == BluetoothGatt.GATT_SUCCESS) {
                Log.e(TAG, "發(fā)送成功");
            }   
            super.onCharacteristicWrite(gatt, characteristic, status);
}

若發(fā)送的數(shù)據(jù)符合通信協(xié)議租漂,則下位機(jī)會(huì)向上位機(jī)回復(fù)相應(yīng)的數(shù)據(jù)阶女。發(fā)送的數(shù)據(jù)通過(guò)回調(diào)onCharacteristicChanged()方法獲取颊糜,其處理方式如下:

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

            // value為設(shè)備發(fā)送的數(shù)據(jù),根據(jù)數(shù)據(jù)協(xié)議進(jìn)行解析
            byte[] value = characteristic.getValue();

}

通過(guò)向下位機(jī)發(fā)送指令獲取下位機(jī)的回復(fù)數(shù)據(jù)秃踩,即可完成與設(shè)備的通信過(guò)程衬鱼。

(六)斷開連接

當(dāng)與設(shè)備完成通信之后之后一定要斷開與設(shè)備的連接。調(diào)用以下方法斷開與設(shè)備的連接:

mBluetoothGatt.disconnect();
mBluetoothGatt.close();

通過(guò)以上六個(gè)主要步驟即可實(shí)現(xiàn)與設(shè)備的通信流程憔杨,BLE藍(lán)牙開發(fā)流程相對(duì)固定鸟赫,只需按照固定步驟執(zhí)行即可。若按照上述流程扔不能完成整個(gè)通信過(guò)程消别,可以查看另外一篇文章安卓藍(lán)牙開發(fā)填坑之路抛蚤,看看是不是踩到坑了。
筆者能力有限寻狂,謝謝大家的支持岁经。。蛇券。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末缀壤,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子纠亚,更是在濱河造成了極大的恐慌塘慕,老刑警劉巖,帶你破解...
    沈念sama閱讀 212,383評(píng)論 6 493
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件蒂胞,死亡現(xiàn)場(chǎng)離奇詭異图呢,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)骗随,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,522評(píng)論 3 385
  • 文/潘曉璐 我一進(jìn)店門岳瞭,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人蚊锹,你說(shuō)我怎么就攤上這事瞳筏。” “怎么了牡昆?”我有些...
    開封第一講書人閱讀 157,852評(píng)論 0 348
  • 文/不壞的土叔 我叫張陵姚炕,是天一觀的道長(zhǎng)摊欠。 經(jīng)常有香客問(wèn)我,道長(zhǎng)柱宦,這世上最難降的妖魔是什么些椒? 我笑而不...
    開封第一講書人閱讀 56,621評(píng)論 1 284
  • 正文 為了忘掉前任,我火速辦了婚禮掸刊,結(jié)果婚禮上免糕,老公的妹妹穿的比我還像新娘。我一直安慰自己忧侧,他們只是感情好石窑,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,741評(píng)論 6 386
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著蚓炬,像睡著了一般松逊。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上肯夏,一...
    開封第一講書人閱讀 49,929評(píng)論 1 290
  • 那天经宏,我揣著相機(jī)與錄音,去河邊找鬼驯击。 笑死烁兰,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的徊都。 我是一名探鬼主播沪斟,決...
    沈念sama閱讀 39,076評(píng)論 3 410
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼碟贾!你這毒婦竟也來(lái)了币喧?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,803評(píng)論 0 268
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤袱耽,失蹤者是張志新(化名)和其女友劉穎杀餐,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體朱巨,經(jīng)...
    沈念sama閱讀 44,265評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡史翘,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,582評(píng)論 2 327
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了冀续。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片琼讽。...
    茶點(diǎn)故事閱讀 38,716評(píng)論 1 341
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖洪唐,靈堂內(nèi)的尸體忽然破棺而出钻蹬,到底是詐尸還是另有隱情,我是刑警寧澤凭需,帶...
    沈念sama閱讀 34,395評(píng)論 4 333
  • 正文 年R本政府宣布问欠,位于F島的核電站肝匆,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏顺献。R本人自食惡果不足惜旗国,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 40,039評(píng)論 3 316
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望注整。 院中可真熱鬧能曾,春花似錦、人聲如沸肿轨。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,798評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)萝招。三九已至蚂斤,卻和暖如春存捺,著一層夾襖步出監(jiān)牢的瞬間槐沼,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,027評(píng)論 1 266
  • 我被黑心中介騙來(lái)泰國(guó)打工捌治, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留岗钩,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 46,488評(píng)論 2 361
  • 正文 我出身青樓肖油,卻偏偏與公主長(zhǎng)得像兼吓,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子森枪,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,612評(píng)論 2 350