Android 蓝牙4.0使用心得
来源:互联网 发布:maven实战源码 许晓斌 编辑:程序博客网 时间:2024/05/29 18:04
最近在公司做了个蓝牙项目,之前刚出道的时候做过个智能手环的项目,那时蓝牙模块是开发组长写的,我看的也是一知半解,很多不了解的东西现在看起来有些新心得,写下来记录下.
蓝牙4.0(BLE) 在安卓4.3(API 18)以上支持,相比传统的蓝牙,BLE更显著的特点是低功耗。是Android 蓝牙史上一大转折点
经过这次开发我把BLE使用只要分为4个步骤:
- 搜索蓝牙设备
- 连接设备/断开连接设备
- 设置服务的读/写/通知通道
- 操作写,读/接受通知
搜索蓝牙设备
首先要获取到手机蓝牙权限。
在应用程序manifest文件中添加如下代码,声明蓝牙权限。
<uses-permission android:name="android.permission.BLUETOOTH"/><uses-permission android:name="android.permission.BLUETOOTH_ADMIN"/>
但在android6.0以上做开发的同学需要注意在6.0以上还需加上位置的权限,否则会搜索不到任何设备。还需动态去获取权限(动态获取不详说)
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" /> <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
2.判断手机是否支持蓝牙和蓝牙是否已打开,否跳转打开蓝牙设备
BluetoothAdapter=mBluetoothAdapte=BluetoothAdapter.getDefa ultAdapter(); if (mBluetoothAdapter == null) { "此设备不支持蓝牙传输功能!" } else { "此设备支持蓝牙传输功能!", if (!mBluetoothAdapter.isEnabled())//判 断是否打开,否就跳转打开 { Intent enableIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE); enableIntent.putExtra(BluetoothAdapter.EXTRA_DISCOVERABLE_DURATION, 300); startActivityForResult(enableIntent, REQUEST_ENABLE_BT);
1).BluetoothAdapter 就代表本地蓝牙设备,用getDefaultAdapter()获取本地蓝牙设备,其返回值如果为空表示不存在蓝牙设备,否则就说明存在蓝牙设备
2).用isEnabled()方法来确定蓝牙设备是否打开,若没有打开返回值为false,需要重新调用startActivityForResult(enableIntent, REQUEST_ENABLE_BT);方法来打开蓝牙设备
3.扫描设备
主要通过获取本地的BluetoothAdapter 进行扫描和停止扫描,通过BluetoothAdapter 的回调返回扫描到的结果
回调
private BluetoothAdapter.LeScanCallback mLeScanCallback = new BluetoothAdapter.LeScanCallback() { @Override public void onLeScan(final BluetoothDevice device, int rssi, byte[] scanRecord) { if (device != null){ Log.e("device.getName()",""+device.getName());//设备名称 Log.e("device.getName()",""+device.getAddress());//设备地址 };
扫描/停止扫描
/** * 搜索蓝牙或停止搜索 * @param enable */ @SuppressLint("NewApi") 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);//SCAN_PERIOD=扫描时间 mScanning = true; mBluetoothAdapter.startLeScan(mLeScanCallback); } else { mScanning = false; mBluetoothAdapter.stopLeScan(mLeScanCallback); } }
连接设备和断开连接
一般我会把连接设备/断开连接设备,设置服务的读/写/通知通道 操作读/写/接受通知, 操作读/写/接受通知这三个步骤放在Service,这样能更好的脱离界面做到随处可操作。
获取BluetoothAdapter 里的BluetoothDevice,通过BluetoothDevice和之前扫描得到的设备地址进行连接。通过BluetoothGatt回调返回结果,BluetoothGatt这个比较关键之后所有的操作都通过它返回结果
获取BluetoothAdapter
/** * 初始化蓝牙服务 * @return */ public boolean initialize() { // For API level 18 and above, get a reference to BluetoothAdapter // through // BluetoothManager. if (mBluetoothManager == null) { mBluetoothManager = (BluetoothManager) getSystemService(Context.BLUETOOTH_SERVICE); if (mBluetoothManager == null) { Log.e(TAG, "Unable to initialize BluetoothManager."); return false; } } mBluetoothAdapter = mBluetoothManager.getAdapter(); if (mBluetoothAdapter == null) { Log.e(TAG, "Unable to obtain a BluetoothAdapter."); return false; } return true; }
获取BluetoothDevice并连接
/** * 请求连接 */ public boolean connect(final String address) { if (mBluetoothAdapter == null || address == null) { Log.e(TAG,"BluetoothAdapter not initialized or unspecified address."); return false; } // Previously connected device. Try to reconnect. (先前连接的设备。 尝试重新连接) if (mBluetoothDeviceAddress != null&& address.equals(mBluetoothDeviceAddress) && mBluetoothGatt != null) { if (mBluetoothGatt.connect()) { 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; } mBluetoothGatt = device.connectGatt(this, false, mGattCallback); return true; }
BluetoothGatt类
BluetoothGatt 是我们用的最多,她就像Android手机与BLE终端设备建立通信的一个大的管道,只有有了这个管道,我们才有了通信的前提所有的数据读写,通知等通道都是在这个管道传输。
BluetoothGatt 类
private final BluetoothGattCallback mGattCallback = new BluetoothGattCallback() { @Override public void onConnectionStateChange(BluetoothGatt gatt, int status,int newState) { if (newState == BluetoothProfile.STATE_CONNECTED) { //连接成功 mBluetoothGatt.discoverServices(); //连接成功后就去找出该设备中的服务(这方法比较重要必须调) BleConnetInter.sendCallBack(1); Log.e(TAG,"Ble connect suess"); } else if (newState == BluetoothProfile.STATE_DISCONNECTED) { //连接失败 BleConnetInter.sendCallBack(3); Log.e(TAG,"Ble connect fail"); } } @Override public void onServicesDiscovered(BluetoothGatt gatt, int status) { if (status == BluetoothGatt.GATT_SUCCESS) { Log.e(TAG,"onServicesDiscovered sucess"); displayGattServices();//发现服务回调,在这设置服务,读,写,通知通道 } else { } Log.e(TAG, "onServicesDiscovered received: " + status); } @Override public void onCharacteristicRead(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) { System.out.println("onCharacteristicRead"); if (status == BluetoothGatt.GATT_SUCCESS) { //读通道返回数据 } } @Override public void onDescriptorWrite(BluetoothGatt gatt, BluetoothGattDescriptor descriptor, int status) { //写通道 } @SuppressLint("LongLogTag") @Override public void onCharacteristicChanged(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) { // byte[] data = characteristic.getValue();//接受终端返回的通知数据 if (data != null && data.length>0){ ParaseBleData(data);//解析数据 } } @Override public void onReadRemoteRssi(BluetoothGatt gatt, int rssi, int status) { System.out.println("rssi = " + rssi); } public void onCharacteristicWrite(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) { System.out.println("--------write success----- status:" + status); }; };
设置服务的读写通知通道
首先了解些这些关键词,一个BLE终端可以包含多个Service, 一个Service可以包含多个Characteristic,每个Characteristic可以说是一个通道。Characteristic是比较重要的,也就是我们所说的通信通道,是手机与BLE终端交换数据的关键,读取设置数据等操作都是操作Characteristic的相关属性。
每个Service有一个唯一的UUID,每个Characteristic也都有一个唯一的UUID,我们就是通过硬件工程师提供的BLE终端的UUID确定我们需要的Service和Characteristic
当连接上设备室 BluetoothGatt 类会回到连接成功的通知,然后掉发现服务的方法BluetoothGatt.discoverServices(); (上面BluetoothGatt 例子里有代码提到),发现服务后在BluetoothGatt 里也有回调,在回调里获取和解析Service和Characteristic,这时我们需要根据提供的UUID去确定我们需要的Service,Characteristic
/** *分析服务列表 */ public void displayGattServices(){ List<BluetoothGattService> listServices = mBluetoothGatt.getServices() for (BluetoothGattService service : listServices){ if (service != null){ Log.e(TAG,""+service.getUuid());//获取服务的UUID if (service.getUuid().toString().equals(HANDBAND_SERVER)){//UUID比对 ArrayList<BluetoothGattCharacteristic> mGattCharacteristics = (ArrayList<BluetoothGattCharacteristic>) service.getCharacteristics();//获取Characteristics for (BluetoothGattCharacteristic characteristics : mGattCharacteristics){ if (characteristics != null){ if (HANDBAND_WRITE.equals(characteristics.getUuid().toString())){//比对写通道(characteristics) // Log.e(TAG,"if characteristicsUUID:"+mGattCharacteristics.size()); writeBluetoothGattCharacteristic = characteristics; }else if (HANDBAND_NOTIFICATION.equals(characteristics.getUuid().toString())){//比对通知通道(characteristics) notifyBluetoothGattCharacteristic = characteristics; //setCharacteristicNotification(true); setNotify(notifyBluetoothGattCharacteristic);//设置通知通道 setEnableNotify(notifyBluetoothGattCharacteristic, true);////设置通知通道 } } Log.e(TAG,"for characteristicsUUID:"+characteristics.getUuid());//service.getUuid(); } } } } }
设置通知通道
// 设置可通知 public boolean setNotify(BluetoothGattCharacteristic data_char) { if (!mBluetoothAdapter.isEnabled()) { // 没有打开蓝牙 return false; } if (data_char == null) { return false; } if (0 != (data_char.getProperties() & BluetoothGattCharacteristic.PROPERTY_NOTIFY)) { // 查看是否带有可通知属性 mBluetoothGatt.setCharacteristicNotification(data_char, true); BluetoothGattDescriptor descriptor = data_char.getDescriptor(UUID .fromString(CLIENT_CHARACTERISTIC_CONFIG)); // descriptor .setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE); mBluetoothGatt.writeDescriptor(descriptor); }else if (0 != (data_char.getProperties() & BluetoothGattCharacteristic.PROPERTY_INDICATE)){ mBluetoothGatt.setCharacteristicNotification(data_char, true); BluetoothGattDescriptor descriptor = data_char.getDescriptor(UUID .fromString(CLIENT_CHARACTERISTIC_CONFIG)); // descriptor .setValue(BluetoothGattDescriptor.ENABLE_INDICATION_VALUE); mBluetoothGatt.writeDescriptor(descriptor); } return true; }
// 设置允许通知 public boolean setEnableNotify(BluetoothGattCharacteristic data_char, boolean enable) { if (!mBluetoothAdapter.isEnabled()) { // 没有打开蓝牙 return false; } if (data_char == null) { return false; } mBluetoothGatt.setCharacteristicNotification(data_char, enable); return true; }
/** * 设置可读 * @param characteristic */ public void readCharacteristic(BluetoothGattCharacteristic characteristic) { if (mBluetoothAdapter == null || mBluetoothGatt == null) { Log.w(TAG, "BluetoothAdapter not initialized"); return; } mBluetoothGatt.readCharacteristic(characteristic); }
4. 操作读/写/接受通知
1.写操作
在发现和解析服务里我们获取到写的Characteristic,通过Characteristic进行对BLE终端设备写的操作
/** * 写操作 * @param */ public synchronized void wirteCharacteristic(byte[] data) { if (mBluetoothAdapter == null || mBluetoothGatt == null || writeBluetoothGattCharacteristic == null) { Log.w(TAG, "BluetoothAdapter not initialized"); return; } writeBluetoothGattCharacteristic.setValue(data); Log.e("wirteCharacteristic",""+writeBluetoothGattCharacteristic.getValue()); String dataStr = ""; for(int i =0;i<data.length;i++){ dataStr =dataStr+" , "+data[i]; } Log.e(TAG,"data:"+dataStr); mBluetoothGatt.writeCharacteristic(writeBluetoothGattCharacteristic); }
2.读/接受通知的消息都是在BluetoothGatt类里返回。
到此Ble 的基本 操作已完成,蓝牙说白了也就是一个信息传输通道,只要掌握了这四个步骤也就可以对她进行操作,无非就是扫描,连接,向终端设备写指令,接受终端指令。
在这我也跟家分享下在开发过程中遇到小坑,我遇到一个坑就是硬件工程师提供的指令协议不正确,因为硬件方面我们是购买别人的成品,他们给我的协议没更新,我按协议给终端发送个设置信息的指令,终端返回成功,通过查询指令查出来结果也是跟我设置的结果一样,但设置就是无效,将协议跟指令来回比对也没发现问题,因为这个设置涉及到几个步骤又来回测试步骤,最后都没能成功,因为设备是购买的成品不能连调,,但他们有成品的APK,通过他们APk设置完成没问题,这是怀疑是不是协议出问题,但不能确定,购买的成品沟通起来也麻烦,最后把发的指令和返回的指令一一列出来,做出个文档让他们比对下最后发现问题,,协议更改了没跟新。其中不想求别人最后是用抓包工具,其中朋友推荐了几篇蓝牙抓包文章,还没研究好,研究好再写出来分享。
- Android 蓝牙4.0使用心得
- IOS蓝牙4.0使用心得
- android 蓝牙4.0开发心得(一)蓝牙搜索
- Android蓝牙4.0的使用
- 关于蓝牙4.0及WebSocket的使用心得
- 关于蓝牙4.0及WebSocket的使用心得
- Android 蓝牙 一对一聊天APP 心得
- iOS蓝牙链接打印机的使用心得
- Android---蓝牙的使用
- Android中使用蓝牙
- android蓝牙的使用
- Android:使用BLE蓝牙
- Android蓝牙使用小结
- android蓝牙的使用
- Android 蓝牙如何使用
- android蓝牙简单使用
- Android蓝牙使用(一)
- Android蓝牙使用(一)
- logging知识点小结
- Mybatis-动态sql-17
- selenium在远程服务器上的虚拟显示
- 欢迎使用CSDN-markdown编辑器
- supervisor的配置与使用
- Android 蓝牙4.0使用心得
- mongodb设置远程连接
- peewee中创建自己的Field
- Orange Pi PC试用体验】从Mac远程登录图形界面
- vitualenvwrapper的常用命令
- 在spring boot中log4j2编程式配置(Programmatic Configuration)步骤
- startActivityForResult和setResult详解
- ruby的安装
- 语义化版本编号