近期一个项目须要用到低功耗蓝牙的开发,因为以前没有蓝牙开发的经验,发现网上关于蓝牙开发的资料很少,不是随便描述一下就是已通过时的,在此整理一篇低功耗蓝牙的入门资料,可以完成使用蓝牙的接受和发送数据。html
低功耗蓝牙 (BLE,Bluetooth Low Energy的简称) 从Android 4.3 开始支持,现在愈来愈多外设都是使用低功耗蓝牙来传输数据的,与经典蓝牙本质上没有太多的区别,有不少类似之处,工做流程都是:发现设备 --> 配对/绑定设备 --> 链接设备 --> 数据传输。可是,低功耗蓝牙在安卓开发中的使用和经典蓝牙是彻底不一样的,若是按照以前很熟悉的经典蓝牙开发思惟来作,说不定还会踩坑。。。java
官方相关的开发指南:
经典蓝牙
低功耗蓝牙
低功耗蓝牙使用实例项目android
先来了解一些关于低功耗蓝牙的基本概念:git
这些概念不用深刻去探究,有必定了解开发的时候不至于一无所知就够了,想要具体了解低功耗蓝牙这里有篇不错的文章。github
使用蓝牙功能首先须要声明相关的权限,好比:app
<uses-permission android:name="android.permission.BLUETOOTH"/> <uses-permission android:name="android.permission.BLUETOOTH_ADMIN"/>
同时,也就能够经过蓝牙特性配置来限制支持蓝牙功能的设备使用APP:ide
<uses-feature android:name="android.hardware.bluetooth_le" android:required="true"/>
或者经过在代码中判断,不过如今基本没有什么手机不支持蓝牙功能了吧。post
// 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(); }
须要注意的是,官方说明 Android 5.0 及以上设备使用蓝牙时还须要定位权限,须要注意的是开发的时候若是是在 Android 6.0 及以上设备的须要动态获取定位权限,不然蓝牙功能也是没法使用的。ui
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/> <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" /> <manifest ... > <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" /> ... <!-- Needed only if your app targets Android 5.0 (API level 21) or higher. --> <uses-feature android:name="android.hardware.location.gps" /> ... </manifest>
private BluetoothAdapter mBluetoothAdapter; ... // Initializes Bluetooth adapter. final BluetoothManager bluetoothManager = (BluetoothManager) getSystemService(Context.BLUETOOTH_SERVICE); mBluetoothAdapter = bluetoothManager.getAdapter();
在开始扫描发现蓝牙设备以前须要确保手机的蓝牙功能打开。this
if (mBluetoothAdapter == null || !mBluetoothAdapter.isEnabled()) { Intent enableBtIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE); // 申请打开蓝牙 startActivityForResult(enableBtIntent, REQUEST_ENABLE_BT); }
而后在 onActivityResultI
方法中判断用户是否赞成开启蓝牙功能。
private static final long SCAN_PERIOD = 10000; private void scanLeDevice(final boolean 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); } /** * 发现设备的回调 */ private BluetoothAdapter.LeScanCallback mLeScanCallback = new BluetoothAdapter.LeScanCallback() { @Override public void onLeScan(final BluetoothDevice device, int rssi, byte[] scanRecord) { } };
mBluetoothGatt = device.connectGatt(this, false, mGattCallback);
链接设备的方法须要传入三个参数,第一个是 context,第二个是 boolean,表示是否自动链接,第三个是链接的回调接口,其中有几个很重要的方法。
private final BluetoothGattCallback mGattCallback = new BluetoothGattCallback() { @Override public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) { String intentAction; if (newState == BluetoothProfile.STATE_CONNECTED) { mConnectionState = STATE_CONNECTED; // 开始查找服务,只有找到服务才算是真的链接上 mBluetoothGatt.discoverServices(); } else if (newState == BluetoothProfile.STATE_DISCONNECTED) { intentAction = ACTION_GATT_DISCONNECTED; mConnectionState = STATE_DISCONNECTED; } } @Override public void onServicesDiscovered(BluetoothGatt gatt, int status) { if (status == BluetoothGatt.GATT_SUCCESS) { } else { } } @Override public void onCharacteristicWrite(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) { super.onCharacteristicWrite(gatt, characteristic, status); } @Override public void onCharacteristicRead(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) { if (status == BluetoothGatt.GATT_SUCCESS) { } } @Override public void onCharacteristicChanged(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) { byte[] data = characteristic.getValue(); } };
链接设备成功后须要在回调方法中发现服务mBluetoothGatt.discoverServices()
,发现服务后会回调onServicesDiscovered
方法,发现服务成功才算是真正的链接上蓝牙设备。onCharacteristicWrite
方法是写操做结果的回调,onCharacteristicChanged
方法是状态改变的回调,在该方法中可以获取蓝牙发送的数据。不过,在接收数据以前,咱们必须对Characteristic设置监听才可以接收到蓝牙的数据。
public void setNotification() { BluetoothGattService service = mBluetoothGatt.getService(SERVICE_UUID); if (service == null) { L.e("未找到蓝牙中的对应服务"); return; } BluetoothGattCharacteristic characteristic= service.getCharacteristic(CharacteristicUUID); if (characteristic== null) { L.e("未找到蓝牙中的对应特征"); return; } //设置true为启用通知,false反之 mBluetoothGatt.setCharacteristicNotification(characteristic, true); //下面为开启蓝牙notify功能,向CCCD中写入值1 BluetoothGattDescriptor descriptor = characteristic.getDescriptor(CCCD); descriptor.setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE); mBluetoothGatt.writeDescriptor(descriptor); }
先经过 UUID 找到咱们须要进行数据传输的service,在找到咱们想要设置监听的Characteristic的 UUID 找到响应的characteristic对象,找到响应的characteristic后调用setCharacteristicNotification
方法启用通知,则该characteristic状态发生改变后就会回调onCharacteristicChanged
方法,而开启蓝牙的 notify 功能须要向 UUID 为 CCCD 的 descriptor 中写入值1,其中 CCCD 的值为:
public static final UUID CCCD = UUID.fromString("00002902-0000-1000-8000-00805f9b34fb");
往蓝牙发送数据,能够理解为给蓝牙的characteristic设置数据。
public void writeRXCharacteristic(byte[] value) { if (mBluetoothGatt == null) { return; } BluetoothGattService service= mBluetoothGatt.getService(SERVICE_UUID); BluetoothGattCharacteristic characteristic= service.getCharacteristic(UUID); characteristic.setValue(value); mBluetoothGatt.writeCharacteristic(characteristic); }
咱们能够写入String
和byte[]
的数据,通常为byte[]
。其中咱们须要与哪一个service的characteristic进行数据传输能够联系硬件工程师或者查看蓝牙设备供应商提供的说明得到。咱们也能够经过mBluetoothGatt.getServices()
和mBluetoothGatt.getgetCharacteristics()
方法获取蓝牙设备的全部
service 和某个 service 中的全部 characteristic。
低功耗蓝牙一次性只能发送 20 个字节的数据,超过 20 个字节的没法发送,所以须要对发送的数据进行分包处理,在此给出数据分包的一个示例,是在别人 github 中看到的:
//存储待发送的数据队列 private Queue<byte[]> dataInfoQueue = new LinkedList<>(); private Handler handler = new Handler() { @Override public void handleMessage(Message msg) { super.handleMessage(msg); } }; private Runnable runnable = new Runnable() { @Override public void run() { send(); } }; /** * 向characteristic写数据 * * @param value */ public void writeCharacteristic(BluetoothGattCharacteristic characteristic, byte[] value) { this.mCharacteristic = characteristic; if (dataInfoQueue != null) { dataInfoQueue.clear(); dataInfoQueue = splitPacketFor20Byte(value); handler.post(runnable); } // characteristic.setValue(value); // mBluetoothGatt.writeCharacteristic(characteristic); } private void send() { if (dataInfoQueue != null && !dataInfoQueue.isEmpty()) { //检测到发送数据,直接发送 if (dataInfoQueue.peek() != null) { this.mCharacteristic.setValue(dataInfoQueue.poll());//移除并返回队列头部的元素 mBluetoothGatt.writeCharacteristic(mCharacteristic); } //检测还有数据,延时后继续发送,通常延时100毫秒左右 if (dataInfoQueue.peek() != null) { handler.postDelayed(runnable, 200); } } } //数据分包处理 private Queue<byte[]> splitPacketFor20Byte(byte[] data) { Queue<byte[]> dataInfoQueue = new LinkedList<>(); if (data != null) { int index = 0; do { byte[] surplusData = new byte[data.length - index]; byte[] currentData; System.arraycopy(data, index, surplusData, 0, data.length - index); if (surplusData.length <= 20) { currentData = new byte[surplusData.length]; System.arraycopy(surplusData, 0, currentData, 0, surplusData.length); index += surplusData.length; } else { currentData = new byte[20]; System.arraycopy(data, index, currentData, 0, 20); index += 20; } dataInfoQueue.offer(currentData); } while (index < data.length); } return dataInfoQueue; }
这篇文章简单介绍了安卓进行低功耗蓝牙开发的流程以及提到了一些注意事项,看完本文基本就可以进行低功耗蓝牙开发,除此意外,强烈推荐去 Github 看一下低功耗蓝牙使用实例项目,例子不难理解,看懂了 Demo 能对低功耗蓝牙的使用有更深的了解。
本文原文地址:http://electhuang.com/2017/07/10/android-ble/