Android 蓝牙4.0BLE开发实现对蓝牙的写入数据和读取数据
2017-01-16 20:54
926 查看
代码基本上都是官方的demo,只是通过修改获得自己想要的结果,下面就简单介绍一下自己的理解。
一、扫描BLE设备activity
检查该设备是否支持BLE设备,谷歌在Android4.3才开始支持BLE设备(晕死,很长一段时间都没有一台4.3的设备,看着程序修改了也不能测试!)。
初始化获得一个bluetoothManager,并检测设备是否支持蓝牙
二、蓝牙控制的服务BluetoothLeService
在这个服务里面有一个很重要的回调函数BluetoothGattCallback(),蓝牙的数据读取和状态改变都会回调这个函数。
还有几个重要的函数比如readCharacteristic(BluetoothGattCharacteristic characteristic)函数,读取蓝牙中数据。
String address)、断开蓝牙连接函数disconnect()等。
三、蓝牙控制DeviceControlActivity
扫描到蓝牙设备之后就是对蓝牙进行自己需要的控制,比如写数据,读数据,获取设备信息,设备电量等。
在Service中讲到有一个广播,广播接收器就在这个activity中,通过不同的action做出相应的操作。
注册的几种事件
![](http://image.lxway.com/upload/2/53/253bae38169a5e4739c2bfc8c1b041d0_thumb.jpg)
有了这两个Service和characteristic的UUID,就可以对蓝牙发送数据,并发出通知(当写数据发生改变时发出)。
向蓝牙发送数据。
网上的Bluetooth BLE Demo都能实现BLE属性的读取,但写这块,我遇到的问题是:这些Demo都无法对BLE设备进行写数据,即无法修改BLE设备下的属性,如Major ,Minor,UUID等属性。这些Demo都是通过characteristic.setValue(byte[]),然后调用BluetoothGatt对象 mBluetoothGatt.writeCharacteristic(characteristic)进行数据写操作的,我用这样的方法能执行到Callback下的onCharacteristicWrite方法,但写入失败。
下面是我修改BLE设备属性的流程:(一般来说,characteristic是手机与BLE设备交换数据的关键,characteristic有很多跟权限相关的字段,要注意的一个地方是:手机与BLE设备是在连接状态下才能进行写数据)
BLE设备写数据主要分为三个流程:
(第一步)获取修改数据的权限
获取service对象,对应的UUID是“0000FFF0-0000-1000-8000-00805f9b34fb” ;(UUID对应功能见下图)
获取characteristic,对应UUID是“0000FFFA-0000-1000-8000-00805f9b34fb” ,该特性是指定授权的
调用characteristic.setValue(“AcCrEdItiSOK”.getBytes()),这个”AcCrEdItiSOK”具体是什么,我也还没搞懂,照抄上去就可以了。
调用writecharacteristic().
代码如下:
2
3
4
5
6
7
8
9
1
2
3
4
5
6
7
8
9
(第二步)获取属性对应的characteristic(这里以修改Minor为例)
service对象不变,获取Minor属性对应的characteristic对象,对应的UUID是”0000FFF3-0000-1000-8000-00805f9b34fb”。如需读取Minor的值,并不能直接通过characteristic.getValue()来读取,而是先使用readCharacteristic(characteristic),然后在OncharacteristicRead回调函数中使用characteristic.getValue()来读取。
setValue(new byte[]{1,1}) ;byte数组中传入你想修改的数据,由下图Ibeacon的功能选项图可知,Minor属性对应的长度是2个byte,十六进制,即写入的数据为0x101,修改后读取出来的数值应为十进制的257。
调用writecharacteristic().
代码如下:
2
3
4
5
6
7
1
2
3
4
5
6
7
(第三步)Reboot BLE (写入数据后要reboot,数据才能生效),这里的reboot指的不是将BLE设备关闭再开启,而是使用java代码写入相关characteristic。
获取对应reboot的characteristic,对应的UUID是”0000FFFF-0000-1000-8000-00805f9b34fb”
setValue(Base64.decode(“bWluZXcxMjM”.getBytes(),Base64.DEFAULT))
调用writecharacteristic().
2
3
4
5
6
7
8
1
2
3
4
5
6
7
8
如何将这三段代码整合进去:
以上三个步骤不可直接按顺序写,应该是每个步骤做完写操作后,在CallBack下的回调函数OncharacteristicWrite(…)中依次执行这三个步骤(这样确保每个步骤都是在上一个步骤完成后再执行的。反之,直接将这三个步骤连着写,是修改不了数据的,个人认为原因是只有执行了回调函数才能确保某一操作真正完成)。在OncharacteristicWrite(…)中作个判断,依次执行这三个write()操作。
注意:同理,读操作和写操作也不能写在一块,应执行玩一个操作后,在对应的回调函数中再执行另一个操作,否则,数据写不进去。
一、扫描BLE设备activity
检查该设备是否支持BLE设备,谷歌在Android4.3才开始支持BLE设备(晕死,很长一段时间都没有一台4.3的设备,看着程序修改了也不能测试!)。
if (!getPackageManager().hasSystemFeature(PackageManager.FEATURE_BLUETOOTH_LE)) { Toast.makeText(this, R.string.ble_not_supported, Toast.LENGTH_SHORT).show(); finish(); }
初始化获得一个bluetoothManager,并检测设备是否支持蓝牙
final BluetoothManager bluetoothManager = (BluetoothManager) getSystemService(Context.BLUETOOTH_SERVICE); mBluetoothAdapter = bluetoothManager.getAdapter();
// Checks if Bluetooth is supported on the device. if (mBluetoothAdapter == null) { Toast.makeText(this, R.string.error_bluetooth_not_supported, Toast.LENGTH_SHORT).show(); finish(); return; }扫描BLE设备,然后添加到listView里面。
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); invalidateOptionsMenu(); } }, SCAN_PERIOD); mScanning = true; mBluetoothAdapter.startLeScan(mLeScanCallback); } else { mScanning = false; mBluetoothAdapter.stopLeScan(mLeScanCallback); } invalidateOptionsMenu(); }
二、蓝牙控制的服务BluetoothLeService
在这个服务里面有一个很重要的回调函数BluetoothGattCallback(),蓝牙的数据读取和状态改变都会回调这个函数。
private final BluetoothGattCallback mGattCallback = new BluetoothGattCallback() { @Override public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) { String intentAction; //收到设备notify值 (设备上报值) if (newState == BluetoothProfile.STATE_CONNECTED) { intentAction = ACTION_GATT_CONNECTED; mConnectionState = STATE_CONNECTED; broadcastUpdate(intentAction); Log.i(TAG, "Connected to GATT server."); // Attempts to discover services after successful connection. 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 public void onServicesDiscovered(BluetoothGatt gatt, int status) { if (status == BluetoothGatt.GATT_SUCCESS) { broadcastUpdate(ACTION_GATT_SERVICES_DISCOVERED); } else { Log.w(TAG, "onServicesDiscovered received: " + status); System.out.println("onServicesDiscovered received: " + status); } } @Override public void onCharacteristicRead(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) { //读取到值,在这里读数据 if (status == BluetoothGatt.GATT_SUCCESS) { broadcastUpdate(ACTION_DATA_AVAILABLE, characteristic); } } @Override public void onCharacteristicChanged(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) { broadcastUpdate(ACTION_DATA_AVAILABLE, characteristic); } };在官方的demo中还使用到广播,可能是因为人大神写的,要严谨些。我一开始看的时候就得这有点麻烦,跳转的多麻烦。
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: // http://developer.bluetooth.org/gatt/characteristics/Pages/CharacteristicViewer.aspx?u=org.bluetooth.characteristic.heart_rate_measurement.xml 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(); for (int i = 0; i < data.length; i++) { System.out.println("data......" + data[i]); } 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()); intent.putExtra(EXTRA_DATA, new String(data)); } } sendBroadcast(intent); }发送了广播之后就肯定有广播接收器,这个是在控制蓝牙的activity中,后面再说。
还有几个重要的函数比如readCharacteristic(BluetoothGattCharacteristic characteristic)函数,读取蓝牙中数据。
public void readCharacteristic(BluetoothGattCharacteristic characteristic) { if (mBluetoothAdapter == null || mBluetoothGatt == null) { Log.w(TAG, "BluetoothAdapter not initialized"); return; } mBluetoothGatt.readCharacteristic(characteristic); }有readCharacteristic(BluetoothGattCharacteristic characteristic)函数,当然就有writeCharacteristic(BluetoothGattCharacteristic characteristic),向蓝牙中写入数据。
public void writeCharacteristic(BluetoothGattCharacteristic characteristic) { if (mBluetoothAdapter == null || mBluetoothGatt == null) { Log.w(TAG, "BluetoothAdapter not initialized"); return; } mBluetoothGatt.writeCharacteristic(characteristic); }另外在这个service中还有其他的一些函数例如初始化initialize()函数、连接蓝牙函数connect(final
String address)、断开蓝牙连接函数disconnect()等。
三、蓝牙控制DeviceControlActivity
扫描到蓝牙设备之后就是对蓝牙进行自己需要的控制,比如写数据,读数据,获取设备信息,设备电量等。
在Service中讲到有一个广播,广播接收器就在这个activity中,通过不同的action做出相应的操作。
注册的几种事件
private static IntentFilter makeGattUpdateIntentFilter() { final IntentFilter intentFilter = new IntentFilter(); intentFilter.addAction(BluetoothLeService.ACTION_GATT_CONNECTED); intentFilter.addAction(BluetoothLeService.ACTION_GATT_DISCONNECTED); intentFilter.addAction(BluetoothLeService.ACTION_GATT_SERVICES_DISCOVERED); intentFilter.addAction(BluetoothLeService.ACTION_DATA_AVAILABLE); return intentFilter; }
// Handles various events fired by the Service.处理服务所激发的各种事件 // ACTION_GATT_CONNECTED: connected to a GATT server.连接一个GATT服务 // ACTION_GATT_DISCONNECTED: disconnected from a GATT server.从GATT服务中断开连接 // ACTION_GATT_SERVICES_DISCOVERED: discovered GATT services.查找GATT服务 // 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)) { //写数据的服务和characteristic mnotyGattService = mBluetoothLeService.getSupportedGattServices(UUID.fromString("0000ffe5-0000-1000-8000-00805f9b34fb")); characteristic = mnotyGattService.getCharacteristic(UUID.fromString("0000ffe9-0000-1000-8000-00805f9b34fb")); //读数据的服务和characteristic readMnotyGattService = mBluetoothLeService.getSupportedGattServices(UUID.fromString("0000ffe0-0000-1000-8000-00805f9b34fb")); readCharacteristic = readMnotyGattService.getCharacteristic(UUID.fromString("0000ffe4-0000-1000-8000-00805f9b34fb")); } //显示数据 else if (BluetoothLeService.ACTION_DATA_AVAILABLE.equals(action)) { //将数据显示在mDataField上 String data = intent.getStringExtra(BluetoothLeService.EXTRA_DATA); System.out.println("data----" + data); displayData(data); } } };在发现了有可支持的服务之后会回调Service中的onServicesDiscovered()函数,并发送广播,在官方的demo中发现了可用的Service之后,就查找该BLE设备支持的所有服务和characteristic,在这里不需要查找所有的服务,只需要向蓝牙写数据和读取数据的Service和characteristic的UUID即可。通过查询低功耗蓝牙(BLE)的数据手册可以得到所需要的UUID。
![](http://image.lxway.com/upload/2/53/253bae38169a5e4739c2bfc8c1b041d0_thumb.jpg)
有了这两个Service和characteristic的UUID,就可以对蓝牙发送数据,并发出通知(当写数据发生改变时发出)。
<span style=";">//写数据的服务和characteristic mnotyGattService = mBluetoothLeService.getSupportedGattServices(UUID.fromString("0000ffe5-0000-1000-8000-00805f9b34fb")); characteristic = mnotyGattService.getCharacteristic(UUID.fromString("0000ffe9-0000-1000-8000-00805f9b34fb")); //读数据的服务和characteristic readMnotyGattService = mBluetoothLeService.getSupportedGattServices(UUID.fromString("0000ffe0-0000-1000-8000-00805f9b34fb")); readCharacteristic = readMnotyGattService.getCharacteristic(UUID.fromString("0000ffe4-0000-1000-8000-00805f9b34fb"));</span>得到这两个Service和characteristic就可以向蓝牙发送数据了。
private void read() { //mBluetoothLeService.readCharacteristic(readCharacteristic); //readCharacteristic的数据发生变化,发出通知 mBluetoothLeService.setCharacteristicNotification(readCharacteristic, true); //Toast.makeText(this, "读成功", Toast.LENGTH_SHORT).show(); }检测readCharacteristic的数据发生变化,发出通知。
向蓝牙发送数据。
read(); final int charaProp = characteristic.getProperties(); //如果该char可写 if ((charaProp | BluetoothGattCharacteristic.PROPERTY_READ) > 0) { // If there is an active notification on a characteristic, clear // it first so it doesn't update the data field on the user interface. if (mNotifyCharacteristic != null) { mBluetoothLeService.setCharacteristicNotification( mNotifyCharacteristic, false); mNotifyCharacteristic = null; } //读取数据,数据将在回调函数中 //mBluetoothLeService.readCharacteristic(characteristic); byte[] value = new byte[20]; value[0] = (byte) 0x00; if(edittext_input_value.getText().toString().equals("")){ Toast.makeText(getApplicationContext(), "请输入!", Toast.LENGTH_SHORT).show(); return; }else{ WriteBytes = edittext_input_value.getText().toString().getBytes(); characteristic.setValue(value[0],BluetoothGattCharacteristic.FORMAT_UINT8, 0); characteristic.setValue(WriteBytes); mBluetoothLeService.writeCharacteristic(characteristic); Toast.makeText(getApplicationContext(), "写入成功!", Toast.LENGTH_SHORT).show(); } } if ((charaProp | BluetoothGattCharacteristic.PROPERTY_NOTIFY) > 0) { mNotifyCharacteristic = characteristic; mBluetoothLeService.setCharacteristicNotification(characteristic, true); } edittext_input_value.setText(""); }一旦数据发生改变,就会发出通知,通知发出后就会调用下面的函数并发出广播。
@Override public void onCharacteristicChanged(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) { broadcastUpdate(ACTION_DATA_AVAILABLE, characteristic); }
网上的Bluetooth BLE Demo都能实现BLE属性的读取,但写这块,我遇到的问题是:这些Demo都无法对BLE设备进行写数据,即无法修改BLE设备下的属性,如Major ,Minor,UUID等属性。这些Demo都是通过characteristic.setValue(byte[]),然后调用BluetoothGatt对象 mBluetoothGatt.writeCharacteristic(characteristic)进行数据写操作的,我用这样的方法能执行到Callback下的onCharacteristicWrite方法,但写入失败。
下面是我修改BLE设备属性的流程:(一般来说,characteristic是手机与BLE设备交换数据的关键,characteristic有很多跟权限相关的字段,要注意的一个地方是:手机与BLE设备是在连接状态下才能进行写数据)
BLE设备写数据主要分为三个流程:
(第一步)获取修改数据的权限
获取service对象,对应的UUID是“0000FFF0-0000-1000-8000-00805f9b34fb” ;(UUID对应功能见下图)
获取characteristic,对应UUID是“0000FFFA-0000-1000-8000-00805f9b34fb” ,该特性是指定授权的
调用characteristic.setValue(“AcCrEdItiSOK”.getBytes()),这个”AcCrEdItiSOK”具体是什么,我也还没搞懂,照抄上去就可以了。
调用writecharacteristic().
代码如下:
BluetoothGattService service = mBluetoothGatt.getService(UUID.fromString( "0000FFF0-0000-1000-8000-00805f9b34fb")); public void wirte1() { BluetoothGattCharacteristic characteristic = service.getCharacteristic(UUID.fromString( "0000FFFA-0000-1000-8000-00805f9b34fb")); characteristic.setValue("AcCrEdItiSOK".getBytes()); mBluetoothGatt.writeCharacteristic(characteristic); }1
2
3
4
5
6
7
8
9
1
2
3
4
5
6
7
8
9
(第二步)获取属性对应的characteristic(这里以修改Minor为例)
service对象不变,获取Minor属性对应的characteristic对象,对应的UUID是”0000FFF3-0000-1000-8000-00805f9b34fb”。如需读取Minor的值,并不能直接通过characteristic.getValue()来读取,而是先使用readCharacteristic(characteristic),然后在OncharacteristicRead回调函数中使用characteristic.getValue()来读取。
setValue(new byte[]{1,1}) ;byte数组中传入你想修改的数据,由下图Ibeacon的功能选项图可知,Minor属性对应的长度是2个byte,十六进制,即写入的数据为0x101,修改后读取出来的数值应为十进制的257。
调用writecharacteristic().
代码如下:
public void write2() { BluetoothGattCharacteristic characteristic = service.getCharacteristic(UUID.fromString( "0000FFF3-0000-1000-8000-00805f9b34fb")); characteristic.setValue(new byte[]{1,1}); mBluetoothGatt.writeCharacteristic(characteristic); }1
2
3
4
5
6
7
1
2
3
4
5
6
7
(第三步)Reboot BLE (写入数据后要reboot,数据才能生效),这里的reboot指的不是将BLE设备关闭再开启,而是使用java代码写入相关characteristic。
获取对应reboot的characteristic,对应的UUID是”0000FFFF-0000-1000-8000-00805f9b34fb”
setValue(Base64.decode(“bWluZXcxMjM”.getBytes(),Base64.DEFAULT))
调用writecharacteristic().
public void write3() { BluetoothGattCharacteristic characteristic = service.getCharacteristic(UUID.fromString( "0000FFFF-0000-1000-8000-00805f9b34fb")); //我这个IBeacon设备的密码是“minew123”,通过UnicodeToBase64转换后是"bWluZXcxMjM" characteristic.setValue(Base64.decode("bWluZXcxMjM".getBytes(),Base64.DEFAULT)); mBluetoothGatt.writeCharacteristic(characteristic); }1
2
3
4
5
6
7
8
1
2
3
4
5
6
7
8
如何将这三段代码整合进去:
以上三个步骤不可直接按顺序写,应该是每个步骤做完写操作后,在CallBack下的回调函数OncharacteristicWrite(…)中依次执行这三个步骤(这样确保每个步骤都是在上一个步骤完成后再执行的。反之,直接将这三个步骤连着写,是修改不了数据的,个人认为原因是只有执行了回调函数才能确保某一操作真正完成)。在OncharacteristicWrite(…)中作个判断,依次执行这三个write()操作。
注意:同理,读操作和写操作也不能写在一块,应执行玩一个操作后,在对应的回调函数中再执行另一个操作,否则,数据写不进去。
相关文章推荐
- Android BLE 蓝牙低功耗教程,中央和周边的实现
- android沉浸式状态栏的最简单实现
- Android自定义Dialog对话框
- xposed multidex dex
- Android ViewGroup添加布局动画
- Android vold进程一 Vold启动和NetlinkManager数据获取
- 【Android】ScrollView嵌套RecyclerView,RecyclerView总是把它上面的控件顶出页面
- Android:使用PopupWindow.update( )碰到的坑
- Android自定义控件系列(二)—icon+文字的多种效果实现
- Android 搜索框:SearchView 的属性和用法详解
- android studio使用随手记
- Android 判断是开发debug模式,还是发布release模式
- Android RecyclerView item选中放大被遮挡问题
- Android学习之路---使用ViewPager实现引导页
- Android ScrollView和OnTouch冲突解决(最新)
- Android 中12时56分这类的处理
- Android自定义控件系列(一)—Button七十二变
- Android实现卡片翻转动画
- 关于android百度地图PoiNearbySearch无结果的问题
- 解决图片旋转问题