Android4.3 蓝牙BLE初步
 一、关键概念:
 Generic Attribute Profile  (GATT)
 通过BLE连接,读写属性类小数据的Profile通用规范。现在所有的BLE应用Profile都是基于GATT的。
  
 Attribute Protocol  (ATT)
 GATT是基于ATT  Protocol的。ATT针对BLE设备做了专门的优化,具体就是在传输过程中使用尽量少的数据。每个属性都有一个唯一的UUID,属性将以characteristics and services的形式传输。
  
 Characteristic
 Characteristic可以理解为一个数据类型,它包括一个value和0至多个对次value的描述(Descriptor)。
  
 Descriptor
 对Characteristic的描述,例如范围、计量单位等。
  
 Service
 Characteristic的集合。例如一个service叫做“Heart Rate  Monitor”,它可能包含多个Characteristics,其中可能包含一个叫做“heart rate  measurement"的Characteristic。
  
 二、角色和职责:
 Android设备与BLE设备交互有两组角色:
  
 中心设备和外围设备(Central vs. peripheral);
 GATT server vs. GATT client.
  
 Central vs. peripheral:
 中心设备和外围设备的概念针对的是BLE连接本身。Central角色负责scan  advertisement。而peripheral角色负责make  advertisement。
  
 GATT server vs. GATT  client:
 这两种角色取决于BLE连接成功后,两个设备间通信的方式。
  
 举例说明:
 现有一个活动追踪的BLE设备和一个支持BLE的Android设备。Android设备支持Central角色,而BLE设备支持peripheral角色。创建一个BLE连接需要这两个角色都存在,都仅支持Central角色或者都仅支持peripheral角色则无法建立连接。
  
 当连接建立后,它们之间就需要传输GATT数据。谁做server,谁做client,则取决于具体数据传输的情况。例如,如果活动追踪的BLE设备需要向Android设备传输sensor数据,则活动追踪器自然成为了server端;而如果活动追踪器需要从Android设备获取更新信息,则Android设备作为server端可能更合适。
  
  
 三、权限及feature:
 和经典蓝牙一样,应用使用蓝牙,需要声明BLUETOOTH权限,如果需要扫描设备或者操作蓝牙设置,则还需要BLUETOOTH_ADMIN权限:
  <uses-permission  android:name="android.permission.BLUETOOTH"/>
<uses-permission  android:name="android.permission.BLUETOOTH_ADMIN"/>
   
 除了蓝牙权限外,如果需要BLE  feature则还需要声明uses-feature:
  <uses-feature  android:name="android.hardware.bluetooth_le"  android:required="true"/>
   
 按时required为true时,则应用只能在支持BLE的Android设备上安装运行;required为false时,Android设备均可正常安装运行,需要在代码运行时判断设备是否支持BLE  feature:
  
  // Use this check to determine whether BLE is supported on the  device. Then
// you can selectively disable  BLE-related features.
if  (!getPackageManager().hasSystemFeature(PackageManager.FEATURE_BLUETOOTH_LE))  {
     Toast.makeText(this,  R.string.ble_not_supported,  Toast.LENGTH_SHORT).show();
     finish();
}
   
  
 四、启动蓝牙:
 在使用蓝牙BLE之前,需要确认Android设备是否支持BLE  feature(required为false时),另外要需要确认蓝牙是否打开。 
 如果发现不支持BLE,则不能使用BLE相关的功能。如果支持BLE,但是蓝牙没打开,则需要打开蓝牙。
  
 打开蓝牙的步骤:
  
 1、获取BluetoothAdapter
  
 BluetoothAdapter是Android系统中所有蓝牙操作都需要的,它对应本地Android设备的蓝牙模块,在整个系统中BluetoothAdapter是单例的。当你获取到它的示例之后,就能进行相关的蓝牙操作了。
  
 获取BluetoothAdapter代码示例如下:
 // Initializes Bluetooth  adapter.
final  BluetoothManager bluetoothManager  =
         (BluetoothManager)  getSystemService(Context.BLUETOOTH_SERVICE);
