簡(jiǎn)述Android Bluetooth Low Energy

目錄
[TOC]

Bluetooth Low Energy

  • 簡(jiǎn)介:
    • 與傳統(tǒng)藍(lán)牙相比鼓拧,低功耗藍(lán)牙的設(shè)計(jì)對(duì)電量消耗更低驯杜。
    • 只支持Android 4.3以上的系統(tǒng)版本迅办,即 API Level>=18。

藍(lán)牙開(kāi)發(fā)對(duì)象

  • BluetoothManager

    • 獲取對(duì)象:

        BluetoothManager bluetoothManager = (BluetoothManager) getSystemService(Context.BLUETOOTH_SERVICE);
      
    • 注意:BluetoothManager僅在Android4.3以上的系統(tǒng)版本支持套利,即 API Level>=18奴璃。

  • BluetoothAdapter

    • 獲取對(duì)象:

        BluetoothAdapter mBluetoothAdapter = BluetoothManager.getAdapter();
        // 或者
        BluetoothAdapter mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
      
      • 注意:一個(gè)Android系統(tǒng)只有一個(gè)BluetoothAdapter悉默。
    • 方法描述:

      • 藍(lán)牙狀態(tài)
        • isEnabled()
          • 判斷系統(tǒng)藍(lán)牙是否打開(kāi)
        • disable()
          • 隱式關(guān)閉系統(tǒng)藍(lán)牙
        • enable()
          • 隱式打開(kāi)系統(tǒng)藍(lán)牙
      • 藍(lán)牙設(shè)備搜索
        • startDiscovery()/cancelDiscovery()
          • 開(kāi)始/取消搜索設(shè)備(經(jīng)典藍(lán)牙 和 低功耗藍(lán)牙)
          • 執(zhí)行過(guò)程(耗時(shí)12秒):
            1. 系統(tǒng)發(fā)送 BluetoothAdapter.ACTIOIN_DISCOVERY_STARTED 的廣播
            2. 搜索藍(lán)牙設(shè)備...
            3. 只要找到一個(gè)設(shè)備就發(fā)送一個(gè) BluetoothDevice.ACTION_FOUND 的廣播
            4. 從廣播接收器中就可以得到這個(gè)BluetoothDevice對(duì)象
            5. 系統(tǒng)發(fā)送 BluetoothAdapter.ACTION_FINISHED 的廣播
        • startLeScan()/stopLeScan()
          • 開(kāi)始/取消搜索設(shè)備(低功耗藍(lán)牙)
          • 注意:
            • 在API Level-21以上被標(biāo)記廢棄使用
  • BluetoothLeScanner

    • 獲取對(duì)象:

        BluetoothLeScanner mBluetoothLeScanner= BluetoothAdapter.getBluetoothLeScanner();
      
    • 方法描述:

      • startScan()/stopScan()
        • 開(kāi)始/取消搜索設(shè)備(低功耗藍(lán)牙)
        • 注意:
          • 在API Level-21以上使用
  • BluetoothDevice

    • 代表了一個(gè)遠(yuǎn)程的藍(lán)牙設(shè)備, 通過(guò)這個(gè)類(lèi)可以查詢(xún)遠(yuǎn)程設(shè)備的物理地址, 名稱(chēng), 連接狀態(tài)等信息,
    • 獲取對(duì)象:
      • 物理地址對(duì)應(yīng)的Device:

          BluetoothAdapter.getRemoteDevice(address);
        
      • 已經(jīng)配對(duì)的Device集合:

          BluetoothAdapter.getBoundedDevices();
        
      • 掃描結(jié)果回調(diào)中的Device:

          BluetoothAdapter.LeScanCallback leScanCallback = new BluetoothAdapter.LeScanCallback() {
              @Override
              public void onLeScan(BluetoothDevice device, int rssi, byte[] scanRecord) {
              }
          };
        
  • BluetoothGatt

    • BluetoothGatt繼承BluetoothProfile苟穆,
    • 通過(guò)BluetoothGatt可以連接設(shè)備(connect),發(fā)現(xiàn)服務(wù)(discoverServices)麦牺,并把相應(yīng)地屬性返回到 BluetoothGattCallback,
    • 獲取對(duì)象:
      • BluetoothGattCallback回調(diào)方法中獲取鞭缭。
  • BluetoothProfile

    • 一個(gè)通用的規(guī)范剖膳,按照這個(gè)規(guī)范來(lái)收發(fā)數(shù)據(jù)。
  • 連接成功后岭辣,我們首先需要獲得服務(wù)吱晒,然后是該服務(wù)所包含的特征,最后是特征的描述符沦童。

  • 服務(wù)(Service):

    • BluetoothGattService
      • 一個(gè) Service 可以包含多個(gè) Characteristic仑濒,
      • 獲取對(duì)象:
        • 通過(guò)指定的 UUID 從 BluetoothGatt 實(shí)例中獲得:

            BluetoothGattService service = BluetoothGatt.getService(UUID.fromString(BLE_SERVICE));
          
  • 特征(Characteristic):

    • BluetoothGattCharacteristic
      • 一個(gè) Characteristic 包含一個(gè) Value 和多個(gè) Descriptor,
      • 相當(dāng)于一個(gè)數(shù)據(jù)類(lèi)型偷遗,它包括一個(gè)value和0~n個(gè)value的描述(Descriptor)墩瞳,
      • 獲取對(duì)象:
        • 通過(guò)指定的 UUID 從 BluetoothGattService 中得到:

            BluetoothGattCharacteristic characteristic = BluetoothGattService.getCharacteristic(UUID.fromString(BLE_CHARACTERISTIC))
          
  • 描述符(Descriptor):

    • BluetoothGattDescriptor
      • 一個(gè) Descriptor 包含一個(gè) Value,
      • 對(duì) Characteristic 的描述氏豌,包括范圍喉酌、計(jì)量單位等,
      • 獲取對(duì)象:
        • 通過(guò)指定的 UUID 從 BluetoothGattCharacteristic 對(duì)象中獲得:

            List<BluetoothGattDescriptor> descriptorList = BluetoothGattCharacteristic.getDescriptors();
            // 或者
            BluetoothGattDescriptor descriptor = BluetoothGattCharacteristic.getDescriptor(UUID.fromString("BLE_DESCRIPTOR"));
          

