蓝牙 BLE API 使用

整理翻译自:https://developer.android.com/guide/topics/connectivity/bluetooth-le.html


首次翻译,内容错误请指正,不明白的地方可以参考原文



在你的应用通过BLE通信之前,你需要检查你的设备是否支持BLE,如果支持,确保已经使能蓝牙功能,如果不支持BLE,那你需要在你应用里优雅的不要使用蓝牙的一些特性
如果你的设备支持BLE,但是被禁用了,你可以在你的程序里使能蓝牙,这项操作可以通过BluetoothAdapter,分两步来完成。

使能蓝牙

1.  获取BluetoothAdapter
BlueAdapter操作自身蓝牙,整个系统中只有一个Bluetooth Adapter
// Initializes Bluetooth adapter.
privateBluetoothAdapter mBluetoothAdapter;

finalBluetoothManager bluetoothManager =
       
(BluetoothManager) getSystemService(Context.BLUETOOTH_SERVICE);
mBluetoothAdapter
= bluetoothManager.getAdapter();


2. 使能蓝牙
// 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 =newIntent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
    startActivityForResult
(enableBtIntent, REQUEST_ENABLE_BT);
}
扫描BLE 设备

你可以通过  startLeScan()   方法查照附近的BLE设备,这个方法需要传入一个BluetoothAdapter.LeScanCallback  的参数,这个参数是一个回调参数,你需要实现其中的回调。扫描操作对电量比较敏感,你需要遵循以下原则:
1. 一旦找到设备就停止扫描
2. 不要在循环中调用扫描,设置一个扫描时间限值,如果先前扫到的设备可能暂时不在扫描的有效距离之内,所以继续扫描有可能耗干电量。

扫描过程:
/**
 * Activity for scanning and displaying available BLE devices.
 */

publicclassDeviceScanActivityextendsListActivity{

   
privateBluetoothAdapter mBluetoothAdapter;
   
privateboolean mScanning;
   
privateHandler mHandler;

   
// Stops scanning after 10 seconds.
   
privatestaticfinallong SCAN_PERIOD =10000;
   
...
   
privatevoid scanLeDevice(finalboolean enable){
       
if(enable){
           
// Stops scanning after a pre-defined scan period.
            mHandler
.postDelayed(newRunnable(){
               
@Override
               
publicvoid run(){
                    mScanning
=false;
                    mBluetoothAdapter
.stopLeScan(mLeScanCallback);
               
}
           
}, SCAN_PERIOD);

            mScanning
=true;
            mBluetoothAdapter
.startLeScan(mLeScanCallback);
       
}else{
            mScanning
=false;
            mBluetoothAdapter
.stopLeScan(mLeScanCallback);
       
}
       
...
   
}
...
}
如果你只想扫描指定类型的外围设备,你可以调用方法 startLeScan(UUID[], BluetoothAdapter.LeScanCallback),替代上述扫描。这个方法提供了一个UUID 数组 作为程序支持的GATT Server服务的标志。
扫描回调函数
privateBluetoothAdapter.LeScanCallback mLeScanCallback =
       
newBluetoothAdapter.LeScanCallback(){
   
@Override
   
publicvoid onLeScan(finalBluetoothDevice device,int rssi,
           
byte[] scanRecord){
        runOnUiThread
(newRunnable(){
           
@Override
           
publicvoid run(){
               mLeDeviceListAdapter
.addDevice(device);
               mLeDeviceListAdapter
.notifyDataSetChanged();
           
}
       
});
   
}
};
注意:扫描的过程中可以扫到BLE设备,同时也可以扫描到经典蓝牙设备


连接一个 GATT Server