mBluetoothAdapter  =  bluetoothManager.getAdapter();
   
 注:这里通过getSystemService获取BluetoothManager,再通过BluetoothManager获取BluetoothAdapter。BluetoothManager在Android4.3以上支持(API  level 18)。
  
 2、判断是否支持蓝牙,并打开蓝牙
  
 获取到BluetoothAdapter之后,还需要判断是否支持蓝牙,以及蓝牙是否打开。
 如果没打开,需要让用户打开蓝牙:
  private BluetoothAdapter  mBluetoothAdapter;
...
//  Ensures Bluetooth is available on the device and it is enabled. If  not,
// displays a dialog requesting user  permission to enable Bluetooth.
if  (mBluetoothAdapter ==  null ||  !mBluetoothAdapter.isEnabled())  {
    Intent  enableBtIntent = new  Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
      startActivityForResult(enableBtIntent,  REQUEST_ENABLE_BT);
}
   
  
 五、搜索BLE设备:
    
 由于搜索需要尽量减少功耗,因此在实际使用时需要注意:
  
 1、当找到对应的设备后,立即停止扫描;
 2、不要循环搜索设备,为每次搜索设置适合的时间限制。避免设备不在可用范围的时候持续不停扫描,消耗电量。
  
 搜索的示例代码如下:
  /**
 * Activity for scanning and displaying available BLE  devices.
 */
public  class DeviceScanActivity  extends ListActivity  {
    private  BluetoothAdapter  mBluetoothAdapter;
     private boolean  mScanning;
    private  Handler mHandler;
     // Stops scanning after 10 seconds.
     private static  final long SCAN_PERIOD  = 10000;
     ...
    private  void  scanLeDevice(final  boolean enable)  {
        if  (enable)  {
            // Stops scanning after  a pre-defined scan period.
             mHandler.postDelayed(new  Runnable()  {
                 @Override
                 public void  run() {
                     mScanning =  false;
                     mBluetoothAdapter.stopLeScan(mLeScanCallback);
                 }
             }, SCAN_PERIOD);
             mScanning =  true;
             mBluetoothAdapter.startLeScan(mLeScanCallback);
         } else  {
            mScanning =  false;
             mBluetoothAdapter.stopLeScan(mLeScanCallback);
         }
        ...
      }
...
}
   
  其中UUID数组指定你的应用程序所支持的GATT  Services的UUID。
  
  
   private LeDeviceListAdapter  mLeDeviceListAdapter;
...
//  Device scan callback.
private  BluetoothAdapter.LeScanCallback  mLeScanCallback =
         new  BluetoothAdapter.LeScanCallback()  {
    @Override
     public void  onLeScan(final  BluetoothDevice device,  int rssi,
             byte[]  scanRecord) {
         runOnUiThread(new  Runnable()  {
            @Override
            public void  run() {
                mLeDeviceListAdapter.addDevice(device);
                 mLeDeviceListAdapter.notifyDataSetChanged();
            }
        });
    }
};
   
 注意:搜索时,你只能搜索传统蓝牙设备或者BLE设备,两者完全独立,不可同时被搜索。
  
  
 六、连接GATT  Server:
 两个设备通过BLE通信,首先需要建立GATT连接。这里我们讲的是Android设备作为client端,连接GATT  Server。
   
  mBluetoothGatt =  device.connectGatt(this,  false,  mGattCallback);
   
   
 BluetoothGatt常规用到的几个操作示例:
  
 connect()  :连接远程设备。
 discoverServices()  : 搜索连接设备所支持的service。
 disconnect():断开与远程设备的GATT连接。
 close():关闭GATT  Client端。
 readCharacteristic(characteristic)  :读取指定的characteristic。
 setCharacteristicNotification(characteristic, enabled)  :设置当指定characteristic值变化时,发出通知。
 getServices() :获取远程设备所支持的services。
  
 等等。
  
 注:
 1、某些函数调用之间存在先后关系。例如首先需要connect上才能discoverServices。
 2、一些函数调用是异步的,需要得到的值不会立即返回,而会在BluetoothGattCallback的回调函数中返回。例如discoverServices与onServicesDiscovered回调,readCharacteristic与onCharacteristicRead回调,setCharacteristicNotification与onCharacteristicChanged回调等。