低功耗藍(lán)牙開(kāi)發(fā)基本流程

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

    • 基礎(chǔ)權(quán)限:

        <!-- 執(zhí)行所有的藍(lán)牙通信泵喘,如請(qǐng)求連接泪电,接受連接和傳輸數(shù)據(jù) -->
        <uses-permission android:name="android.permission.BLUETOOTH"/>
        <!-- 初始化設(shè)備發(fā)現(xiàn)或者操縱藍(lán)牙設(shè)置 -->
        <uses-permission android:name="android.permission.BLUETOOTH_ADMIN"/>
      
    • 注意:

      • 如果應(yīng)用僅支持低功耗藍(lán)牙
        • 在 AndroidManifest.xml 添加以下聲明:

            <uses-feature android:name="android.hardware.bluetooth_le" android:required="true"/>
          
        • 動(dòng)態(tài)判斷:

            if (!getPackageManager().hasSystemFeature(PackageManager.FEATURE_BLUETOOTH_LE)) {
                Toast.makeText(this, "該設(shè)備不支持低功耗藍(lán)牙", Toast.LENGTH_SHORT).show();
            }  
          
      • 在 Android 6.0 及以上的系統(tǒng)版本,需動(dòng)態(tài)申請(qǐng)位置權(quán)限纪铺。
        如果應(yīng)用沒(méi)有位置權(quán)限相速,藍(lán)牙掃描功能不能使用(其它藍(lán)牙操作例如連接藍(lán)牙設(shè)備和寫(xiě)入數(shù)據(jù)不受影響)。
        • 在 AndroidManifest.xml 添加以下聲明:

          <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/>
          或
          <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
          
        • 動(dòng)態(tài)申請(qǐng):

          if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
              if (this.checkSelfPermission(Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED) {
                  requestPermissions(new String[]{Manifest.permission.ACCESS_COARSE_LOCATION}, PERMISSION_REQUEST_COARSE_LOCATION);
              }
          }
          
          @Override
          public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
              switch (requestCode) {
                  case PERMISSION_REQUEST_COARSE_LOCATION:
                      if (grantResults[0] == PackageManager.PERMISSION_GRANTED) {
                          // 權(quán)限申請(qǐng)成功鲜锚,處理業(yè)務(wù)邏輯
                      }
                      break;
                  default:
                      break;
              }
          }
          
  • 是否支持藍(lán)牙

      BluetoothManager bluetoothManager = (BluetoothManager) getSystemService(Context.BLUETOOTH_SERVICE);
      
      if (bluetoothManager.getAdapter() == null) {
          Toast.makeText(Context, "沒(méi)有發(fā)現(xiàn)藍(lán)牙模塊", Toast.LENGTH_SHORT).show();
      }
    
  • 開(kāi)啟/關(guān)閉藍(lán)牙

    • 檢查藍(lán)牙是否開(kāi)啟

        BluetoothAdapter.isEnabled();
      
    • 開(kāi)啟

      • 隱式開(kāi)啟

          BluetoothAdapter.enable();
        
        • 注意:
          • 需要注冊(cè)權(quán)限:

              <uses-permission android:name="android.permission.BLUETOOTH_ADMIN"/>
            
          • 在 Android 6.0 及以上的系統(tǒng)版本突诬,隱式開(kāi)啟依舊會(huì)提示用戶(hù)苫拍。

      • 顯示開(kāi)啟

          Intent enableBtIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
          startActivityForResult(enableBtIntent, REQUESTCODE_BLUETOOTH);
        
        • 確認(rèn)結(jié)果:

          • 方法一:注冊(cè)廣播

              IntentFilter intentFilter = new IntentFilter();  
              intentFilter.addAction(BluetoothAdapter.ACTION_STATE_CHANGED);//藍(lán)牙狀態(tài)
              intentFilter.addAction(BluetoothAdapter.ACTION_DISCOVERY_STARTED);// 掃描開(kāi)始
              intentFilter.addAction(BluetoothAdapter.ACTION_DISCOVERY_FINISHED);// 掃描結(jié)束
              registerReceiver(new BluetoothReceiver(), intentFilter);
            
              private class BluetoothReceiver extends BroadcastReceiver {
            
                  @Override
                  public void onReceive(Context context, Intent intent) {
            
                      String action = intent.getAction();
                      if (BluetoothAdapter.ACTION_STATE_CHANGED.equals(action)) {
                          switch (BluetoothAdapter.getDefaultAdapter().getState()) {
                              case BluetoothAdapter.STATE_ON:// 打開(kāi)
                                  break;
                              case BluetoothAdapter.STATE_OFF:// 關(guān)閉
                                  break;
                              case BluetoothAdapter.STATE_TURNING_OFF:// 藍(lán)牙處于關(guān)閉過(guò)程中
                                  break;
                              case BluetoothAdapter.STATE_TURNING_ON:// 藍(lán)牙處于打開(kāi)過(guò)程中
                                  break;
                              default:
                                  break;
                          }
                      }
                  }
              }
            
          • 方法二:重寫(xiě)onActivityResult()

              @Override
              protected void onActivityResult(int requestCode, int resultCode, Intent data) {
                  super.onActivityResult(requestCode, resultCode, data);
                  if (resultCode == RESULT_OK) {//藍(lán)牙開(kāi)啟成功
                      switch (requestCode) {
                          case REQUESTCODE_BLUETOOTH:
                              break;
                          default:
                              break;
                      }
                  } else if (resultCode == RESULT_CANCELED) {//藍(lán)牙開(kāi)啟失敗
                      ToastUtils.showShort("藍(lán)牙開(kāi)啟失敗,請(qǐng)手動(dòng)開(kāi)啟藍(lán)牙");
                  }
              }
            
        • 前往系統(tǒng)設(shè)置界面

            startActivity(new Intent(Settings.ACTION_BLUETOOTH_SETTINGS));
          
    • 關(guān)閉

      • 隱式關(guān)閉

          BluetoothAdapter.disable();
        
      • 前往系統(tǒng)設(shè)置界面

          startActivity(new Intent(Settings.ACTION_BLUETOOTH_SETTINGS));
        
  • 掃描藍(lán)牙設(shè)備

    • 開(kāi)啟/關(guān)閉掃描設(shè)備
      • 開(kāi)啟

        • 掃描全部藍(lán)牙設(shè)備

            BluetoothAdapter.startLeScan(BluetoothAdapter.LeScanCallback callback)
          
        • 只掃描含有特定 UUID Service 的藍(lán)牙設(shè)備

            BluetoothAdapter.startLeScan(UUID[] serviceUuids, BluetoothAdapter.LeScanCallback callback)
          
      • 關(guān)閉

          BluetoothAdapter.stopLeScan(BluetoothAdapter.LeScanCallback callback)
        
  • 連接設(shè)備

      BluetoothGatt BluetoothDevice.connectGatt(Context context, boolean autoConnect, BluetoothGattCallback callback) {}
    
    • autoConnect:表示是否需要自動(dòng)連接旺隙。
      • 設(shè)置為 true 表示如果設(shè)備斷開(kāi)了怯疤,會(huì)不斷的嘗試自動(dòng)連接。
      • 設(shè)置為 false 表示只進(jìn)行一次連接嘗試催束。
    • BluetoothGattCallback:表示連接后進(jìn)行的一系列操作的回調(diào),共計(jì)9個(gè)回調(diào)方法伏社,以下列舉常用的5個(gè)抠刺。
      • 連接狀態(tài)變化

          void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) {}
        
      • 執(zhí)行BluetoothGatt.discoverServices();后回調(diào),發(fā)現(xiàn)服務(wù)

          void onServicesDiscovered(BluetoothGatt gatt, int status) {}
        
      • 執(zhí)行BluetoothGatt.writeCharacteristic();后回調(diào)摘昌,寫(xiě)入數(shù)據(jù)執(zhí)行結(jié)果

          void onCharacteristicWrite(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) {}
        
      • 執(zhí)行BluetoothGatt.setCharacteristicNotification();后回調(diào)速妖,Characteristic值發(fā)生改變

          void onCharacteristicChanged(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) {}
        
      • 執(zhí)行BluetoothGatt.writeDescriptor();后回調(diào)

          void onDescriptorWrite(BluetoothGatt gatt, BluetoothGattDescriptor descriptor, int status) {}
        
  • 發(fā)現(xiàn)服務(wù)

    • 在onConnectionStateChange()中,判斷連接狀態(tài)聪黎,連接成功罕容,搜索連接設(shè)備所支持的service

        @Override
        public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) {
            super.onConnectionStateChange(gatt, status, newState);
      
            if (newState == BluetoothGatt.STATE_CONNECTED) {//連接成功
      
                gatt.discoverServices();//搜索連接設(shè)備所支持的service
      
            } else if (newState == BluetoothGatt.STATE_DISCONNECTED) {//連接斷開(kāi)
            }
        }
      
    • discoverServices()被執(zhí)行后,在onServicesDiscovered()中稿饰,獲取指定UUID Service锦秒,存下BluetoothGatt對(duì)象

        @Override
        public void onServicesDiscovered(BluetoothGatt gatt, int status) {
            super.onServicesDiscovered(gatt, status);
      
            BluetoothGattService service = gatt.getService(UUID.fromString(BLE_SERVICE));
      
            if (service != null) {
                mBluetoothGatt = gatt;
            }
        }
      
  • 讀寫(xiě)數(shù)據(jù)

    • 寫(xiě):
      • 獲取指定 UUID Serivce 的 BluetoothGattService 對(duì)象,

      • 從而得到指定 UUID Characteristic 的 BluetoothGattCharacteristic 對(duì)象喉镰,將數(shù)據(jù)設(shè)置進(jìn)去旅择,

      • 最后用 BluetoothGatt 對(duì)象向藍(lán)牙設(shè)備寫(xiě)入數(shù)據(jù)

          public void writeCharacteristic() {
              
              byte[] data = {0x00};//封包數(shù)據(jù),根據(jù)硬件協(xié)議填寫(xiě)
              
              BluetoothGattCharacteristic characteristic = mBluetoothGatt.getService(UUID.fromString(BLE_SERVICE)).getCharacteristic(UUID.fromString(BLE_WRITE));
              characteristic.setValue(data);//設(shè)置數(shù)據(jù)
        
              mBluetoothGatt.writeCharacteristic(characteristic);//寫(xiě)入設(shè)備
          }
        
    • 讀:
      • writeCharacteristic() 調(diào)用后侣姆,

      • 會(huì)走 onCharacteristicChanged() 回調(diào)生真,

      • BluetoothGattCharacteristic 對(duì)象中獲取藍(lán)牙設(shè)備傳回的數(shù)據(jù),

      • 通過(guò)藍(lán)牙硬件協(xié)議判斷并做出相應(yīng)處理捺宗。

          @Override
          public void onCharacteristicChanged(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) {
              super.onCharacteristicChanged(gatt, characteristic);
              
              byte[] value = characteristic.getValue();
        
              switch (value[0]) {//判斷協(xié)議柱蟀,對(duì)應(yīng)處理
                  case 0x00:
                      break;
                  default:
                      break;
              }
          }
        
  • 斷開(kāi)連接

      BluetoothGatt.disconnect();
      BluetoothGatt.close();
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市蚜厉,隨后出現(xiàn)的幾起案子长已,更是在濱河造成了極大的恐慌,老刑警劉巖昼牛,帶你破解...
    沈念sama閱讀 221,548評(píng)論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件痰哨,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡匾嘱,警方通過(guò)查閱死者的電腦和手機(jī)斤斧,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,497評(píng)論 3 399
  • 文/潘曉璐 我一進(jìn)店門(mén),熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)霎烙,“玉大人撬讽,你說(shuō)我怎么就攤上這事蕊连。” “怎么了游昼?”我有些...
    開(kāi)封第一講書(shū)人閱讀 167,990評(píng)論 0 360
  • 文/不壞的土叔 我叫張陵甘苍,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我烘豌,道長(zhǎng)载庭,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 59,618評(píng)論 1 296
  • 正文 為了忘掉前任廊佩,我火速辦了婚禮囚聚,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘标锄。我一直安慰自己顽铸,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,618評(píng)論 6 397
  • 文/花漫 我一把揭開(kāi)白布料皇。 她就那樣靜靜地躺著谓松,像睡著了一般。 火紅的嫁衣襯著肌膚如雪践剂。 梳的紋絲不亂的頭發(fā)上鬼譬,一...
    開(kāi)封第一講書(shū)人閱讀 52,246評(píng)論 1 308
  • 那天,我揣著相機(jī)與錄音逊脯,去河邊找鬼拧簸。 笑死,一個(gè)胖子當(dāng)著我的面吹牛男窟,可吹牛的內(nèi)容都是我干的盆赤。 我是一名探鬼主播,決...
    沈念sama閱讀 40,819評(píng)論 3 421
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼歉眷,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼牺六!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起汗捡,我...
    開(kāi)封第一講書(shū)人閱讀 39,725評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤淑际,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后扇住,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體春缕,經(jīng)...
    沈念sama閱讀 46,268評(píng)論 1 320
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,356評(píng)論 3 340
  • 正文 我和宋清朗相戀三年艘蹋,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了锄贼。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,488評(píng)論 1 352
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡女阀,死狀恐怖宅荤,靈堂內(nèi)的尸體忽然破棺而出屑迂,到底是詐尸還是另有隱情,我是刑警寧澤冯键,帶...
    沈念sama閱讀 36,181評(píng)論 5 350
  • 正文 年R本政府宣布惹盼,位于F島的核電站,受9級(jí)特大地震影響惫确,放射性物質(zhì)發(fā)生泄漏手报。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,862評(píng)論 3 333
  • 文/蒙蒙 一改化、第九天 我趴在偏房一處隱蔽的房頂上張望掩蛤。 院中可真熱鬧,春花似錦所袁、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 32,331評(píng)論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至懦窘,卻和暖如春前翎,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背畅涂。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,445評(píng)論 1 272
  • 我被黑心中介騙來(lái)泰國(guó)打工港华, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人午衰。 一個(gè)月前我還...
    沈念sama閱讀 48,897評(píng)論 3 376
  • 正文 我出身青樓立宜,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親臊岸。 傳聞我的和親對(duì)象是個(gè)殘疾皇子橙数,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,500評(píng)論 2 359

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