1500字范文,内容丰富有趣,写作好帮手!
1500字范文 > 安卓蓝牙BLE设备开发

安卓蓝牙BLE设备开发

时间:2023-04-28 03:14:53

相关推荐

安卓蓝牙BLE设备开发

 前段时间做了一个有关于安卓蓝牙BLE设备的开发项目,主要的功能包括了搜索蓝牙ble设备和ble设备的数据读写等等,本篇博客用于记录安卓蓝牙ble设备的通信的细节。

 其实关于BLE设备的通信在API中已经讲地比较清楚了,这里只是做一个总结,如果要进行BLE设备的开发,首先可以阅读API.

BLE有关API

BLE设备的定义和特点

 BLE-维基百科

 对于BLE设备的定义,我们只需要看维基百科的说明就好了,简单的说明一下BLE的原理。

 首先对于BLE硬件来说,它能够决定什么时候去发出请求和外接的设备进行连接,安卓官方称之为advertisement,市面上见到的BLE设备,比如蓝牙音响,一打开就能够被手机检测到,原因是其硬件上设定了会一直发出advertisement,直到连接上设备为止。但是实际上BLE设备本身是可以控制自己要不要发出advertisement的,如果BLE设备没有发出这个advertisement,那么我们是搜索不到它的。

 对于BLE设备还有一个坑,那就是BLE设备只能连接一个设备,也就是说,一旦当BLE设备连接了一台手机后,其他的手机上就无法搜索到这台BLE设备了。我猜测应该是BLE设备连接之后就不会发出advertisement了,导致其他设备搜索不到。

安卓上进行BLE开发的步骤

1、AndroidManifest中声明权限

对于BLE开发我们需要声明两个权限

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

 其中第一个权限是只要我们进行与蓝牙相关的操作都需要声明的,比如你的APP只是希望能够被别人检测到,然后通过蓝牙交换数据,就需要声明这一个。

 第二个权限是如果你的设备需要主动搜索蓝牙设备,或者是对蓝牙的某些设置进行更改就需要这个权限,一般来说我们在进行BLE开发的时候会同时声明这两个权限。

 因为不是所有的手机上都有蓝牙BLE功能,需要API18(Android4.3)以上才能进行BLE的开发,所以我们可以声明features过滤掉不满足要求的设备

<uses-feature android:name="android.hardware.bluetooth_le" android:required="true"/>

2、获取BluetoothAdapter

 BluetoothAdapter是用来获取可以连接的BLE设备列表的一个类,想要和BLE设备通信当然需要先能够搜索到可用的BLE设备。安卓获取BluetoothAdapter的实例的方式很简单。首先通过调用系统服务获得BluetoothManager的实例,然后调用BluetoothManager的getAdapter()方法就可以了,代码如下:

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

 当然,这里的mBluetoothAdapter我们最好保存为类的数据成员,因为之后我们还会用到它去获取当前可连接的BLE设备的列表。

3、开启蓝牙

 得到了BluetoothAdapter的实例之后我们就可以用其中的方法去搜索设备了,但是在此之前,我们首先要打开手机上的蓝牙功能,这个可以通过用户自己找到蓝牙去打开,但是这样很明显会影响用户体验。没有人想在使用APP的时候还需要去找到系统的蓝牙并打开。安卓API为我们提供了在APP中开启系统蓝牙的方法:即调用BluetoothAdapter中的enable()方法。

 为了严谨,我们需要判断上一步获得的mBluetoothAdapter是否为空(只有当设备不支持BLE的时候这个实例才会为null)

public void enable() {if(mBluetoothAdapter == null || ! mBluetoothAdapter.isEnabled()) {mBluetoothAdapter.enable();}}

这里我们调用了mBluetoothAdapter.isEnabled(),避免重复打开蓝牙。

4、搜索BLE设备

 打开蓝牙之后,我们终于可以搜索BLE设备了。这个部分需要注意的问题主要是搜索时间的问题,因为搜索BLE设备是一个很费电的过程,所以搜索的时间不应该设置太长。在开始搜索的同时,我们应该设置一个Handler,然后调用Handler的postDelayed(Runnable r)方法去设定在搜索一定时间后停止搜索。

 将搜索BLE设备的过程独立写成一个方法,这样如果需要多次搜索的话只需要再次调用此方法即可。另外应该设立一个标志位mScanning,在调用搜索方法的时候应该判断标志位,如果现在正在搜索就不应该再次进行搜索。

public void scanLeDevice(boolean enable) {if(enable) {mHandler.postDelayed(new Runnable() {@Overridepublic void run() {mScanning = false;mBluetoothAdapter.stopLeScan(mLeScanCallback);mBLEListActivity.setTextInfo();}}, SCAN_PERIOD);mScanning = true;mBluetoothAdapter.startLeScan(mLeScanCallback);} else {mScanning = false;mBluetoothAdapter.stopLeScan(mLeScanCallback);}}

 这段方法的核心主要是两个方法:mBluetoothAdapter.startLeScan(mLeScanCallback)和

