android ble 标准服务,Android BLE开发入门

BLE 即 Bluetooth Low Energy,蓝牙低功耗技术,是蓝牙4.0引入的新技术。现在越来越多的智能设备使用了BLE,像满大街的智能手环。

Android在4.3(API 18)中引进了对BLE central role的支持,同时提供API供App来扫描设备、查询服务、读写特征值等。

关键术语和概念

Generic Attribute Profile (GATT)—GATT概述(profile)是一个通用的通过BLE连接来发送和接受短的被称为“属性”的数据的规范。所有现在的低功耗应用概述(profile)都基于GATT。

蓝牙标准协会为低功耗设备定义了很多 profiles。概述(profile)是设备在特定应用场景下如何工作的规范。注意设备能实现不止一种概述,例如,一个设备可以包含心律监测和电池电量检测。

Attribute Protocol (ATT)—GATT建立在属性协议(ATT)上。通常他们一起被叫做GATT/ATT。ATT针对在BLE设备上运行做了优化。为了这个目的,它使用尽可能少的字节。每个属性通过一个标准的128位格式的字符串ID作为唯一标识信息的通用唯一识别码(Universally Unique Identifier 即UUID)来唯一的标识。属性被ATT格式化为特征和服务来传输。

Characteristic—一个特征包含一个单一的值(value)和0-n个描述信息块(descriptor)来描述特征的值。特征可以被认为是一种类型(type),类似一个类(class)。

Descriptor—描述信息块定义了特征值(characteristic value)的属性,一个描述信息块可能指定一个可读的描述,一个特征值可接受的范围,或者指定一个特征值的计量单位。

Service—服务是特征的集合。例如,你可以有个叫做“心率检测器”且包含“心律测量方式”特征的服务。你可以在bluetooth.org上找到一份现有的基于GATT的概述(profile)和服务列表。

角色和职责

中心 vs. 外围。你必须同时有这两种设备才能建立他们之间的连接。两个中心设备或者两个外围设备都不能建立连接。

GATT服务端 vs. GATT客户端。这取决于他们之间是怎么交流的。

BLE权限

检查设备是否支持BLE:

// 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

整个设备只有一个BluetoothAdapter

获取BluetoothAdapter

整个设备只有一个BluetoothAdapter,它代表了设备自己的蓝牙适配器,应用通过这个对象与设备进行交互。

// Initializes Bluetooth adapter.

final BluetoothManager bluetoothManager =

(BluetoothManager) getSystemService(Context.BLUETOOTH_SERVICE);

mBluetoothAdapter = bluetoothManager.getAdapter();

开启蓝牙

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设备

要查找BLE设备,需要使用startLeScan()方法,它的参数是BluetoothAdapter.LeScanCallback。你必须实现这个callbak,因为这是扫描结果返回的方式。蓝牙扫描是一个电量敏感的操作,你需要遵守以下指导:

只要找到了要找的设备,停止扫描

不要在循环里扫描,设置一个扫描时间。之前的设备可能已经移动到范围外,持续扫描会耗尽电量。

如何开始和停止扫描:

/**

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

}

...

}

...

}

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设备或传统蓝牙设备二者之一,就像 Bluetooth描述的那样。你不能同时扫描两种设备。

连接GATT服务端

与BLE设备交互第一步就是要连接它—更确切的说,是连接到设备上的GATT服务端。要连接到BLE设备的GATT服务端,使用[connectGatt()](https://developer.android.com/reference/android/bluetooth/BluetoothDevice.html#connectGatt(android.content.Context, boolean, android.bluetooth.BluetoothGattCallback))方法。这个方法需要三个参数,一个Context对象,autoConnect(一个指示是否自动连接到BLE设备--当它一旦可用的时候--的布尔值),和一个 BluetoothGattCallback的引用:

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

返回的BluetoothGatt实例之后你可以用它来管理GATT客户端的操作。调用者(Android app)是GATT客户端。 BluetoothGattCallback

用于传递结果给用户,例如连接状态,以及任何进一步GATT客户端操作。

在这个例子中,这个BLE APP提供了一个activity(DeviceControlActivity)来连接,显示数据,显示该设备支持的GATT services和characteristics。根据用户的输入,这个activity与一个叫做BluetoothLeService的 Service通信,它通过Android BLE API实现与BLE设备交互:

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

}

}

...

};

...

}

当一个特定的回调被触发的时候,它会调用相应的broadcastUpdate()辅助方法并且传递给它一个action。注意在该部分中的数据解析按照蓝牙心率测量配置文件规格profile specifications进行。

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

}

返回到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.

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

}

}

};

读取BLE属性

一旦你的Android app已经连接到GATT服务端连接且发现services后,就可以读、写那些支持的属性。例如,这段代码遍历服务端的services和 characteristics,并且将它们显示在UI上。

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 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> gattServiceData =

new ArrayList>();

ArrayList>> gattCharacteristicData

= new ArrayList>>();

mGattCharacteristics =

new ArrayList>();

// Loops through available GATT Services.

for (BluetoothGattService gattService : gattServices) {

HashMap currentServiceData =

new HashMap();

uuid = gattService.getUuid().toString();

currentServiceData.put(

LIST_NAME, SampleGattAttributes.

lookup(uuid, unknownServiceString));

currentServiceData.put(LIST_UUID, uuid);

gattServiceData.add(currentServiceData);

ArrayList> gattCharacteristicGroupData =

new ArrayList>();

List gattCharacteristics =

gattService.getCharacteristics();

ArrayList charas =

new ArrayList();

// Loops through available Characteristics.

for (BluetoothGattCharacteristic gattCharacteristic :

gattCharacteristics) {

charas.add(gattCharacteristic);

HashMap currentCharaData =

new HashMap();

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通知

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

@Override

// Characteristic notification

public void onCharacteristicChanged(BluetoothGatt gatt,

BluetoothGattCharacteristic characteristic) {

broadcastUpdate(ACTION_DATA_AVAILABLE, characteristic);

}

关闭客户端App

一旦你的app完成了对BLE设备的使用,需要调用close()以便系统能适当地释放资源:

public void close() {

if (mBluetoothGatt == null) {

return;

}

mBluetoothGatt.close();

mBluetoothGatt = null;

}

你可能感兴趣的:(android,ble,标准服务)