android 蓝牙4.0 ble 低功耗蓝牙
2015-11-18 16:46
543 查看
一:概述
这段时间做了蓝牙4.0的项目,就是一个蓝牙设备控制手机进行拍照。并且有很多按键,不同的按键对应到手机上有不同的功能,并且组合起来也有不同的功能。[code] 低功耗蓝牙有中央设备后周边设备的概念手机就是一个中央设备,像我这次试用的一个控制器, 我试过小米体重秤。来测试玩。 a.GATT 这是蓝牙技术联盟定义的一个协议。 b.Service 这个是许多或者一个特征值的集合。 c.Characteristic 这是特征值。我们需要使用的数据就是这个。 我们可以读取或者在其值在变化的时候会接收到其变化的回调。 d.Descriptor 这个是特征值的描述。最开始我有点不太懂这个东西到底哪里能用到。结果在设置 Characteristic 为可以通知的时候才看到,就是辅助Characteristic 的。这个谷歌官方的解释
可以看看我做的一个小demo的效果(一个控制器按键后):
请忽略背景音乐
二:源码解析
现在就通过源码讲解下我这个小demo:因为这个是需要蓝牙权限,并且在android 系统4.3后才支持的接口。所以在使用蓝牙之前都需要申请权限和判断是否可以使用.
[code] <uses-permission android:name="android.permission.BLUETOOTH"/> <uses-permission android:name="android.permission.BLUETOOTH_ADMIN"/> //这里的true标示手机如果不支持低功耗蓝牙就直接不让安装,如果你不想这样的话,可以写false <uses-feature android:name="android.hardware.bluetooth_le" android:required="true"/>
这里是检查是否支持低功耗蓝牙。
[code] // 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,"不支持蓝牙4.0", Toast.LENGTH_SHORT).show(); finish(); }
获取mBluetoothAdapter 需要用他进行判断蓝牙的开关,和搜索蓝牙设备。
[code] // Initializes Bluetooth adapter. final BluetoothManager bluetoothManager = (BluetoothManager) getSystemService(Context.BLUETOOTH_SERVICE); mBluetoothAdapter = bluetoothManager.getAdapter(); if(mBluetoothAdapter == null){ Toast.makeText(this,"获取失败!", Toast.LENGTH_SHORT).show(); finish(); }
猜这个是干嘛的,居然猜到是判断是否开蓝牙了。哟西。
如果没有开蓝牙当然就让用户开了。会有一个弹窗出来让用户选择是开还是关。
[code] if (!mBluetoothAdapter.isEnabled()) { Intent enableBtIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE); startActivityForResult(enableBtIntent, REQUEST_ENABLE_BT); }
如果用户选择不开的话,就直接结束当前的activity。
[code] @Override protected void onActivityResult(int requestCode, int resultCode, Intent data) { // User chose not to enable Bluetooth. if (requestCode == REQUEST_ENABLE_BT && resultCode == Activity.RESULT_CANCELED) { finish(); return; } super.onActivityResult(requestCode, resultCode, data); }
好了。蓝牙也开了。那么开始搜索蓝牙了。mLeScanCallback是一个回调,当搜索到一个蓝牙的时候就回调他的接口
[code] 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); } }
喏,回调就长这个样子。还行吧。
BluetoothDevice 重写了equals的方法用设备的address作为作为判断的
所以 listDevice.contains(device) 是可以判断的。好吧。这是java的基础知识。还是唠叨两句。
[code] 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() { if(!listDevice.contains(device)){ //不重复添加 listDevice.add(device); deviceAdapter.setListDevice(listDevice); } } }); } };
我们找到了蓝牙之后干嘛呢?你觉得还能干嘛呢?肯定就是开始玩dota啦(想的美)。
好了我们准备开始连接蓝牙。
[code]/*mBluetoothLeService是一个android上的后台服务,不是低功耗蓝牙中的服务啊。这个service是从谷歌demo中拷贝的,改动了部分。*/ mBluetoothLeService.connect(device.getAddress());
这个connect中具体的实现,我看见了mBluetoothGatt,就是那个破协议。
然后还看见mGattCallback一个回调,想想应该是连接成功后的一些回调。
[code] public boolean connect(final String address) { if (mBluetoothAdapter == null || address == null) { Log.w(TAG, "BluetoothAdapter not initialized or unspecified address."); return false; } if (mBluetoothDeviceAddress != null && address.equals(mBluetoothDeviceAddress) && mBluetoothGatt != null) { if (mBluetoothGatt.connect()) { mConnectionState = STATE_CONNECTING; return true; } else { return false; } } final BluetoothDevice device = mBluetoothAdapter.getRemoteDevice(address); if (device == null) { Log.w(TAG, "Device not found. Unable to connect."); return false; } // We want to directly connect to the device, so we are setting the autoConnect // parameter to false. mBluetoothGatt = device.connectGatt(this, false, mGattCallback); Log.d(TAG, "Trying to create a new connection."); mBluetoothDeviceAddress = address; mConnectionState = STATE_CONNECTING; return true; }
下面的就是连接成功后具体的回调。各部分干什么的在注释中可以看看。
broadcastUpdate(…)是发送广播。
[code]private final BluetoothGattCallback mGattCallback = new BluetoothGattCallback() { @Override public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) { String intentAction; if (newState == BluetoothProfile.STATE_CONNECTED) { //蓝牙连接成功后 broadcastUpdate(intentAction); } else if (newState == BluetoothProfile.STATE_DISCONNECTED) { //蓝牙断开连接 broadcastUpdate(intentAction); } } @Override public void onServicesDiscovered(BluetoothGatt gatt, int status) { if (status == BluetoothGatt.GATT_SUCCESS) { //这个就是发现了蓝牙4.0的服务 broadcastUpdate(ACTION_GATT_SERVICES_DISCOVERED); } else { Log.w(TAG, "onServicesDiscovered received: " + status); } } @Override public void onCharacteristicRead(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) { if (status == BluetoothGatt.GATT_SUCCESS) { //这个是读取Characteristic,通俗点就是读取我们想要的数据 broadcastUpdate(ACTION_DATA_***AILABLE, characteristic); } } @Override public void onCharacteristicChanged(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) { //这个是我们监听的某个Characteristic变化了。然后发送一个广播 broadcastUpdate(ACTION_DATA_***AILABLE, characteristic); } };
再看看我们接收广播的时候做了什么 这个广播接收的部分代码。因为代码贴的太多感觉有点不爽了。
[code] else if (BluetoothLeService. ACTION_GATT_SERVICES_DISCOVERED.equals(action)) { //services被发现的状态 //在这里找到要要读的特征值或者需要通知的特征值 LogServiceAndChara(mBluetoothLeService.getSupportedGattServices()); }
我们在看看LogServiceAndChara()函数到底干了啥。(这个函数有点乱。T_T,就贴出部分代码吧)
首先我们遍历所有服务然后找我们想要的特殊服务(-_-我啥都不知道。)这个特殊服务怎么找?
用uuid,找到了我们想找的服务后,然互就开始找特征值,也是用uuid识别。
[code]private void LogServiceAndChara(List<BluetoothGattService> gattServices){ // Loops through available GATT Services. for (BluetoothGattService gattService : gattServices) { if(gattService.getUuid().toString().equals(SampleGattAttributes.DEVICE_SERVICE)){ //找到了这个服务 List<BluetoothGattCharacteristic> gattCharacteristics = gattService.getCharacteristics(); for(BluetoothGattCharacteristic gattCharacteristic : gattCharacteristics){ if(gattCharacteristic.getUuid().toString().equals(SampleGattAttributes.DEVICE_CHARACTER)){ //找到了对应的服务 if((gattCharacteristic.getProperties() | BluetoothGattCharacteristic.PROPERTY_READ)>0){ if(mNotifyCharacteristic != null){ mBluetoothLeService.setCharacteristicNotification(gattCharacteristic,false); mNotifyCharacteristic = null; } mBluetoothLeService.readCharacteristic(gattCharacteristic); } if((gattCharacteristic.getProperties() | BluetoothGattCharacteristic.PROPERTY_NOTIFY)>0){ mBluetoothLeService.setCharacteristicNotification(gattCharacteristic,true); mNotifyCharacteristic = gattCharacteristic; } } } } } }
这两个分别是特征值和服务的uuid
[code]public static String DEVICE_CHARACTER = "0000dfb1-0000-1000-8000-00805f9b34fb"; public static String DEVICE_SERVICE = "0000dfb0-0000-1000-8000-00805f9b34fb";
找到了我们想要的特征值后我们就要监听他了,或者读取他,我这里是用的监听。
这里有个点我纠结了挺久的地方。
[code] BluetoothGattDescriptor descriptor = characteristic.getDescriptor( UUID.fromString(SampleGattAttributes.CLIENT_CHARACTERISTIC_CONFIG));
我打印了所有特征值的descriptor的uuid都是一个样,我觉得应该不是硬件上自定义的。应该是蓝牙技术联盟定义的公用的。
然后去蓝牙技术联盟的官网查了下。发现了这个。蓝牙技术联盟CLIENT_CHARACTERISTIC_CONFIG解释
确定了就是他们定义公用的,然后用
descriptor.setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE);
设置这个特征值为可以为监听状态(就是可以变化的时我能收到通知)
[code] public void setCharacteristicNotification(BluetoothGattCharacteristic characteristic, boolean enabled) { if (mBluetoothAdapter == null || mBluetoothGatt == null) { Log.w(TAG, "BluetoothAdapter not initialized"); return; } mBluetoothGatt.setCharacteristicNotification(characteristic, enabled); //关于CLIENT_CHARACTERISTIC_CONFIG这个descriptor的解释 BluetoothGattDescriptor descriptor = characteristic.getDescriptor( UUID.fromString(SampleGattAttributes.CLIENT_CHARACTERISTIC_CONFIG)); descriptor.setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE); mBluetoothGatt.writeDescriptor(descriptor); }
在接收到变化或读取到后都能在这里接收到(这个是来自广播中的方法)
[code] else if (BluetoothLeService.ACTION_DATA_***AILABLE.equals(action)) { //接收到数据的状态 str += intent.getStringExtra(BluetoothLeService.EXTRA_DATA); str += "\n\r"; tv.setText(str); }
源码下载
加个好友共同学习(不是公众号):
因为小弟水平有限,如果有写的有问题,希望指出。
相关文章推荐
- Android 总结:ContentProvider 的使用
- android利用sdk文档查看style相关属性
- Android Context 上下文 你必须知道的一切
- android的消息处理机制(图+源码分析)——Looper,Handler,Message
- android 异步操作总结
- Android Fragment 你应该知道的一切
- 学习笔记_android之复制黏贴实现方法
- Android基础入门教程——8.4.4 Android动画合集之属性动画-又见
- Android中View绘制流程以及invalidate()等相关方法分析
- Java中的Timer和TimerTask在Android中的用法
- Android实现获取验证码效果
- Android 高清加载巨图方案 拒绝压缩图片
- IT蓝豹强烈推荐:符合1-2年工作经验,开发中的难点及相关优化:
- android中string.xml中的特殊符号写法
- Android自定义View的实现方法,带你一步步深入了解View(四)
- Android模拟器无法上网问题
- Android开发者应该深入学习的10个开源应用项目
- 使用AS-构建自己的仓库
- Android实现QQ第三方登录
- Android视图状态及重绘流程分析,带你一步步深入了解View(三)