整理翻译自: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);
}
你可以通过
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 =注意:扫描的过程中可以扫到BLE设备,同时也可以扫描到经典蓝牙设备
newBluetoothAdapter.LeScanCallback(){
@Override
publicvoid onLeScan(finalBluetoothDevice device,int rssi,
byte[] scanRecord){
runOnUiThread(newRunnable(){
@Override
publicvoid run(){
mLeDeviceListAdapter.addDevice(device);
mLeDeviceListAdapter.notifyDataSetChanged();
}
});
}
};
连接一个 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{接收 GATT通知
...
// 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);
}
...
}
...
}
当从设备上的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;
}