简述Android Bluetooth Low Energy

目录
[TOC]

Bluetooth Low Energy

  • 简介:
    • 与传统蓝牙相比,低功耗蓝牙的设计对电量消耗更低。
    • 只支持Android 4.3以上的系统版本,即 API Level>=18。

蓝牙开发对象

  • BluetoothManager

    • 获取对象:

        BluetoothManager bluetoothManager = (BluetoothManager) getSystemService(Context.BLUETOOTH_SERVICE);
      
    • 注意:BluetoothManager仅在Android4.3以上的系统版本支持,即 API Level>=18。

  • BluetoothAdapter

    • 获取对象:

        BluetoothAdapter mBluetoothAdapter = BluetoothManager.getAdapter();
        // 或者
        BluetoothAdapter mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
      
      • 注意:一个Android系统只有一个BluetoothAdapter。
    • 方法描述:

      • 蓝牙状态
        • isEnabled()
          • 判断系统蓝牙是否打开
        • disable()
          • 隐式关闭系统蓝牙
        • enable()
          • 隐式打开系统蓝牙
      • 蓝牙设备搜索
        • startDiscovery()/cancelDiscovery()
          • 开始/取消搜索设备(经典蓝牙 和 低功耗蓝牙)
          • 执行过程(耗时12秒):
            1. 系统发送 BluetoothAdapter.ACTIOIN_DISCOVERY_STARTED 的广播
            2. 搜索蓝牙设备...
            3. 只要找到一个设备就发送一个 BluetoothDevice.ACTION_FOUND 的广播
            4. 从广播接收器中就可以得到这个BluetoothDevice对象
            5. 系统发送 BluetoothAdapter.ACTION_FINISHED 的广播
        • startLeScan()/stopLeScan()
          • 开始/取消搜索设备(低功耗蓝牙)
          • 注意:
            • 在API Level-21以上被标记废弃使用
  • BluetoothLeScanner

    • 获取对象:

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

      • startScan()/stopScan()
        • 开始/取消搜索设备(低功耗蓝牙)
        • 注意:
          • 在API Level-21以上使用
  • BluetoothDevice

    • 代表了一个远程的蓝牙设备, 通过这个类可以查询远程设备的物理地址, 名称, 连接状态等信息,
    • 获取对象:
      • 物理地址对应的Device:

          BluetoothAdapter.getRemoteDevice(address);
        
      • 已经配对的Device集合:

          BluetoothAdapter.getBoundedDevices();
        
      • 扫描结果回调中的Device:

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

    • BluetoothGatt继承BluetoothProfile,
    • 通过BluetoothGatt可以连接设备(connect),发现服务(discoverServices),并把相应地属性返回到 BluetoothGattCallback,
    • 获取对象:
      • BluetoothGattCallback回调方法中获取。
  • BluetoothProfile

    • 一个通用的规范,按照这个规范来收发数据。
  • 连接成功后,我们首先需要获得服务,然后是该服务所包含的特征,最后是特征的描述符。

  • 服务(Service):

    • BluetoothGattService
      • 一个 Service 可以包含多个 Characteristic,
      • 获取对象:
        • 通过指定的 UUID 从 BluetoothGatt 实例中获得:

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

    • BluetoothGattCharacteristic
      • 一个 Characteristic 包含一个 Value 和多个 Descriptor,
      • 相当于一个数据类型,它包括一个value和0~n个value的描述(Descriptor),
      • 获取对象:
        • 通过指定的 UUID 从 BluetoothGattService 中得到:

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

    • BluetoothGattDescriptor
      • 一个 Descriptor 包含一个 Value,
      • 对 Characteristic 的描述,包括范围、计量单位等,
      • 获取对象:
        • 通过指定的 UUID 从 BluetoothGattCharacteristic 对象中获得:

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

