Android蓝牙通信之搜索蓝牙设备
一:注意事项
1:android6.0使用蓝牙时,需要开启gps定位权限,不然无法搜索其它蓝牙设备。
二:权限
1:权限配置
<!--允许程序连接到已配对的蓝牙设备--> <uses-permission android:name="android.permission.bluetooth" /> <!-- 允许程序发现和配对蓝牙设备 --> <uses-permission android:name="android.permission.bluetooth_admin" /> <!--android 6.0 涉及到的权限--> <uses-permission android:name="android.permission.access_fine_location" /> <uses-permission android:name="android.permission.access_coarse_location" /> <!-- 在sdcard中创建与删除文件的权限 --> <uses-permission android:name="android.permission.mount_unmount_filesystems"/> <!-- 往sdcard写入数据的权限 --> <uses-permission android:name="android.permission.write_external_storage"/>
2:动态权限代码
由于需要用到存储卡,定位等,android6.0以上需要代码动态设置。
a)获取定位设置
if (build.version.sdk_int >= 23) { boolean islocat = islocationopen(getapplicationcontext()); toast.maketext(mcontext, "islo:" + islocat, toast.length_long).show(); //开启位置服务,支持获取ble蓝牙扫描结果 if (!islocat) { intent enablelocate = new intent(settings.action_location_source_settings); startactivityforresult(enablelocate, 1); } } /** * 判断位置信息是否开启 * * @param context * @return */ private static boolean islocationopen(final context context) { locationmanager manager = (locationmanager) context.getsystemservice(context.location_service); //gps定位 boolean isgpsprovider = manager.isproviderenabled(locationmanager.gps_provider); //网络定位 boolean isnetworkprovider = manager.isproviderenabled(locationmanager.network_provider); return isgpsprovider || isnetworkprovider; }
b)存储卡权限设置
if (build.version.sdk_int >= 23) { int write = checkselfpermission(manifest.permission.write_external_storage); int read = checkselfpermission(manifest.permission.read_external_storage); //动态请求读写sd卡权限 if (write != packagemanager.permission_granted || read != packagemanager.permission_granted) { requestpermissions(new string[]{manifest.permission.write_external_storage, manifest.permission.read_external_storage}, sd_card); } }
然后通过onrequestpermissionsresult()方法获取动态权限的结果:
@override public void onrequestpermissionsresult(int requestcode, string[] permissions, int[] grantresults) { switch (requestcode){ case sd_card: if(grantresults.length>0&&grantresults[0] == packagemanager.permission_granted){ //允许访问 }else{ toast.maketext(mcontext,"您拒绝了程序访问存储卡",toast.length_long).show(); } break; case coares_location: break; } }
三:蓝牙搜索
android.bluetooth.bluetoothadapter 是蓝牙开发用得比较多,并且比较重要的一个类,可以设备蓝牙名称,打开,关闭,搜索等常规操作。
1 蓝牙打开,以及搜索
蓝牙打开和关闭信息使用bluetoothadapter.action_state_changed去接收广播
bluetoothadapter mbluetoothadapter = bluetoothadapter.getdefaultadapter(); mbluetoothadapter.setname("bluetestphone"); //判断蓝牙是否打开 boolean originalbluetooth = (mbluetoothadapter != null && mbluetoothadapter.isenabled()); if (originalbluetooth) { mbluetoothadapter.startdiscovery(); } else if (originalbluetooth == false) { mbluetoothadapter.enable(); }
蓝牙打开后,我们可以获取设备的蓝牙信息
stringbuilder sb = new stringbuilder(); //获取本机蓝牙名称 string name = mbluetoothadapter.getname(); //获取本机蓝牙地址 string address = mbluetoothadapter.getaddress();
搜索完成后,通过bluetoothdevice.action_found广播去接收结果,广播代码如下(注意:可能出现设备搜索不到的情况,设备需要开启允许周围设备搜索,或者通过程序来控制允许搜索的时间范围)
/*确保蓝牙被发现,在荣耀8手机上,设置了还是默认的2分钟,所以以下几句代码程序中没有,*/ intent discoverableintent = new intent(bluetoothadapter.action_request_discoverable); //设置可见状态的持续时间为300秒,但是最多是300秒 discoverableintent.putextra(bluetoothadapter.extra_discoverable_duration, 300); startactivityforresult(discoverableintent, request_discoverable_bluetooth); private void initsearchbroadcast() { intentfilter intentfilter = new intentfilter(); //发现设备 intentfilter.addaction(bluetoothdevice.action_found); //设备配对状态改变 intentfilter.addaction(bluetoothdevice.action_bond_state_changed); //蓝牙设备状态改变 intentfilter.addaction(bluetoothadapter.action_state_changed); //开始扫描 intentfilter.addaction(bluetoothadapter.action_discovery_started); //结束扫描 intentfilter.addaction(bluetoothadapter.action_discovery_finished); //其它设备请求配对 intentfilter.addaction(action_pairing_request); //intentfilter.addaction(bluetoothadapter.connection_state_changed); registerreceiver(bluetoothreceiver, intentfilter); } private broadcastreceiver bluetoothreceiver = new broadcastreceiver() { @override public void onreceive(context context, intent intent) { string action = intent.getaction(); logger.e(tag + "mbluetoothreceiver action =" + action); try { if (bluetoothadapter.action_discovery_started.equals(action)) {//开始扫描 setprogressbarindeterminatevisibility(true); log1.settext("正在扫描设备,请稍候..."); } else if (bluetoothadapter.action_discovery_finished.equals(action)) {//结束扫描 logger.e(tag + "设备搜索完毕"); setprogressbarindeterminatevisibility(false); log1.settext("扫描完成"); bondadapter.notifydatasetchanged(); unbondadapter.notifydatasetchanged(); scanstatus = false; } else if (bluetoothdevice.action_found.equals(action)) {//发现设备 finddevice(intent); } else if (bluetoothdevice.action_bond_state_changed.equals(action)) {//蓝牙配对状态的广播 bluetoothdevice device = intent.getparcelableextra(bluetoothdevice.extra_device); logger.e(tag + device.getname() + "蓝牙配对广播:" + device.getbondstate()); switch (device.getbondstate()) { case bluetoothdevice.bond_bonding: logger.e(tag + device.getname() + "蓝牙配对广播 正在配对......"); break; case bluetoothdevice.bond_bonded: logger.e(tag + device.getname() + "蓝牙配对广播 完成配对,本机自动配对"); bonddevices.add(device); unbonddevices.remove(device); bondadapter.notifydatasetchanged(); unbondadapter.notifydatasetchanged(); break; case bluetoothdevice.bond_none: logger.e(tag + device.getname() + "蓝牙配对广播 取消配对"); unbonddevices.add(device); bonddevices.remove(device); unbondadapter.notifydatasetchanged(); bondadapter.notifydatasetchanged(); default: break; } } else if (action.equals(action_pairing_request)) {//其它设备蓝牙配对请求 bluetoothdevice btdevice = intent.getparcelableextra(bluetoothdevice.extra_device); int state = intent.getintextra(bluetoothdevice.extra_bond_state, bluetoothdevice.bond_none); //当前的配对的状态 try { string path = environment.getexternalstoragedirectory() + "/bluetest/"; string devicename = btdevice.getname(); logger.e(tag + "蓝牙 匹配信息:" + devicename + "," + btdevice.getaddress() + ",state:" + state); //1.确认配对,高版本无效,蓝牙配对不是zuk的问题,而是安卓6.0的bug,凡是遇到蓝牙适配问题的,请同时打开蓝牙和定位,再去配对,基本90%都没有问题了。 object object = clsutils.setpairingconfirmation(btdevice.getclass(), btdevice, true); //2.终止有序广播,如果没有将广播终止,则会出现一个一闪而过的配对框。 abortbroadcast(); //3.调用setpin方法进行配对... boolean ret = clsutils.setpin(btdevice.getclass(), btdevice, pwd); } catch (exception e) { // todo auto-generated catch block e.printstacktrace(); toast.maketext(mcontenxt, "error:" + btdevice + "," + state, toast.length_long).show(); } } else if (action.equals(bluetoothadapter.action_state_changed)) {//蓝牙开关状态 // bluetoothdevice device = intent.getparcelableextra(bluetoothdevice.extra_device); int statue = mbluetoothadapter.getstate(); switch (statue) { case bluetoothadapter.state_off: logger.e("蓝牙状态:,蓝牙关闭"); clsutils.closediscoverabletimeout(mbluetoothadapter); break; case bluetoothadapter.state_on: logger.e("蓝牙状态:,蓝牙打开"); clsutils.setdiscoverabletimeout(1000 * 60, mbluetoothadapter); scanbluetooth(); break; case bluetoothadapter.state_turning_off: logger.e("蓝牙状态:,蓝牙正在关闭"); mbluetoothadapter.canceldiscovery(); break; case bluetoothadapter.state_turning_on: logger.e("蓝牙状态:,蓝牙正在打开"); break; } } } catch (exception e) { e.printstacktrace(); } } }; //发现设备的代码如下 private void finddevice(intent intent) throws exception{ //获取到设备对象 bluetoothdevice device = intent.getparcelableextra(bluetoothdevice.extra_device); string str = device.getname() + "|" + device.getaddress(); logger.e("扫描到设备:" + str); if (device.getbondstate() == bluetoothdevice.bond_bonded) {//判断当前设备地址下的device是否已经配对 if (!bonddevices.contains(device)) { bonddevices.add(device); } } else { if (!unbonddevices.contains(device)) { unbonddevices.add(device); } if (device.getname().equals(test_device_name)) { boolean bondstatus = clsutils.createbond(device.getclass(), device); logger.i(tag + " bondstatus:" + bondstatus); } } log.e("error", "搜索完毕,准备刷新!"); bondadapter.notifydatasetchanged(); unbondadapter.notifydatasetchanged(); }
四:蓝牙配对
正常情况下,蓝牙匹配需要弹出一个匹配确认框,如下图,但我想实现的是,匹配其中一方,不能手动点击配对,因为发起蓝牙连接的设备是android设备,是不能触摸的,所以就要通过程序来解决这个问题,特别声明:(测试的android设备,版本为5.x,并且已经root,没有root的设备,或者不是android5.x不清楚能否实现自动匹配,因为我只有这个测试设备)。
1 当我们搜索到目标手机的蓝牙后,android设备主动发起连接请求,代码如下
if (device.getname().equals(test_device_name)) { boolean bondstatus = clsutils.createbond(device.getclass(), device); logger.i(tag + " bondstatus:" + bondstatus); } //发起蓝牙匹配请求 public boolean createbond(class btclass, bluetoothdevice btdevice) throws exception { method createbondmethod = btclass.getmethod("createbond"); boolean returnvalue = (boolean) createbondmethod.invoke(btdevice); return returnvalue.booleanvalue(); }
2 当被匹配方点击配对后,系统会通过bluetoothdevice.action_bond_state_changed广播告诉android设备,此时android设备就可以自动确认,通过这个流程来完成整个蓝牙的配对,具体代码如下
bluetoothdevice btdevice = intent.getparcelableextra(bluetoothdevice.extra_device); int state = intent.getintextra(bluetoothdevice.extra_bond_state, bluetoothdevice.bond_none); //当前的配对的状态 try { string path = environment.getexternalstoragedirectory() + "/bluetest/"; string devicename = btdevice.getname(); logger.e(tag + "蓝牙 匹配信息:" + devicename + "," + btdevice.getaddress() + ",state:" + state); if(devicename.equals(test_device_name)){//test_device_name 为被匹配蓝牙设备的名称,自己手动定义 object object = clsutils.setpairingconfirmation(btdevice.getclass(), btdevice, true); abortbroadcast(); boolean ret = clsutils.setpin(btdevice.getclass(), btdevice, pwd); } } catch (exception e) { // todo auto-generated catch block e.printstacktrace(); toast.maketext(mcontenxt, "error:" + btdevice + "," + state, toast.length_long).show(); } //确认配对 public object setpairingconfirmation(class<?> btclass, bluetoothdevice device, boolean isconfirm) throws exception { method setpairingconfirmation = btclass.getdeclaredmethod("setpairingconfirmation", boolean.class); object object = setpairingconfirmation.invoke(device, isconfirm); return object; } //配对需要调用的方法 public boolean setpin(class<? extends bluetoothdevice> btclass, bluetoothdevice btdevice, string str) throws exception { try { method removebondmethod = btclass.getdeclaredmethod("setpin", new class[] {byte[].class}); boolean returnvalue = (boolean) removebondmethod.invoke(btdevice, new object[] {str.getbytes()}); log.e("returnvalue", "" + returnvalue); } catch (securityexception e) { // throw new runtimeexception(e.getmessage()); e.printstacktrace(); } catch (illegalargumentexception e) { // throw new runtimeexception(e.getmessage()); e.printstacktrace(); } catch (exception e) { // todo auto-generated catch block e.printstacktrace(); } return true; }
到目前为止,蓝牙权限,以及动态权限,蓝牙的打开,关闭,搜索,以及自动配对(特别声明:(自动配对的android设备,版本为5.x,并且已经root,没有root的设备,或者不是android5.x不清楚能否实现自动匹配,因为我只有这个测试设备)。)代码至此结束。
demo代码下载:
总结
以上所述是小编给大家介绍的android蓝牙通信之搜索蓝牙设备,希望对大家有所帮助
推荐阅读
-
Android蓝牙通信之搜索蓝牙设备
-
Native.js获取监听开关等操作Android蓝牙设备实例代码
-
Android实现蓝牙的搜索,配对(不需要输入PIN,自动匹配),连接,通信
-
Android实现蓝牙(BlueTooth)设备检测连接
-
Android提高之蓝牙传感应用实例
-
Android提高之蓝牙隐藏API探秘
-
Android设备蓝牙连接扫描枪获取扫描内容
-
Android Studio 基础 之 获取蓝牙Bluetooth 的状态,设置的蓝牙Bluetooth 的开关状态,并监听蓝牙Bluetooth 的状态变化方法整理
-
Android BLE定位应用(蓝牙搜索方法)
-
小程序和低功耗蓝牙设备通信,有时候收不到返回值的问题