代码地址以下:
http://www.demodashi.com/demo/15062.htmlhtml
随着物联网时代的到来,愈来愈多的智能硬件设备开始流行起来,好比智能手环、心率检测仪、以及各式各样的智能家具和玩具类产品。安卓4.3(API 18)为BLE的核心功能提供平台支持和API,App能够利用它来发现设备、查询服务和读写特性。相比传统的蓝牙,BLE更显著的特色是低功耗。本文主要讲解Android低功耗蓝牙的api使用以及蓝牙扫描、链接、发送数据、接收数据等一系列操做,并主要介绍本人封装的BleLib蓝牙库,很是适合蓝牙初学者使用,只须要一行代码注入就OK了,并且用法也极其简单,下面会专门讲解BleLib库的使用。android
废话不说,先来看下Demo中的效果图:api
在BLE协议中,有两个角色,周边(Periphery)和中央(Central);周边是数据提供者,中央是数据使用/处理者,一个中央能够同时链接多个周边,可是一个周边某一时刻只能链接一个中央。
首先使用蓝牙就不得不说BluetoothGatt和BluetoothGattCallback这两个类,该类继承自BluetoothProfile,BluetoothGatt做为中央来使用和处理数据,经过BluetoothGatt能够链接设备(connect),发现服务(discoverServices),并把相应地属性返回到BluetoothGattCallback,BluetoothGattCallback返回中央的状态和周边提供的数据。服务器
咱们蓝牙操做的主要目的就是为了拿到中央BluetoothGatt这个对象,进而进行接下来的全部一系列操做,以下:app
1.先拿到BluetoothManager bluetoothManager = (BluetoothManager) getSystemService(Context.BLUETOOTH_SERVICE); 2.再拿到BluetoothAdapt btAdapter = bluetoothManager.getAdapter(); 3.开始扫描:btAdapter.startLeScan( BluetoothAdapter.LeScanCallback); 4.从LeScanCallback中获得BluetoothDevice public void onLeScan(BluetoothDevice device, int rssi, byte[] scanRecord) {…..} 5.用BluetoothDevice获得BluetoothGatt:gatt = device.connectGatt(this, true, gattCallback);
这时总算拿到中央BluetoothGatt了,它有不少的方法,调用这些方法,你就能够经过BluetoothGattCallback和周边BluetoothGattServer交互了。ide
BluetoothProfile: 一个通用的规范,按照这个规范来收发数据。post
BluetoothManager:经过BluetoothManager来获取BluetoothAdapter大数据
如:BluetoothManager bluetoothManager = (BluetoothManager) getSystemService(Context.BLUETOOTH_SERVICE);
BluetoothAdapter:一个Android系统只有一个BluetoothAdapter ,经过BluetoothManager 获取ui
BluetoothAdapter mBluetoothAdapter = bluetoothManager.getAdapter();
BluetoothGattDescriptor:能够当作是描述符,对Characteristic的描述,包括范围、计量单位等。this
BluetoothGattService:服务,Characteristic的集合。
BluetoothGattCallback:已经链接上设备,对设备的某些操做后返回的结果。这里必须提醒下,已经链接上设备后的才能够返回,没有返回的认真看看有没有链接上设备。
private BluetoothGattCallback GattCallback = new BluetoothGattCallback() { // 这里有9个要实现的方法,看状况要实现那些,用到那些就实现那些 public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState){}; public void onCharacteristicWrite(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status){ }; }; BluetoothDevice device = mBluetoothAdapter.getRemoteDevice(address); BluetoothGatt gatt = device.connectGatt(this, false, mGattCallback);
(1) notification对应onCharacteristicChanged;
gatt.setCharacteristicNotification(characteristic, true);
该方法通常是在发现服务后,进行设置的,设置该方法的目的是让硬件在数据改变的时候,发送数据给app,app则经过onCharacteristicChanged方法回调给用户,从参数中可获取到回调回来的数据。
(2) readCharacteristic对应onCharacteristicRead;
gatt.readCharacteristic(characteristic);
(3) writeCharacteristic对应onCharacteristicWrite;
gatt.wirteCharacteristic(mCurrentcharacteristic);
(4) 链接蓝牙或者断开蓝牙 对应 onConnectionStateChange;
bluetoothDevice.connectGatt(this, false, mGattCallback); 或 gatt.disconnect();(断开链接后务必记得gatt.close();)
(5) readDescriptor对应onDescriptorRead;
gatt.readDescriptor(descriptor);
(6) writeDescriptor对应onDescriptorWrite;
gatt.writeDescriptor(descriptor);
(7) readRemoteRssi对应onReadRemoteRssi;
gatt.readRemoteRssi();
(8) executeReliableWrite对应onReliableWriteCompleted;
gatt.executeReliableWrite();
(9) discoverServices对应onServicesDiscovered
gatt.discoverServices();
<uses-permission android:name="android.permission.BLUETOOTH"/> <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/> <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/> <uses-permission android:name="android.permission.BLUETOOTH_ADMIN"/> <uses-feature android:name="android.hardware.bluetooth_le" android:required="true"/>
若是 android.hardware.bluetooth_le设置为false,能够安装在不支持的设备上使用,判断是否支持蓝牙4.0用如下代码就能够了,如:
if (!getPackageManager().hasSystemFeature(PackageManager.FEATURE_BLUETOOTH_LE)) { Toast.makeText(this, “设备不支持蓝牙4.0”, Toast.LENGTH_SHORT).show(); finish(); }
对蓝牙的启动关闭操做:
一、利用系统默认开启蓝牙对话框
if (mBluetoothAdapter == null || !mBluetoothAdapter.isEnabled()) { Intent enableBtIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE); startActivityForResult(enableBtIntent, REQUEST_ENABLE_BT); }
二、后台打开蓝牙,不作任何提示,这个也能够用来自定义打开蓝牙对话框啦
mBluetoothAdapter.enable();
三、后台关闭蓝牙
mBluetoothAdapter.disable();
private void initBle() {
mBle = Ble.options()
.setLogBleExceptions(true)//设置是否输出打印蓝牙日志(非正式打包请设置为true,以便于调试)
.setThrowBleException(true)//设置是否抛出蓝牙异常
.setAutoConnect(true)//设置是否自动链接
.setConnectFailedRetryCount(3)
.setConnectTimeout(10 * 1000)//设置链接超时时长(默认101000 ms)
.setScanPeriod(12 1000)//设置扫描时长(默认10*1000 ms)
.setUuid_service(UUID.fromString("0000fee9-0000-1000-8000-00805f9b34fb"))//主服务的uuid
.setUuid_write_cha(UUID.fromString("d44bc439-abfd-45a2-b575-925416129600"))//可写特征的uuid
.create(getApplicationContext());
}
有对比才有伤害,那就来看下原生api调用蓝牙流程和该库之间的对比:
private void scanLeDevice(final boolean enable) { if (enable) { // 通过预约扫描期后中止扫描 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); } ... } 而后在mLeScanCallback的回调中拿到扫描结果: // 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() { ... } }); }
mBle.startScan(scanCallback); 回调结果: BleScanCallback<BleDevice> scanCallback = new BleScanCallback<BleDevice>() { @Override public void onLeScan(final BleDevice device, int rssi, byte[] scanRecord) { ... } } };
这绝对是其余蓝牙库所没有的,具体API请看下面的库使用步骤
private void initBle() { mBle = Ble.options() .setLogBleExceptions(true)//设置是否输出打印蓝牙日志(非正式打包请设置为true,以便于调试) .setThrowBleException(true)//设置是否抛出蓝牙异常 .setAutoConnect(true)//设置是否自动链接 .setConnectFailedRetryCount(3)//设置链接失败的重试次数 .setConnectTimeout(10 * 1000)//设置链接超时时长(默认10*1000 ms) .setScanPeriod(12 * 1000)//设置扫描时长(默认10*1000 ms) .setUuid_service(UUID.fromString("0000fee9-0000-1000-8000-00805f9b34fb"))//主服务的uuid .setUuid_write_cha(UUID.fromString("d44bc439-abfd-45a2-b575-925416129600"))//可写特征的uuid .create(getApplicationContext()); }
mBle.startScan(scanCallback); //扫描回调 BleScanCallback<BleDevice> scanCallback = new BleScanCallback<BleDevice>() { @Override public void onLeScan(final BleDevice device, int rssi, byte[] scanRecord) { ... //获取到蓝牙设备对象,根据自身需求进行操做(库中已进行相同设备的过滤) } };
mBle.connect(device, connectCallback); //链接回调 private BleConnCallback<BleDevice> connectCallback = new BleConnCallback<BleDevice>() { @Override public void onConnectionChanged(BleDevice device) { if (device.isConnected()) { //链接成功以后设置通知(切记,很重要) setNotify(device); } Log.e(TAG, "onConnectionChanged: " + device.isConnected()); } @Override public void onConnectException(BleDevice device, int errorCode) { super.onConnectException(device, errorCode); Toast.makeText(BleActivity.this, "链接异常,异常状态码:" + errorCode, Toast.LENGTH_SHORT).show(); } };
链接异常状态码可参阅该项目的[wiki]
private void setNotify(BleDevice device) { /*链接成功后,设置通知*/ mBle.startNotify(device, new BleNotiftCallback<BleDevice>() { @Override public void onChanged(BleDevice device, BluetoothGattCharacteristic characteristic) { Log.e(TAG, "onChanged: 表示返回硬件MCU发来的数据"+Arrays.toString(characteristic.getValue())); } @Override public void onReady(BleDevice device) { Log.e(TAG, "onReady: 表示一切准备就绪,能够进行读写(发送数据或者读取数据)的标志"); } @Override public void onServicesDiscovered(BluetoothGatt gatt) { Log.e(TAG, "onServicesDiscovered is success "); } @Override public void onNotifySuccess(BluetoothGatt gatt) { Log.e(TAG, "onNotifySuccess is success "); } }); }
当收到onChanged(BluetoothGattCharacteristic characteristic)回调时,则说明蓝牙设备的数据发生改变了,通知程序做出改变。还有不少回调,他们对应的状况不懂得能够参考上面的原生API的详细讲解。
mBle.readRssi(mBle.getConnetedDevices().get(0), new BleReadRssiCallback<BleDevice>() { @Override public void onReadRssiSuccess(int rssi) { super.onReadRssiSuccess(rssi); Log.e(TAG, "onReadRssiSuccess: " + rssi); Toast.makeText(BleActivity.this, "onReadRssiSuccess:"+ rssi, Toast.LENGTH_SHORT).show(); } });
public void read(BleDevice device) { boolean result = mBle.read(device, new BleReadCallback<BleDevice>() { @Override public void onReadSuccess(BluetoothGattCharacteristic characteristic) { super.onReadSuccess(characteristic); byte[] data = characteristic.getValue(); Log.w(TAG, "onReadSuccess: " + Arrays.toString(data)); } }); if (!result) { Log.d(TAG, "读取数据失败!"); }
boolean result = mBle.write(device, changeLevelInner(), new BleWriteCallback<BleDevice>() { @Override public void onWriteSuccess(BluetoothGattCharacteristic characteristic) { Toast.makeText(BleActivity.this, "发送数据成功", Toast.LENGTH_SHORT).show(); } }); if (!result) { Log.e(TAG, "changeLevelInner: " + "发送数据失败!"); }
try { //获取整个文件的总字节 byte[]data = toByteArray(getAssets().open("WhiteChristmas.bin")); //发送大数据量的包(参数请查阅Demo Code) mBle.writeEntity(mBle.getConnetedDevices().get(0), data, 20, 50, new BleWriteEntityCallback<BleDevice>() { @Override public void onWriteSuccess() { L.e("writeEntity", "onWriteSuccess"); } @Override public void onWriteFailed() { L.e("writeEntity", "onWriteFailed"); } }); } catch (IOException e) { e.printStackTrace(); }
if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP){ //此处第二个参数 不是特定的 好比你也能够设置500 可是若是设备不支持500个字节则会返回最大支持数 mBle.setMTU(mBle.getConnetedDevices().get(0).getBleAddress(), 250, new BleMtuCallback<BleDevice>() { @Override public void onMtuChanged(BleDevice device, int mtu, int status) { super.onMtuChanged(device, mtu, status); ToastUtil.showToast("最大支持MTU:"+mtu); } }); }else { ToastUtil.showToast("设备不支持MTU"); }
//找到你须要升级文件的路径(通常状况都是保存再服务器上,一旦有更新会自动提示,而后APP下载并保存到本地,生成对应的file对象) File file = new File(...); //读写SD卡权限,此处略(6.0及以上需添加) OtaManager mOtaManager = new OtaManager(BleActivity.this); boolean result = mOtaManager.startOtaUpdate(file, (BleDevice) mBle.getConnetedDevices().get(0), mBle); Log.e("OTA升级结果:", result + "");
分析以前先来张BleLib库API的结构图供你们参考(下图是1.x库的结构,API名称部分与当前有点不一样):
该类提供了几乎全部你须要用到的方法,包括蓝牙扫描、链接、断开、蓝牙当前链接状态等等,管理了蓝牙操做的全部接口和方法。
该类的主要是来描述并记录蓝牙的属性和状态,如记录蓝牙名称、蓝牙MAC地址、蓝牙别名(即修改以后的名称)、蓝牙链接状态等。
该类是蓝牙状态类,定义了蓝牙扫描、链接、通知使能、发送、接收等状态的常量值(链接异常等状态码可参考该类)
该类是最重要的一个核心蓝牙处理类,主要是蓝牙操做中用到的各个方法的实现类,是整个蓝牙的核心功能实现,Ble是对外提供全部蓝牙操做接口的管理类。
在此要注意一些细节,好比大多数设备扫描的时候会重复扫描到相同蓝牙设备,必需要进行过滤,开发应用时,必须还要进行产品过滤,好比经过设备的广播包过滤,或者经过设备名过滤都是能够的,以下(注意:要根据本身产品提供的广播包进行过滤,下图是咱们本身产品的):
/** * Verify the product broadcast parameters * @param data Parameter data * @return Whether the match */ public static boolean matchProduct(byte[] data) { if (data == null || data.length <= 0) { return false; } int i = 0; do { // Read packet size int len = data[i++] & 0xff; if (len > 0) { // Read packet data byte[] d = new byte[len]; int j = 0; do { d[j++] = data[i++]; } while (j < len); // Authentication Type and Length if (d.length > BROADCAST_SPECIFIC_PRODUCT.length && (d[0] & 0xFF) == BLE_GAP_AD_TYPE_MANUFACTURER_SPECIFIC_DATA) { // Matching product parameters boolean passed = true; for (int x = 0; x < BROADCAST_SPECIFIC_PRODUCT.length; x++) { passed = passed && d[x + 1] == BROADCAST_SPECIFIC_PRODUCT[x]; } //Match successful if (passed) { return true; } } } } while (i < data.length); return false; }
OK,要注意的细节问题已经介绍的差很少了,若是感兴趣的朋友能够去应用该库到本身的项目中。
Android BLE蓝牙详细解读
代码地址以下:
http://www.demodashi.com/demo/15062.html
注:本文著做权归做者,由demo大师代发,拒绝转载,转载须要做者受权