低功耗蓝牙(BLE)之开发步骤

Android Bluetooth源码静态类图如下:


使用低功耗蓝牙需要用到的权限:

?
1
2
<uses-permission android:name= "android.permission.BLUETOOTH" />
<uses-permission android:name= "android.permission.BLUETOOTH_ADMIN" />

下面介绍怎样使用BLE:

1、准备BLE

    1)获取BluetoothAdapter

     BluetoothAdapter是从系统服务获取到的,全系统就一个。

?
1
2
3
4
// Initializes Bluetooth adapter.
final  BluetoothManager bluetoothManager =
         (BluetoothManager) getSystemService(Context.BLUETOOTH_SERVICE);
mBluetoothAdapter = bluetoothManager.getAdapter();

    2)检测蓝牙是否打开

    如果蓝牙未打开,系统会自动打开,会弹出系统框展示打开蓝牙。

?
1
2
3
4
5
6
7
8
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);
}


2、查找BLE设备

   因为扫描BLE设备是电源密集型操作,浪费电量,因此要保证以下原则:

    1)扫描到需要的设备后,马上停止扫描;

    2)给扫描一个时间限制

   扫描示例代码如下:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
/**
  * 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);
         }
         ...
     }
...
}

     如果只是要扫描到特定类型的设备,则使用接口 startLeScan(UUID[], BluetoothAdapter.LeScanCallback)通过UUID来查找设备。

    扫描回调的代码如下所示:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
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设备,也可以扫描普通蓝牙设备,也可以同时将BLE设备和普通蓝牙设备一起扫描到。


3、连接到GATT Server

   获取到BluetoothGatt实例,

?
1
mBluetoothGatt = device.connectGatt( this false , mGattCallback);

    具体实例如下:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
// A service that interacts with the BLE device via the Android BLE API.
public  class  BluetoothLeService  extends  Service {
     private  final  static  String TAG = BluetoothLeService. class .getSimpleName();
 
     private  BluetoothManager mBluetoothManager;
     private  BluetoothAdapter mBluetoothAdapter;
     private  String mBluetoothDeviceAddress;
     private  BluetoothGatt mBluetoothGatt;
     private  int  mConnectionState = STATE_DISCONNECTED;
 
     private  static  final  int  STATE_DISCONNECTED =  0 ;
     private  static  final  int  STATE_CONNECTING =  1 ;
     private  static  final  int  STATE_CONNECTED =  2 ;
 
     public  final  static  String ACTION_GATT_CONNECTED =
             "com.example.bluetooth.le.ACTION_GATT_CONNECTED" ;
     public  final  static  String ACTION_GATT_DISCONNECTED =
             "com.example.bluetooth.le.ACTION_GATT_DISCONNECTED" ;
     public  final  static  String ACTION_GATT_SERVICES_DISCOVERED =
             "com.example.bluetooth.le.ACTION_GATT_SERVICES_DISCOVERED" ;
     public  final  static  String ACTION_DATA_AVAILABLE =
             "com.example.bluetooth.le.ACTION_DATA_AVAILABLE" ;
     public  final  static  String EXTRA_DATA =
             "com.example.bluetooth.le.EXTRA_DATA" ;
 
     public  final  static  UUID UUID_HEART_RATE_MEASUREMENT =
             UUID.fromString(SampleGattAttributes.HEART_RATE_MEASUREMENT);
 
     // Various callback methods defined by the BLE API.
     private  final  BluetoothGattCallback mGattCallback =
             new  BluetoothGattCallback() {
         @Override
         public  void  onConnectionStateChange(BluetoothGatt gatt,  int  status,
                 int  newState) {
             String intentAction;
             if  (newState == BluetoothProfile.STATE_CONNECTED) {
                 intentAction = ACTION_GATT_CONNECTED;
                 mConnectionState = STATE_CONNECTED;
                 broadcastUpdate(intentAction);
                 Log.i(TAG,  "Connected to GATT server." );
                 Log.i(TAG,  "Attempting to start service discovery:"  +
                         mBluetoothGatt.discoverServices());
 
             else  if  (newState == BluetoothProfile.STATE_DISCONNECTED) {
                 intentAction = ACTION_GATT_DISCONNECTED;
                 mConnectionState = STATE_DISCONNECTED;
                 Log.i(TAG,  "Disconnected from GATT server." );
                 broadcastUpdate(intentAction);
             }
         }
 
         @Override
         // New services discovered
         public  void  onServicesDiscovered(BluetoothGatt gatt,  int  status) {
             if  (status == BluetoothGatt.GATT_SUCCESS) {
                 broadcastUpdate(ACTION_GATT_SERVICES_DISCOVERED);
             else  {
                 Log.w(TAG,  "onServicesDiscovered received: "  + status);
             }
         }
 
         @Override
         // Result of a characteristic read operation
         public  void  onCharacteristicRead(BluetoothGatt gatt,
                 BluetoothGattCharacteristic characteristic,
                 int  status) {
             if  (status == BluetoothGatt.GATT_SUCCESS) {
                 broadcastUpdate(ACTION_DATA_AVAILABLE, characteristic);
             }
         }
      ...
     };
...
}

    其中,discoverService方式是异步的,它的回调方法是上面代码中的onServiceDiscovered。

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
private  void  broadcastUpdate( final  String action) {
     final  Intent intent =  new  Intent(action);
     sendBroadcast(intent);
}
 
private  void  broadcastUpdate( final  String action,
                              final  BluetoothGattCharacteristic characteristic) {
     final  Intent intent =  new  Intent(action);
 
     // This is special handling for the Heart Rate Measurement profile. Data
     // parsing is carried out as per profile specifications.
     if  (UUID_HEART_RATE_MEASUREMENT.equals(characteristic.getUuid())) {
         int  flag = characteristic.getProperties();
         int  format = - 1 ;
         if  ((flag &  0x01 ) !=  0 ) {
             format = BluetoothGattCharacteristic.FORMAT_UINT16;
             Log.d(TAG,  "Heart rate format UINT16." );
         else  {
             format = BluetoothGattCharacteristic.FORMAT_UINT8;
             Log.d(TAG,  "Heart rate format UINT8." );
         }
         final  int  heartRate = characteristic.getIntValue(format,  1 );
         Log.d(TAG, String.format( "Received heart rate: %d" , heartRate));
         intent.putExtra(EXTRA_DATA, String.valueOf(heartRate));
     else  {
         // For all other profiles, writes the data formatted in HEX.
         final  byte [] data = characteristic.getValue();
         if  (data !=  null  && data.length >  0 ) {
             final  StringBuilder stringBuilder =  new  StringBuilder(data.length);
             for ( byte  byteChar : data)
                 stringBuilder.append(String.format( "%02X " , byteChar));
             intent.putExtra(EXTRA_DATA,  new  String(data) +  "\n"  +
                     stringBuilder.toString());
         }
     }
     sendBroadcast(intent);
}


?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
// Handles various events fired by the Service.
// ACTION_GATT_CONNECTED: connected to a GATT server.
// ACTION_GATT_DISCONNECTED: disconnected from a GATT server.
// ACTION_GATT_SERVICES_DISCOVERED: discovered GATT services.
// ACTION_DATA_AVAILABLE: received data from the device. This can be a
// result of read or notification operations.
private  final  BroadcastReceiver mGattUpdateReceiver =  new  BroadcastReceiver() {
     @Override
     public  void  onReceive(Context context, Intent intent) {
         final  String action = intent.getAction();
         if  (BluetoothLeService.ACTION_GATT_CONNECTED.equals(action)) {
             mConnected =  true ;
             updateConnectionState(R.string.connected);
             invalidateOptionsMenu();
         else  if  (BluetoothLeService.ACTION_GATT_DISCONNECTED.equals(action)) {
             mConnected =  false ;
             updateConnectionState(R.string.disconnected);
             invalidateOptionsMenu();
             clearUI();
         else  if  (BluetoothLeService.
                 ACTION_GATT_SERVICES_DISCOVERED.equals(action)) {
             // Show all the supported services and characteristics on the
             // user interface.
             displayGattServices(mBluetoothLeService.getSupportedGattServices());
         else  if  (BluetoothLeService.ACTION_DATA_AVAILABLE.equals(action)) {
             displayData(intent.getStringExtra(BluetoothLeService.EXTRA_DATA));
         }
     }
};


4、读BLE属性

    一旦获取到GATT的Services,就可以读写他们的属性了,实例如下:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
public  class  DeviceControlActivity  extends  Activity {
     ...
     // Demonstrates how to iterate through the supported GATT
     // Services/Characteristics.
     // In this sample, we populate the data structure that is bound to the
     // ExpandableListView on the UI.
     private  void  displayGattServices(List<BluetoothGattService> gattServices) {
         if  (gattServices ==  null return ;
         String uuid =  null ;
         String unknownServiceString = getResources().
                 getString(R.string.unknown_service);
         String unknownCharaString = getResources().
                 getString(R.string.unknown_characteristic);
         ArrayList<HashMap<String, String>> gattServiceData =
                 new  ArrayList<HashMap<String, String>>();
         ArrayList<ArrayList<HashMap<String, String>>> gattCharacteristicData
                 new  ArrayList<ArrayList<HashMap<String, String>>>();
         mGattCharacteristics =
                 new  ArrayList<ArrayList<BluetoothGattCharacteristic>>();
 
         // Loops through available GATT Services.
         for  (BluetoothGattService gattService : gattServices) {
             HashMap<String, String> currentServiceData =
                     new  HashMap<String, String>();
             uuid = gattService.getUuid().toString();
             currentServiceData.put(
                     LIST_NAME, SampleGattAttributes.
                             lookup(uuid, unknownServiceString));
             currentServiceData.put(LIST_UUID, uuid);
             gattServiceData.add(currentServiceData);
 
             ArrayList<HashMap<String, String>> gattCharacteristicGroupData =
                     new  ArrayList<HashMap<String, String>>();
             List<BluetoothGattCharacteristic> gattCharacteristics =
                     gattService.getCharacteristics();
             ArrayList<BluetoothGattCharacteristic> charas =
                     new  ArrayList<BluetoothGattCharacteristic>();
            // Loops through available Characteristics.
             for  (BluetoothGattCharacteristic gattCharacteristic :
                     gattCharacteristics) {
                 charas.add(gattCharacteristic);
                 HashMap<String, String> currentCharaData =
                         new  HashMap<String, String>();
                 uuid = gattCharacteristic.getUuid().toString();
                 currentCharaData.put(
                         LIST_NAME, SampleGattAttributes.lookup(uuid,
                                 unknownCharaString));
                 currentCharaData.put(LIST_UUID, uuid);
                 gattCharacteristicGroupData.add(currentCharaData);
             }
             mGattCharacteristics.add(charas);
             gattCharacteristicData.add(gattCharacteristicGroupData);
          }
     ...
     }
...
}

  在获取Service的时候,每个蓝牙设备都会有两个默认的Service,它们和对应的UUID分别如下:

Bluetooth Generic Access Profile    {00001800-0000-1000-8000-00805f9b34fb}

Bluetooth Generic Attribute Profile {00001801-0000-1000-8000-00805F9B34FB}


5、收到GATT通知

   如果设备主动给手机发信息,则可以通过notification的方式,这种方式不用手机去轮询地读设备上的数据。手机可以用如下方式给设备设置notification功能。

?
1
2
3
4
5
6
7
8
9
10
private  BluetoothGatt mBluetoothGatt;
BluetoothGattCharacteristic characteristic;
boolean  enabled;
...
mBluetoothGatt.setCharacteristicNotification(characteristic, enabled);
...
BluetoothGattDescriptor descriptor = characteristic.getDescriptor(
         UUID.fromString(SampleGattAttributes.CLIENT_CHARACTERISTIC_CONFIG));
descriptor.setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE);
mBluetoothGatt.writeDescriptor(descriptor);

    如果notificaiton方式对于某个Characteristic是enable的,那么当设备上的这个Characteristic改变时,手机上的onCharacteristicChanged() 回调就会被促发。如下所示:

?
1
2
3
4
5
6
@Override
// Characteristic notification
public  void  onCharacteristicChanged(BluetoothGatt gatt,
         BluetoothGattCharacteristic characteristic) {
     broadcastUpdate(ACTION_DATA_AVAILABLE, characteristic);
}

6、关闭客户端蓝牙

?
1
2
3
4
5
6
7
public  void  close() {
     if  (mBluetoothGatt ==  null ) {
         return ;
     }
     mBluetoothGatt.close();
     mBluetoothGatt =  null ;
}

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值