与BLE设备通信的首要步骤就是去连接他,准确的说应该是连接他的GATT Server。您可以调用 connectGatt()  方法 连接一个BLE设备的GATT Server。这个方法有三个形参,Context 对象, 自动连接标志(autoConnect 标志这个设备蓝牙可用时是否自动连接这个BLE设备) 和 BluetoothGattCallback  回调的引用,如下:
mBluetoothGatt = device.connectGatt(this,false, mGattCallback);
连接作为主设备的GATT Server,返回了一个 BluetoothGatt  句柄,通过这个句柄你可以进行一些GATT Client的一些操作。调用者就是GATT Client,例如Android应用程序。回调函数BluetoothGattCallback 主要是用来向Client传递一些结果,例如连接状态,以及进一步的GATT Client的操作

在这个例子中,BLE 应用程序提供了一个Activity 来连接,显示数据  显示设备支持的 GATT services  和 characteristics 。Activity 获取用户的输入,与一个被称为BluetoothLeService 的Service进行通信。这个服务通过Android BLE API 与BLE设备进行通信

// A service that interacts with the BLE device via the Android BLE API.
publicclassBluetoothLeServiceextendsService{
   
privatefinalstaticString TAG =BluetoothLeService.class.getSimpleName();

   
privateBluetoothManager mBluetoothManager;
   
privateBluetoothAdapter mBluetoothAdapter;
   
privateString mBluetoothDeviceAddress;
   
privateBluetoothGatt mBluetoothGatt;
   
privateint mConnectionState = STATE_DISCONNECTED;

   
privatestaticfinalint STATE_DISCONNECTED =0;
   
privatestaticfinalint STATE_CONNECTING =1;
   
privatestaticfinalint STATE_CONNECTED =2;

   
publicfinalstaticString ACTION_GATT_CONNECTED =
           
"com.example.bluetooth.le.ACTION_GATT_CONNECTED";
   
publicfinalstaticString ACTION_GATT_DISCONNECTED =
           
"com.example.bluetooth.le.ACTION_GATT_DISCONNECTED";
   
publicfinalstaticString ACTION_GATT_SERVICES_DISCOVERED =
           
"com.example.bluetooth.le.ACTION_GATT_SERVICES_DISCOVERED";
   
publicfinalstaticString ACTION_DATA_AVAILABLE =
           
"com.example.bluetooth.le.ACTION_DATA_AVAILABLE";
   
publicfinalstaticString EXTRA_DATA =
           
"com.example.bluetooth.le.EXTRA_DATA";

   
publicfinalstatic UUID UUID_HEART_RATE_MEASUREMENT =
            UUID
.fromString(SampleGattAttributes.HEART_RATE_MEASUREMENT);

   
// Various callback methods defined by the BLE API.
   
privatefinalBluetoothGattCallback mGattCallback =
           
newBluetoothGattCallback(){
       
@Override
       
publicvoid 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());

           
}elseif(newState ==BluetoothProfile.STATE_DISCONNECTED){
                intentAction
= ACTION_GATT_DISCONNECTED;
                mConnectionState
= STATE_DISCONNECTED;
               
Log.i(TAG,"Disconnected from GATT server.");
                broadcastUpdate
(intentAction);
           
}
       
}

       
@Override
       
// New services discovered
       
publicvoid 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
       
publicvoid onCharacteristicRead(BluetoothGatt gatt,
               
BluetoothGattCharacteristic characteristic,
               
int status){
           
if(status ==BluetoothGatt.GATT_SUCCESS){
                broadcastUpdate
(ACTION_DATA_AVAILABLE, characteristic);
           
}
       
}
     
...
   
};
...
}

当回调被触发后,会调用一个合适的broadcastUpdate() 方法传这个动作
privatevoid broadcastUpdate(finalString action){
   
finalIntent intent =newIntent(action);
    sendBroadcast
(intent);
}