mBluetoothAdapter.stopLeScan(mLeScanCallback)。分别对应开始BLE设备的搜索和停止BLE设备的搜索。需要注意的是这两个方法只针对于BLE设备,对于普通的蓝牙设备,调用这个方法是搜索不到的。

 但是你可能要问了,我们怎么才能得到搜索的结果呢?请注意,在这两个方法中都有一个共同的参数mLeScanCallback,这个mLeScanCallback就是我们获取搜索结果的载体。

 让我们一起来看看mLeScanCallback的定义

private BluetoothAdapter.LeScanCallback mLeScanCallback =new BluetoothAdapter.LeScanCallback() {@Overridepublic void onLeScan(final BluetoothDevice device, int rssi, byte[] scanRecord) {if(!mBLEListActivity.mBLEArrayList.contains(device)) {mBLEListActivity.addBluetoothItem(device);mBLEListActivity.runOnUiThread(new Runnable() {@Overridepublic void run() { mBLEListActivity.mBLEAdapter.notifyDataSetChanged();mBLEListActivity.mListView.setAdapter(mBLEListActivity.mBLEAdapter);} });}}};

 可以看到mLeScanCallback实际上是一个BluetoothAdapter.LeScanCallback的实例。LeScanCallback中包含了一个回调方法onLeScan(),我们通过重写这个方法去进行一系列操作。

 到这里我们基本上就可以理解了,实际上BLE的搜索的实现是通过回调方法完成的,当检测到BLE设备的时候,就调用一次onLeScan()方法,然后我们应该在里面去保存搜索到的device,比如放在一个List中。一般的处理方法是将搜索到的device放在ListView中进行显示,然后响应用户的点击进行连接。这不属于BLE开发的范畴,所以暂不赘述。

5、与指定BLE设备进行通信

 通过第4步我们能够搜索到我们想要的device,现在应该开始通信了。通信的第一步就是和BLE设备建立连接。安卓用BluetoothGatt类来管理这种连接,首先我们需要获得一个BluetoothGatt实例,只需要调用BluetoothDevice的connectGatt()方法即可。

if(mBLEDevice != null) {Log.d(TAG, "建立连接");mBluetoothGatt = mBLEDevice.connectGatt(mBLEService, true, mGattCallback);} else {Log.e(TAG, "没找到特定的BLE设备,连接无法建立");}

这里有三个参数,重点说一下第三个参数mGattCallback,这时连接过程中最为重要的一个参数,其定义如下(此部分为安卓官方的代码)

// 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() {@Overridepublic 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 discoveredpublic 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 operationpublic void onCharacteristicRead(BluetoothGatt gatt,BluetoothGattCharacteristic characteristic,int status) {if (status == BluetoothGatt.GATT_SUCCESS) {broadcastUpdate(ACTION_DATA_AVAILABLE, characteristic);}}...};...}

 代码比较繁杂,不需要仔细看。我们只需要知道mGattCallback是一个BluetoothGattCallback的实例,里面有很多的回调方法就够了。下面我会说到一些比较重要的回调方法,这些回调方法主要是用于检测通信过程中状态的改变的。

 连接的过程中我们传入了mGattCallback,然后mGattCallback中的回调方法是需要我们自己去重写的,在对应不同状态的时候去进行不同的动作。比较重要的方法有以下几个:

onConnectionStateChange()

public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) {super.onConnectionStateChange(gatt, status, newState);if(newState == BluetoothProfile.STATE_CONNECTED) {mBluetoothGatt = gatt;if(mBluetoothGatt != null) {mBluetoothGatt.discoverServices();} else {Log.d(TAG, "mBluetoothGatt为空");}} else if(newState == BluetoothProfile.STATE_DISCONNECTED) {mActivity.runOnUiThread(new Runnable() {@Overridepublic void run() {Toast.makeText(mActivity, "连接断开,请检查设备", Toast.LENGTH_LONG).show();} });}}

 这个方法主要检测的是设备的连接状态的改变,每当设备连接状态发生改变的时候,即从连接到断开或者从断开到连接的时候都会调用此方法。我们应该判断newState的值,然后在连接成功的时候去调用mBluetoothGatt.discoverServices()(之后我会说明这个方法的作用),在连接断开的时候进行清理,并且告知用户连接断开了。

onServiceDiscovered()