低功耗蓝牙开发基本流程

  • 申请权限

    • 基础权限:

        <!-- 执行所有的蓝牙通信,如请求连接,接受连接和传输数据 -->
        <uses-permission android:name="android.permission.BLUETOOTH"/>
        <!-- 初始化设备发现或者操纵蓝牙设置 -->
        <uses-permission android:name="android.permission.BLUETOOTH_ADMIN"/>
      
    • 注意:

      • 如果应用仅支持低功耗蓝牙
        • 在 AndroidManifest.xml 添加以下声明:

            <uses-feature android:name="android.hardware.bluetooth_le" android:required="true"/>
          
        • 动态判断:

            if (!getPackageManager().hasSystemFeature(PackageManager.FEATURE_BLUETOOTH_LE)) {
                Toast.makeText(this, "该设备不支持低功耗蓝牙", Toast.LENGTH_SHORT).show();
            }  
          
      • 在 Android 6.0 及以上的系统版本,需动态申请位置权限。
        如果应用没有位置权限,蓝牙扫描功能不能使用(其它蓝牙操作例如连接蓝牙设备和写入数据不受影响)。
        • 在 AndroidManifest.xml 添加以下声明:

          <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/>
          或
          <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
          
        • 动态申请:

          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) {
                          // 权限申请成功,处理业务逻辑
                      }
                      break;
                  default:
                      break;
              }
          }
          
  • 是否支持蓝牙

      BluetoothManager bluetoothManager = (BluetoothManager) getSystemService(Context.BLUETOOTH_SERVICE);
      
      if (bluetoothManager.getAdapter() == null) {
          Toast.makeText(Context, "没有发现蓝牙模块", Toast.LENGTH_SHORT).show();
      }
    
  • 开启/关闭蓝牙

    • 检查蓝牙是否开启

        BluetoothAdapter.isEnabled();
      
    • 开启

      • 隐式开启

          BluetoothAdapter.enable();
        
        • 注意:
          • 需要注册权限:

              <uses-permission android:name="android.permission.BLUETOOTH_ADMIN"/>
            
          • 在 Android 6.0 及以上的系统版本,隐式开启依旧会提示用户。

      • 显示开启

          Intent enableBtIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
          startActivityForResult(enableBtIntent, REQUESTCODE_BLUETOOTH);
        
        • 确认结果:

          • 方法一:注册广播

              IntentFilter intentFilter = new IntentFilter();  
              intentFilter.addAction(BluetoothAdapter.ACTION_STATE_CHANGED);//蓝牙状态
              intentFilter.addAction(BluetoothAdapter.ACTION_DISCOVERY_STARTED);// 扫描开始
              intentFilter.addAction(BluetoothAdapter.ACTION_DISCOVERY_FINISHED);// 扫描结束
              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:// 打开
                                  break;
                              case BluetoothAdapter.STATE_OFF:// 关闭
                                  break;
                              case BluetoothAdapter.STATE_TURNING_OFF:// 蓝牙处于关闭过程中
                                  break;
                              case BluetoothAdapter.STATE_TURNING_ON:// 蓝牙处于打开过程中
                                  break;
                              default:
                                  break;
                          }
                      }
                  }
              }
            
          • 方法二:重写onActivityResult()

              @Override
              protected void onActivityResult(int requestCode, int resultCode, Intent data) {
                  super.onActivityResult(requestCode, resultCode, data);
                  if (resultCode == RESULT_OK) {//蓝牙开启成功
                      switch (requestCode) {
                          case REQUESTCODE_BLUETOOTH:
                              break;
                          default:
                              break;
                      }
                  } else if (resultCode == RESULT_CANCELED) {//蓝牙开启失败
                      ToastUtils.showShort("蓝牙开启失败,请手动开启蓝牙");
                  }
              }
            
        • 前往系统设置界面

            startActivity(new Intent(Settings.ACTION_BLUETOOTH_SETTINGS));
          
    • 关闭

      • 隐式关闭

          BluetoothAdapter.disable();
        
      • 前往系统设置界面

          startActivity(new Intent(Settings.ACTION_BLUETOOTH_SETTINGS));
        
  • 扫描蓝牙设备

    • 开启/关闭扫描设备
      • 开启

        • 扫描全部蓝牙设备

            BluetoothAdapter.startLeScan(BluetoothAdapter.LeScanCallback callback)
          
        • 只扫描含有特定 UUID Service 的蓝牙设备

            BluetoothAdapter.startLeScan(UUID[] serviceUuids, BluetoothAdapter.LeScanCallback callback)
          
      • 关闭

          BluetoothAdapter.stopLeScan(BluetoothAdapter.LeScanCallback callback)
        
  • 连接设备

      BluetoothGatt BluetoothDevice.connectGatt(Context context, boolean autoConnect, BluetoothGattCallback callback) {}
    
    • autoConnect:表示是否需要自动连接。
      • 设置为 true 表示如果设备断开了,会不断的尝试自动连接。
      • 设置为 false 表示只进行一次连接尝试。
    • BluetoothGattCallback:表示连接后进行的一系列操作的回调,共计9个回调方法,以下列举常用的5个。
      • 连接状态变化

          void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) {}
        
      • 执行BluetoothGatt.discoverServices();后回调,发现服务

          void onServicesDiscovered(BluetoothGatt gatt, int status) {}
        
      • 执行BluetoothGatt.writeCharacteristic();后回调,写入数据执行结果

          void onCharacteristicWrite(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) {}
        
      • 执行BluetoothGatt.setCharacteristicNotification();后回调,Characteristic值发生改变

          void onCharacteristicChanged(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) {}
        
      • 执行BluetoothGatt.writeDescriptor();后回调

          void onDescriptorWrite(BluetoothGatt gatt, BluetoothGattDescriptor descriptor, int status) {}
        
  • 发现服务

    • 在onConnectionStateChange()中,判断连接状态,连接成功,搜索连接设备所支持的service

        @Override
        public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) {
            super.onConnectionStateChange(gatt, status, newState);
      
            if (newState == BluetoothGatt.STATE_CONNECTED) {//连接成功
      
                gatt.discoverServices();//搜索连接设备所支持的service
      
            } else if (newState == BluetoothGatt.STATE_DISCONNECTED) {//连接断开
            }
        }
      
    • discoverServices()被执行后,在onServicesDiscovered()中,获取指定UUID Service,存下BluetoothGatt对象

        @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;
            }
        }
      
  • 读写数据

    • 写:
      • 获取指定 UUID Serivce 的 BluetoothGattService 对象,

      • 从而得到指定 UUID Characteristic 的 BluetoothGattCharacteristic 对象,将数据设置进去,

      • 最后用 BluetoothGatt 对象向蓝牙设备写入数据

          public void writeCharacteristic() {
              
              byte[] data = {0x00};//封包数据,根据硬件协议填写
              
              BluetoothGattCharacteristic characteristic = mBluetoothGatt.getService(UUID.fromString(BLE_SERVICE)).getCharacteristic(UUID.fromString(BLE_WRITE));
              characteristic.setValue(data);//设置数据
        
              mBluetoothGatt.writeCharacteristic(characteristic);//写入设备
          }
        
    • 读:
      • writeCharacteristic() 调用后,

      • 会走 onCharacteristicChanged() 回调,

      • BluetoothGattCharacteristic 对象中获取蓝牙设备传回的数据,

      • 通过蓝牙硬件协议判断并做出相应处理。

          @Override
          public void onCharacteristicChanged(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) {
              super.onCharacteristicChanged(gatt, characteristic);
              
              byte[] value = characteristic.getValue();
        
              switch (value[0]) {//判断协议,对应处理
                  case 0x00:
                      break;
                  default:
                      break;
              }
          }
        
  • 断开连接

      BluetoothGatt.disconnect();
      BluetoothGatt.close();
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,732评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,496评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,264评论 0 338
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,807评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,806评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,675评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,029评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,683评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 41,704评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,666评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,773评论 1 332
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,413评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,016评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,978评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,204评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,083评论 2 350
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,503评论 2 343

推荐阅读更多精彩内容