privatevoid broadcastUpdate(finalString action,
                             
finalBluetoothGattCharacteristic characteristic){
   
finalIntent intent =newIntent(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.");
       
}
       
finalint 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.
       
finalbyte[] data = characteristic.getValue();
       
if(data !=null&& data.length >0){
           
finalStringBuilder stringBuilder =newStringBuilder(data.length);
           
for(byte byteChar : data)
                stringBuilder
.append(String.format("%02X ", byteChar));
            intent
.putExtra(EXTRA_DATA,newString(data)+"\n"+
                    stringBuilder
.toString());
       
}
   
}
    sendBroadcast
(intent);
}

回到 主Activity(DeviceControlActivity),这些事件主要通过广播 BroadcastReceiver 来传递

// 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.
privatefinalBroadcastReceiver mGattUpdateReceiver =newBroadcastReceiver(){
   
@Override
   
publicvoid onReceive(Context context,Intent intent){
       
finalString action = intent.getAction();
       
if(BluetoothLeService.ACTION_GATT_CONNECTED.equals(action)){
            mConnected
=true;
            updateConnectionState
(R.string.connected);
            invalidateOptionsMenu
();
       
}elseif(BluetoothLeService.ACTION_GATT_DISCONNECTED.equals(action)){
            mConnected
=false;
            updateConnectionState
(R.string.disconnected);
            invalidateOptionsMenu
();
            clearUI
();
       
}elseif(BluetoothLeService.
                ACTION_GATT_SERVICES_DISCOVERED
.equals(action)){
           
// Show all the supported services and characteristics on the
           
// user interface.
            displayGattServices
(mBluetoothLeService.getSupportedGattServices());
       
}elseif(BluetoothLeService.ACTION_DATA_AVAILABLE.equals(action)){
            displayData
(intent.getStringExtra(BluetoothLeService.EXTRA_DATA));
       
}
   
}
};

读取BLE的属性

一旦您的应用连接到BLE的GATT Server 并获取了对应的services,就可以对属性进行读写了,例如下述例子就是遍历Service和Characteristic,并展示到UI上

publicclassDeviceControlActivityextendsActivity{
   
...
   
// 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.
   
privatevoid 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 =
               
newArrayList<HashMap<String,String>>();
       
ArrayList<ArrayList<HashMap<String,String>>> gattCharacteristicData
               
=newArrayList<ArrayList<HashMap<String,String>>>();
        mGattCharacteristics
=
               
newArrayList<ArrayList<BluetoothGattCharacteristic>>();

       
// Loops through available GATT Services.
       
for(BluetoothGattService gattService : gattServices){
           
HashMap<String,String> currentServiceData =
                   
newHashMap<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 =
                   
newArrayList<HashMap<String,String>>();
           
List<BluetoothGattCharacteristic> gattCharacteristics =
                    gattService
.getCharacteristics();
           
ArrayList<BluetoothGattCharacteristic> charas =
                   
newArrayList<BluetoothGattCharacteristic>();
           
// Loops through available Characteristics.
           
for(BluetoothGattCharacteristic gattCharacteristic :
                    gattCharacteristics
){
                charas
.add(gattCharacteristic);
               
HashMap<String,String> currentCharaData =
                       
newHashMap<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);
         
}
   
...
   
}
...
}
接收 GATT通知

当从设备上的Characteristic 变化时,BLE设备能够接收到这个变化,这个功能对于一个BLE app来说是很常见的。下述例子就是通过调用 setCharacteristicNotification() 方法针对特定的Characteristic设置通知响应

privateBluetoothGatt 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);

如果一个Characteristic被设置上了通知,则远端的Characteristic变化时, onCharacteristicChanged() 回调就会被调用

@Override
// Characteristic notification
publicvoid onCharacteristicChanged(BluetoothGatt gatt,
       
BluetoothGattCharacteristic characteristic){
    broadcastUpdate
(ACTION_DATA_AVAILABLE, characteristic);
}

关闭蓝牙连接

publicvoid close(){
   
if(mBluetoothGatt ==null){
       
return;
   
}
    mBluetoothGatt
.close();
    mBluetoothGatt
=null;
}




  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值