public void onServicesDiscovered(BluetoothGatt gatt, int status) {super.onServicesDiscovered(gatt, status);if(status == BluetoothGatt.GATT_SUCCESS) {mActivity.runOnUiThread(new Runnable() {@Overridepublic void run() {Toast.makeText(mActivity, "已连接", Toast.LENGTH_LONG).show();} });try {getBtgService();getBtgCharacteristic();mBLEAudioTrack.play();if(mReadCharacteristic != null) {mBluetoothGatt.setCharacteristicNotification(mReadCharacteristic, true);}}catch(Exception e) {e.printStackTrace();}} else {Log.e("TAG", "连接失败");}}

 刚才我们提到了在连接成功时候需要调用mBluetoothGatt.discoverServices(),为什么呢?这就涉及到蓝牙BLE通信上的一个设计,蓝牙的数据传输并不是一位一位传过来的,而是通过Characteristic进行封装后传过来的,一个Characteristic中包装了一组数据。在Characteristic上层,还有Service,封装了多个Charateristic。在实际的通信中,我们需要首先获取BluetoothGattService,然后通过BluetoothGattService的实例去获取BluetoothGattCharacteristic。

 而在获取BluetoothGattService之前,我们需要先获取服务,这个获取服务相当于是告诉蓝牙设备我要开始进行数据交换了,请你准备好,mBluetoothGatt.discoverServices()就是完成这个功能的。当找到了服务之后,安卓就会自动调用上面的onServicesDiscovered()方法,然后我们就可以在里面去获取BluetoothGattService和BluetoothGattCharacteristic。

 怎么获取Service和Characteristic呢?

 这里用到了UUID,关于UUID的基本知识请参照维基百科-UUID

在Java中UUID通过java.util.UUID.fromString(String str)来产生的,在这一部分需要和BLE硬件相适应,确定BLE设备上发送数据和接收数据的Service和Characteristic的UUID号。以下是我所使用的BLE设备的Service和Characteristic的获取方式

首先用UUID号得到UUID对象

private static final String SERVICE_NAME = "0000FFF0-0000-1000-8000-00805F9B34FB";private static final UUID SERVICE_UUID = UUID.fromString(SERVICE_NAME);private static final String CHARC_NAME = "0000FFF6-0000-1000-8000-00805F9B34FB";private static final UUID CHARC_UUID = UUID.fromString(CHARC_NAME);

获取Service实例

public void getBtgService() {mBluetoothGattService = mBluetoothGatt.getService(SERVICE_UUID);Log.i(TAG, "get service"+mBluetoothGattService.toString());}

获取Characteristic实例

public void getBtgCharacteristic() {mReadCharacteristic = mBluetoothGattService.getCharacteristic(CHARC_UUID);mWriteCharacteristic = mBluetoothGattService.getCharacteristic(CHARC_UUID);Log.i(TAG, "get characteristic"+mReadCharacteristic.toString());List<BluetoothGattDescriptor> descriptors = mReadCharacteristic.getDescriptors();for (BluetoothGattDescriptor bgp : descriptors) {Log.i(TAG, "setCharacteristicNotification: " + bgp);bgp.setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE);mBluetoothGatt.writeDescriptor(bgp);}}

 在获取mReadCharacteristic的时候需要注意,我们通过mReadCharacteristic.getDescriptors()获取了它的描述符,然后对下面的所有描述符都设置了属性,这样做的原因是因为之后我们需要回调方法onCharacteristicChanged(),如果不这样设置,那么当读数据成功后不会进入onCharacteristicChanged()这个回调方法中,这样我们就无法获得之后的数据了。所以为了让读操作能够一直持续,我们需要设置其Descriptor。

 注意,在获取了Service和Characteristic之后实际上我们就可以开始进行数据通信了,我在这里调用了方法

if(mReadCharacteristic != null) {mBluetoothGatt.setCharacteristicNotification(mReadCharacteristic, true);}

通过这个方法,现在就能监听安卓设备是否收到BLE设备发来的数据,如果收到了发来的数据,会调用方法onCharacteristicRead(),下面介绍这个方法。

onCharacteristicRead()

public void onCharacteristicRead(BluetoothGatt gatt,BluetoothGattCharacteristic characteristic,int status) {if (status == BluetoothGatt.GATT_SUCCESS) {readCharacteristicValue(mReadCharacteristic);mBluetoothGatt.setCharacteristicNotification(mReadCharacteristic, true);}}

 onCharacteristicRead()这个方法会在接收数据的时候被调用,在这里面我们可以去得到BLE设备发过来的数据,通过characteristic.getValue()即可。这里对数据的操作被封装在readCharacteristicValue(mReadCharacteristic)中了。

 注意,在这个方法中,一定要再次调用mBluetoothGatt.setCharacteristicNotification(mReadCharacteristic, true);这样才能保证下一次接收到数据时又能进入此回调方法。这样就能保证连续地接收BLE设备的数据了。

onCharacteristicWrite()

public void onCharacteristicWrite(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic,int status) {switch(status) {case BluetoothGatt.GATT_SUCCESS:Log.d(TAG, "write data success");break;case BluetoothGatt.GATT_FAILURE:Log.d(TAG, "write data failed");case BluetoothGatt.GATT_WRITE_NOT_PERMITTED:Log.d(TAG, "write not permitted");}super.onCharacteristicWrite(gatt, characteristic, status);}

如果需要向BLE设备写数据其实是与读数据相似的处理,就不赘述了。

6、结束通信时,释放资源

 这一部分比较简单,只需要调用如下的close()方法即可

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

结语

 这篇博客作为我博客生涯的一个开始,以后我会在学习到新的知识的时候尽量多地去总结,会保持博客的更新。

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。