Android提高之Android手机与BLE终端通信
最近穿戴设备发展得很火,把相关技术也带旺了,其中一项是BLE(BluetoothLowEnergy)。BLE是蓝牙4.0的核心Profile,主打功能是快速搜索,快速连接,超低功耗保持连接和传输数据,弱点是数据传输速率低,由于BLE的低功耗特点,因此普遍用于穿戴设备。Android4.3才开始支持BLEAPI,所以请各位客官把本文代码运行在蓝牙4.0和Android4.3及其以上的系统,另外本文所用的BLE终端是一个蓝牙4.0的串口蓝牙模块。
注:笔者的i9100刷了4.4系统后,竟然也能跟BLE蓝牙模块通信。
BLE分为三部分Service、Characteristic、Descriptor,这三部分都由UUID作为唯一标示符。一个蓝牙4.0的终端可以包含多个Service,一个Service可以包含多个Characteristic,一个Characteristic包含一个Value和多个Descriptor,一个Descriptor包含一个Value。一般来说,Characteristic是手机与BLE终端交换数据的关键,Characteristic有较多的跟权限相关的字段,例如PERMISSION和PROPERTY,而其中最常用的是PROPERTY,本文所用的BLE蓝牙模块竟然没有标准的Characteristic的PERMISSION。Characteristic的PROPERTY可以通过位运算符组合来设置读写属性,例如READ|WRITE、READ|WRITE_NO_RESPONSE|NOTIFY,因此读取PROPERTY后要分解成所用的组合(本文代码已含此分解方法)。
接下来贴出本文运行的结果,首先是连接BLE设备后,枚举出设备所有Service、Characteristic、Descriptor,并且
04-2118:28:25.465:E/DeviceScanActivity(12254):-->servicetype:PRIMARY
04-2118:28:25.465:E/DeviceScanActivity(12254):-->includedServicessize:0
04-2118:28:25.465:E/DeviceScanActivity(12254):-->serviceuuid:00001800-0000-1000-8000-00805f9b34fb
04-2118:28:25.465:E/DeviceScanActivity(12254):---->charuuid:00002a00-0000-1000-8000-00805f9b34fb
04-2118:28:25.465:E/DeviceScanActivity(12254):---->charpermission:UNKNOW
04-2118:28:25.465:E/DeviceScanActivity(12254):---->charproperty:READ
04-2118:28:25.465:E/DeviceScanActivity(12254):---->charuuid:00002a01-0000-1000-8000-00805f9b34fb
04-2118:28:25.470:E/DeviceScanActivity(12254):---->charpermission:UNKNOW
04-2118:28:25.470:E/DeviceScanActivity(12254):---->charproperty:READ
04-2118:28:25.470:E/DeviceScanActivity(12254):---->charuuid:00002a02-0000-1000-8000-00805f9b34fb
04-2118:28:25.470:E/DeviceScanActivity(12254):---->charpermission:UNKNOW
04-2118:28:25.470:E/DeviceScanActivity(12254):---->charproperty:READ|WRITE|
04-2118:28:25.470:E/DeviceScanActivity(12254):---->charuuid:00002a03-0000-1000-8000-00805f9b34fb
04-2118:28:25.470:E/DeviceScanActivity(12254):---->charpermission:UNKNOW
04-2118:28:25.475:E/DeviceScanActivity(12254):---->charproperty:READ|WRITE|
04-2118:28:25.475:E/DeviceScanActivity(12254):---->charuuid:00002a04-0000-1000-8000-00805f9b34fb
04-2118:28:25.475:E/DeviceScanActivity(12254):---->charpermission:UNKNOW
04-2118:28:25.475:E/DeviceScanActivity(12254):---->charproperty:READ
04-2118:28:25.475:E/DeviceScanActivity(12254):-->servicetype:PRIMARY
04-2118:28:25.475:E/DeviceScanActivity(12254):-->includedServicessize:0
04-2118:28:25.475:E/DeviceScanActivity(12254):-->serviceuuid:00001801-0000-1000-8000-00805f9b34fb
04-2118:28:25.480:E/DeviceScanActivity(12254):---->charuuid:00002a05-0000-1000-8000-00805f9b34fb
04-2118:28:25.480:E/DeviceScanActivity(12254):---->charpermission:UNKNOW
04-2118:28:25.480:E/DeviceScanActivity(12254):---->charproperty:INDICATE
04-2118:28:25.480:E/DeviceScanActivity(12254):-------->descuuid:00002902-0000-1000-8000-00805f9b34fb
04-2118:28:25.480:E/DeviceScanActivity(12254):-------->descpermission:UNKNOW
04-2118:28:25.480:E/DeviceScanActivity(12254):-->servicetype:PRIMARY
04-2118:28:25.480:E/DeviceScanActivity(12254):-->includedServicessize:0
04-2118:28:25.480:E/DeviceScanActivity(12254):-->serviceuuid:0000ffe0-0000-1000-8000-00805f9b34fb
04-2118:28:25.480:E/DeviceScanActivity(12254):---->charuuid:0000ffe1-0000-1000-8000-00805f9b34fb
04-2118:28:25.480:E/DeviceScanActivity(12254):---->charpermission:UNKNOW
04-2118:28:25.480:E/DeviceScanActivity(12254):---->charproperty:READ|WRITE_NO_RESPONSE|NOTIFY|
04-2118:28:25.490:E/DeviceScanActivity(12254):-------->descuuid:00002902-0000-1000-8000-00805f9b34fb
04-2118:28:25.490:E/DeviceScanActivity(12254):-------->descpermission:UNKNOW
04-2118:28:25.490:E/DeviceScanActivity(12254):-------->descuuid:00002901-0000-1000-8000-00805f9b34fb
04-2118:28:25.490:E/DeviceScanActivity(12254):-------->descpermission:UNKNOW
这里红字是由BluetoothGattCallback的onCharacteristicRead()回调而打出Log
以下Log是PC上的串口工具通过BLE模块发送过来,由BluetoothGattCallback的onCharacteristicChanged()打出Log
04-2118:30:18.260:E/DeviceScanActivity(12254):onCharWriteBLEDEVICEwrite0000ffe1-0000-1000-8000-00805f9b34fb->senddatatophone
04-2118:30:18.745:E/DeviceScanActivity(12254):onCharWriteBLEDEVICEwrite0000ffe1-0000-1000-8000-00805f9b34fb->senddatatophone
04-2118:30:19.085:E/DeviceScanActivity(12254):onCharWriteBLEDEVICEwrite0000ffe1-0000-1000-8000-00805f9b34fb->senddatatophone
04-2118:30:19.350:E/DeviceScanActivity(12254):onCharWriteBLEDEVICEwrite0000ffe1-0000-1000-8000-00805f9b34fb->senddatatophone
04-2118:30:19.605:E/DeviceScanActivity(12254):onCharWriteBLEDEVICEwrite0000ffe1-0000-1000-8000-00805f9b34fb->senddatatophone
04-2118:30:19.835:E/DeviceScanActivity(12254):onCharWriteBLEDEVICEwrite0000ffe1-0000-1000-8000-00805f9b34fb->senddatatophone
04-2118:30:20.055:E/DeviceScanActivity(12254):onCharWriteBLEDEVICEwrite0000ffe1-0000-1000-8000-00805f9b34fb->senddatatophone
04-2118:30:20.320:E/DeviceScanActivity(12254):onCharWriteBLEDEVICEwrite0000ffe1-0000-1000-8000-00805f9b34fb->senddatatophone
04-2118:30:20.510:E/DeviceScanActivity(12254):onCharWriteBLEDEVICEwrite0000ffe1-0000-1000-8000-00805f9b34fb->senddatatophone
04-2118:30:20.735:E/DeviceScanActivity(12254):onCharWriteBLEDEVICEwrite0000ffe1-0000-1000-8000-00805f9b34fb->senddatatophone
04-2118:30:21.000:E/DeviceScanActivity(12254):onCharWriteBLEDEVICEwrite0000ffe1-0000-1000-8000-00805f9b34fb->senddatatophone
接下来贴出本文核心代码:
publicclassDeviceScanActivityextendsListActivity{ privatefinalstaticStringTAG=DeviceScanActivity.class.getSimpleName(); privatefinalstaticStringUUID_KEY_DATA="0000ffe1-0000-1000-8000-00805f9b34fb"; privateLeDeviceListAdaptermLeDeviceListAdapter; /**搜索BLE终端*/ privateBluetoothAdaptermBluetoothAdapter; /**读写BLE终端*/ privateBluetoothLeClassmBLE; privatebooleanmScanning; privateHandlermHandler; //Stopsscanningafter10seconds. privatestaticfinallongSCAN_PERIOD=10000; @Override publicvoidonCreate(BundlesavedInstanceState){ super.onCreate(savedInstanceState); getActionBar().setTitle(R.string.title_devices); mHandler=newHandler(); //UsethischecktodeterminewhetherBLEissupportedonthedevice.Thenyoucan //selectivelydisableBLE-relatedfeatures. if(!getPackageManager().hasSystemFeature(PackageManager.FEATURE_BLUETOOTH_LE)){ Toast.makeText(this,R.string.ble_not_supported,Toast.LENGTH_SHORT).show(); finish(); } //InitializesaBluetoothadapter.ForAPIlevel18andabove,getareferenceto //BluetoothAdapterthroughBluetoothManager. finalBluetoothManagerbluetoothManager= (BluetoothManager)getSystemService(Context.BLUETOOTH_SERVICE); mBluetoothAdapter=bluetoothManager.getAdapter(); //ChecksifBluetoothissupportedonthedevice. if(mBluetoothAdapter==null){ Toast.makeText(this,R.string.error_bluetooth_not_supported,Toast.LENGTH_SHORT).show(); finish(); return; } //开启蓝牙 mBluetoothAdapter.enable(); mBLE=newBluetoothLeClass(this); if(!mBLE.initialize()){ Log.e(TAG,"UnabletoinitializeBluetooth"); finish(); } //发现BLE终端的Service时回调 mBLE.setOnServiceDiscoverListener(mOnServiceDiscover); //收到BLE终端数据交互的事件 mBLE.setOnDataAvailableListener(mOnDataAvailable); } @Override protectedvoidonResume(){ super.onResume(); //Initializeslistviewadapter. mLeDeviceListAdapter=newLeDeviceListAdapter(this); setListAdapter(mLeDeviceListAdapter); scanLeDevice(true); } @Override protectedvoidonPause(){ super.onPause(); scanLeDevice(false); mLeDeviceListAdapter.clear(); mBLE.disconnect(); } @Override protectedvoidonStop(){ super.onStop(); mBLE.close(); } @Override protectedvoidonListItemClick(ListViewl,Viewv,intposition,longid){ finalBluetoothDevicedevice=mLeDeviceListAdapter.getDevice(position); if(device==null)return; if(mScanning){ mBluetoothAdapter.stopLeScan(mLeScanCallback); mScanning=false; } mBLE.connect(device.getAddress()); } privatevoidscanLeDevice(finalbooleanenable){ if(enable){ //Stopsscanningafterapre-definedscanperiod. mHandler.postDelayed(newRunnable(){ @Override publicvoidrun(){ mScanning=false; mBluetoothAdapter.stopLeScan(mLeScanCallback); invalidateOptionsMenu(); } },SCAN_PERIOD); mScanning=true; mBluetoothAdapter.startLeScan(mLeScanCallback); }else{ mScanning=false; mBluetoothAdapter.stopLeScan(mLeScanCallback); } invalidateOptionsMenu(); } /** *搜索到BLE终端服务的事件 */ privateBluetoothLeClass.OnServiceDiscoverListenermOnServiceDiscover=newOnServiceDiscoverListener(){ @Override publicvoidonServiceDiscover(BluetoothGattgatt){ displayGattServices(mBLE.getSupportedGattServices()); } }; /** *收到BLE终端数据交互的事件 */ privateBluetoothLeClass.OnDataAvailableListenermOnDataAvailable=newOnDataAvailableListener(){ /** *BLE终端数据被读的事件 */ @Override publicvoidonCharacteristicRead(BluetoothGattgatt, BluetoothGattCharacteristiccharacteristic,intstatus){ if(status==BluetoothGatt.GATT_SUCCESS) Log.e(TAG,"onCharRead"+gatt.getDevice().getName() +"read" +characteristic.getUuid().toString() +"->" +Utils.bytesToHexString(characteristic.getValue())); } /** *收到BLE终端写入数据回调 */ @Override publicvoidonCharacteristicWrite(BluetoothGattgatt, BluetoothGattCharacteristiccharacteristic){ Log.e(TAG,"onCharWrite"+gatt.getDevice().getName() +"write" +characteristic.getUuid().toString() +"->" +newString(characteristic.getValue())); } }; //Devicescancallback. privateBluetoothAdapter.LeScanCallbackmLeScanCallback= newBluetoothAdapter.LeScanCallback(){ @Override publicvoidonLeScan(finalBluetoothDevicedevice,intrssi,byte[]scanRecord){ runOnUiThread(newRunnable(){ @Override publicvoidrun(){ mLeDeviceListAdapter.addDevice(device); mLeDeviceListAdapter.notifyDataSetChanged(); } }); } }; privatevoiddisplayGattServices(List<BluetoothGattService>gattServices){ if(gattServices==null)return; for(BluetoothGattServicegattService:gattServices){ //-----Service的字段信息-----// inttype=gattService.getType(); Log.e(TAG,"-->servicetype:"+Utils.getServiceType(type)); Log.e(TAG,"-->includedServicessize:"+gattService.getIncludedServices().size()); Log.e(TAG,"-->serviceuuid:"+gattService.getUuid()); //-----Characteristics的字段信息-----// List<BluetoothGattCharacteristic>gattCharacteristics=gattService.getCharacteristics(); for(finalBluetoothGattCharacteristicgattCharacteristic:gattCharacteristics){ Log.e(TAG,"---->charuuid:"+gattCharacteristic.getUuid()); intpermission=gattCharacteristic.getPermissions(); Log.e(TAG,"---->charpermission:"+Utils.getCharPermission(permission)); intproperty=gattCharacteristic.getProperties(); Log.e(TAG,"---->charproperty:"+Utils.getCharPropertie(property)); byte[]data=gattCharacteristic.getValue(); if(data!=null&&data.length>0){ Log.e(TAG,"---->charvalue:"+newString(data)); } //UUID_KEY_DATA是可以跟蓝牙模块串口通信的Characteristic if(gattCharacteristic.getUuid().toString().equals(UUID_KEY_DATA)){ //测试读取当前Characteristic数据,会触发mOnDataAvailable.onCharacteristicRead() mHandler.postDelayed(newRunnable(){ @Override publicvoidrun(){ mBLE.readCharacteristic(gattCharacteristic); } },500); //接受Characteristic被写的通知,收到蓝牙模块的数据后会触发mOnDataAvailable.onCharacteristicWrite() mBLE.setCharacteristicNotification(gattCharacteristic,true); //设置数据内容 gattCharacteristic.setValue("senddata->"); //往蓝牙模块写入数据 mBLE.writeCharacteristic(gattCharacteristic); } //-----Descriptors的字段信息-----// List<BluetoothGattDescriptor>gattDescriptors=gattCharacteristic.getDescriptors(); for(BluetoothGattDescriptorgattDescriptor:gattDescriptors){ Log.e(TAG,"-------->descuuid:"+gattDescriptor.getUuid()); intdescPermission=gattDescriptor.getPermissions(); Log.e(TAG,"-------->descpermission:"+Utils.getDescPermission(descPermission)); byte[]desData=gattDescriptor.getValue(); if(desData!=null&&desData.length>0){ Log.e(TAG,"-------->descvalue:"+newString(desData)); } } } }// } }
感兴趣的读者可以动手测试一下代码的运行情况,希望能对大家的Android项目开发有所帮助。
相关文章
- android系统开机画面_Android开机画面
- android galley实现画廊效果
- android开发连接手机usb调试模式,安卓手机usb调试在哪里 安卓手机usb调试模式设置教程…[通俗易懂]
- android studio gradle 版本更新
- 谷歌宣布Android将支持RISC-V指令集
- 【Android 逆向】IDA 工具使用 ( IDA 32 位 / 64 位 版本 | 汇编代码视图 IDA View-A | 字符串窗口 Strings window )
- 【Unity3D】Android 打包 ④ ( Android 工程打包 | Unity 中导出安卓工程 | Android Studio 打开 Unity 导出的 Android 工程 )
- Android DialogFragment全屏详解手机开发
- Android版本检测更新详解手机开发
- Android Volley网络通信框架的使用(二):Volley加载网络图片详解手机开发
- 36个Android开发常用代码片段详解手机开发
- [android] 常用数据适配器SimpleAdapter详解手机开发
- [android] listview入门详解手机开发
- [android] 点击事件的四种写法详解手机开发
- android自定义button点击效果详解手机开发
- Android热修复技术总结详解手机开发
- android守护进程详解手机开发
- Android动态化框架App Bundles详解手机开发
- Android设置透明效果的三种方法详解手机开发
- Android Studio 导入项目 出现安装Error:Cause: failed to find target with hash string ‘android-23’ 等错误详解手机开发
- 第一次通过AVD Manager创建了一个虚拟设备,但是在Android Studio运行程序时却无设备可选详解手机开发
- android虚线边框详解手机开发
- Android binder连接中,服务端如何知道客户端断开情况详解手机开发
- 谷歌正式推出Android 5.0 Lollipop
- Intel 展示了可在大屏幕显示 Linux 系统的低端 Android 手机
- 「唯物」把Windows和Android来Remix一下,就得出了这个东西
- android实现简单的画画板实例代码
- android开发教程之判断是手机还是平板的方法