这两天又在搞蓝牙,蓝牙伤我千百遍,我待蓝牙如初恋。

有位朋友说,做个appdemo,来和他的蓝牙模块进行交互。我发现我对蓝牙真的是连冰山一角都还没了解完。说说我都遇到了什么问题吧。

1.两个手机都打开蓝牙,如果离开设置蓝牙界面,难么你会发现你们都搜索不到彼此的设备。这不是你的错,这是谷歌的一个坑。

因为如下:

解决:

 //设置蓝牙可以被其他搜索到 blue.setScanMode(BluetoothAdapter.SCAN_MODE_CONNECTABLE_DISCOVERABLE);    //设置蓝牙可见的时间。0:是永久可见blue.setDiscoverableTimeout(0);//0:蓝牙永久可见

这个时候又郁闷了。BaseAdapter的源码里面setDiscoverableTimeout()和setScanMode()都被/*hide*/注释着。啊,是不是想要蓝牙现身有点曲折,我们想要用这两个方法还是有办法的。利用反射,调用这两个方法。

添加如下代码:

public void setDiscoverableTimeout(BluetoothAdapter adapter,int timeout) {try {Method setDiscoverableTimeout = BluetoothAdapter.class.getMethod("setDiscoverableTimeout", int.class);setDiscoverableTimeout.setAccessible(true);Method setScanMode =BluetoothAdapter.class.getMethod("setScanMode", int.class,int.class);setScanMode.setAccessible(true);setDiscoverableTimeout.invoke(adapter, timeout);setScanMode.invoke(adapter, BluetoothAdapter.SCAN_MODE_CONNECTABLE_DISCOVERABLE,timeout);} catch (Exception e) {e.printStackTrace();}}

这样就可以调用setDiscoverableTimeout()和setScanMode()方法了,就可以让其他设备扫描到我的设备了。

2.监听手机蓝牙是否被了另一个设备匹配。

我第一次用的是这个方法

    private BroadcastReceiver stateChangeRec=new BroadcastReceiver() {@Overridepublic void onReceive(Context context, Intent intent) {String action=intent.getAction();if(action.equals(BluetoothDevice.ACTION_BOND_STATE_CHANGED)){tv_bluetooth_status.setText("蓝牙连接状态:连接成功");Log.i("tang","连接成功");//do something}else if(action.equals(BluetoothDevice.ACTION_ACL_DISCONNECTED)){tv_bluetooth_status.setText("蓝牙连接状态:连接失败");Log.i("tang","连接失败");}}};

我会发现,在另外一台设备向我请求匹配的时候,就已经执行ACTION_BOND_STATE_CHANGED这个操作了。然后第一反应是绝对这个方法不适合我的情况。立刻重新找方法,

于是就得到了这个:

private BroadcastReceiver stateChangeRec=new BroadcastReceiver() {@Overridepublic void onReceive(Context context, Intent intent) {String action=intent.getAction();if(action.equals(BluetoothDevice.ACTION_BOND_STATE_CHANGED)){BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);if (device.getBondState() == BluetoothDevice.BOND_BONDING) {Log.i("tang","正在配对");} else if (device.getBondState() == BluetoothDevice.BOND_BONDED) {Log.i("tang","完成配对");tv_bluetooth_status.setText("蓝牙连接状态:连接成功");} else if (device.getBondState() == BluetoothDevice.BOND_NONE) {Log.i("tang","取消配对");}}}};

这个就是在两个设备已经匹配完成的一个完整步骤。老夫也是松了一口气。

3.还有一个,那就是修改本机蓝牙设备的名字。网上都说的是setName("xx")就好。我好你个香蕉巴拉。。。

bluetoothAdapter.enable();//设置蓝牙为可用bluetoothAdapter.setName(deviceName);//写入你想要设置的设备名字

setName其实是发送了一个广播,我们还需要来进行接收处理,完成整个操作。(很多博主都没说啊,坑死个人啊!)

这儿,我们需要发送一个改名字的广播!向全世界宣布,我的蓝牙我做主!

IntentFilter mFilter = new IntentFilter(BluetoothAdapter.ACTION_LOCAL_NAME_CHANGED);
mFilter.addAction(BluetoothAdapter.EXTRA_LOCAL_NAME);
registerReceiver(mReceiver, mFilter);

下面就是接受广播操作

//蓝牙广播接收器。修改名字时会调用。private BroadcastReceiver mReceiver = new BroadcastReceiver() {@Overridepublic void onReceive(Context context, Intent intent) {tv_current_name.setText("当前蓝牙名字:"+bluetoothAdapter.getName());String action = intent.getAction();if (BluetoothAdapter.ACTION_LOCAL_NAME_CHANGED.equals(action)){Toast.makeText(context,"成功", Toast.LENGTH_SHORT).show();BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);Set<BluetoothDevice> devices = bluetoothAdapter.getBondedDevices();for(int i=0; i<devices.size(); i++){BluetoothDevice device1 = (BluetoothDevice) devices.iterator().next();Log.i("tang","2-2蓝牙名字="+device1.getName());//System.out.println(device1.getName());}}else if(BluetoothAdapter.ACTION_DISCOVERY_FINISHED.equals(action)){Toast.makeText(context,"finished", Toast.LENGTH_SHORT).show();}}};

这才是一个ojbk的操作。

这里面还有一个坑,真的是没找到有博主说过这个问题。setName(“xxx”)这里面的值,是有限制的!

广播发送的字节好像是限制早<=60个字节,所以setName的值不是无限的,(如果你要广播发送包含了名字)。60个字节,30个汉字,包含所发送的其他信息,setName的值,限制也是跟随你所发送的信息多少而决定的。

当蓝牙名字>限制的个数的时候,广播发送会失败,ErrorCode=18或者1,对于BLE蓝牙的ErrorCode网上也没有人说,也搜索不到。我也不知道为啥。(有谁知道为啥蓝牙名字>5的时候广播开启会失败,且ErrorCode=18/1,麻烦留言告知一声,谢谢!)

4.还有个误区。我在断开蓝牙的时候使用的是disconnet(),但是我的小伙伴用他的手机和我测试的时候说,蓝牙没有断开啊。我说不可能啊,我又添加了close,双保险,这下没问题了吧。我小伙伴依然说,蓝牙没断开啊,一看。他看的是“已配对设备”,我特么操着键盘给他扔过去。

已配对,不代表蓝牙没断开,只是说明两个设备在下次连接的时候不需要再次配对了。大家测试的时候不要进入这个误区了。

持续更新一波,我app已经做好。小伙伴说搜索不到,让我把设备变成从设备。我这个蓝牙小白,一脸蒙圈,然后又去百度。设备分为主设备和从设备。

话不多说,到底如何把一个设备变成从设备呢。代码敬上:

这是一个设置广播,打开广播的操作。

private void sendOutSheBei() {if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.LOLLIPOP) {settings = new AdvertiseSettings.Builder().setAdvertiseMode(AdvertiseSettings.ADVERTISE_MODE_LOW_LATENCY) //广播模式: 低功耗,平衡,低延迟.setTxPowerLevel(AdvertiseSettings.ADVERTISE_TX_POWER_HIGH) //发射功率级别: 极低,低,中,高.setConnectable(true) //能否连接,广播分为可连接广播和不可连接广播.build();//广播数据(必须,广播启动就会发送)advertiseData = new AdvertiseData.Builder().setIncludeDeviceName(true) //包含蓝牙名称.setIncludeTxPowerLevel(true) //包含发射功率级别.addManufacturerData(1, new byte[]{23, 33}) //设备厂商数据,自定义.build();//扫描响应数据(可选,当客户端扫描时才发送)scanResponse = new AdvertiseData.Builder().addManufacturerData(2, new byte[]{66, 66}) //设备厂商数据,自定义.addServiceUuid(new ParcelUuid(UUID_SERVICE)) //服务UUID//      .addServiceData(new ParcelUuid(UUID_SERVICE), new byte[]{2}) //服务数据,自定义.build();mBluetoothLeAdvertiser = bluetoothAdapter.getBluetoothLeAdvertiser();}mBluetoothLeAdvertiser.startAdvertising(settings, advertiseData, scanResponse, mAdvertiseCallback);if (mBluetoothLeAdvertiser == null) {Toast.makeText(this, "该设备不支持蓝牙低功耗从设备通讯", Toast.LENGTH_SHORT).show();this.finish();return;}}

这里面有一点需要注意,.setIncludeDeviceName(true) //包含蓝牙名称  我开始以为这只是是否传递蓝牙名字的一个设置,我给它修改为.setIncludeDeviceName(false) ,别人就搜不到我的蓝牙了。你要是想让别人搜索到你的蓝牙,那你就老老实实的写为true。

这上面的代码还不够,还需要一个监听器,监听我们的广播是不是打开了。

// BLE广播Callbackprivate AdvertiseCallback mAdvertiseCallback = new AdvertiseCallback() {@Overridepublic void onStartSuccess(AdvertiseSettings settingsInEffect) {Log.i("tang","BLE广播开启成功");}@Overridepublic void onStartFailure(int errorCode) {Log.i("tang","BLE广播开启失败,错误码"+errorCode);}};

我们一定还想要和其他设备进行信息交互,哪里监听?第一段代码里面就有一个监听回调,好的,现在敬上回调监听代码:

 // BLE服务端Callbackprivate BluetoothGattServerCallback mBluetoothGattServerCallback = new BluetoothGattServerCallback() {@Overridepublic void onConnectionStateChange(BluetoothDevice device, int status, int newState) {Log.i("tang", "从设备连接状态="+String.format(status == 0 ? (newState == 2 ? "与[%s]连接成功" : "与[%s]连接断开") : ("与[%s]连接出错,错误码:" + status)));if(newState==2){//连接成功tv_bluetooth_status.setText("蓝牙连接状态:连接成功");mHandler.postDelayed(new Runnable() {@Overridepublic void run() {tv_bluetooth_status.setText("蓝牙连接状态:连接中断");Toast.makeText(mContext, "蓝牙断开", Toast.LENGTH_SHORT).show();mBluetoothLe.disconnect();mBluetoothLe.close();}}, getPostTime(deviceTime));}else{tv_bluetooth_status.setText("蓝牙连接状态:连接中断");}}@Overridepublic void onServiceAdded(int status, BluetoothGattService service) {Log.i("tang", "从设备连接状态="+String.format(status == 0 ? "添加服务[%s]成功" : "添加服务[%s]失败,错误码:" + status, service.getUuid()));}@Overridepublic void onCharacteristicReadRequest(BluetoothDevice device, int requestId, int offset, BluetoothGattCharacteristic characteristic) {String response = "CHAR_" + (int) (Math.random() * 100); //模拟数据mBluetoothGattServer.sendResponse(device, requestId, BluetoothGatt.GATT_SUCCESS, offset, response.getBytes());// 响应客户端Log.i("tang","客户端读取Characteristic[" + characteristic.getUuid() + "]:\n" + response);}@Overridepublic void onCharacteristicWriteRequest(BluetoothDevice device, int requestId, BluetoothGattCharacteristic characteristic, boolean preparedWrite, boolean responseNeeded, int offset, byte[] requestBytes) {// 获取客户端发过来的数据String requestStr = new String(requestBytes);Log.i("tang","客户端写入Characteristic[" + characteristic.getUuid() + "]:\n" + requestStr);}@Overridepublic void onDescriptorReadRequest(BluetoothDevice device, int requestId, int offset, BluetoothGattDescriptor descriptor) {String response = "DESC_" + (int) (Math.random() * 100); //模拟数据mBluetoothGattServer.sendResponse(device, requestId, BluetoothGatt.GATT_SUCCESS, offset, response.getBytes()); // 响应客户端Log.i("tang","客户端读取Descriptor[" + descriptor.getUuid() + "]:\n" + response);}@Overridepublic void onDescriptorWriteRequest(final BluetoothDevice device, int requestId, BluetoothGattDescriptor descriptor, boolean preparedWrite, boolean responseNeeded, int offset, byte[] value) {// 获取客户端发过来的数据String valueStr = Arrays.toString(value);mBluetoothGattServer.sendResponse(device, requestId, BluetoothGatt.GATT_SUCCESS, offset, value);// 响应客户端// 简单模拟通知客户端Characteristic变化if (Arrays.toString(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE).equals(valueStr)) { //是否开启通知final BluetoothGattCharacteristic characteristic = descriptor.getCharacteristic();new Thread(new Runnable() {@Overridepublic void run() {for (int i = 0; i < 5; i++) {SystemClock.sleep(3000);String response = "CHAR_" + (int) (Math.random() * 100); //模拟数据characteristic.setValue(response);mBluetoothGattServer.notifyCharacteristicChanged(device, characteristic, false);Log.i("tang","通知客户端改变Characteristic[" + characteristic.getUuid() + "]:\n" + response);//logTv("通知客户端改变Characteristic[" + characteristic.getUuid() + "]:\n" + response);}}}).start();}}@Overridepublic void onExecuteWrite(BluetoothDevice device, int requestId, boolean execute) {Log.i("tang",String.format("onExecuteWrite:%s,%s,%s,%s", device.getName(), device.getAddress(), requestId, execute));@Overridepublic void onNotificationSent(BluetoothDevice device, int status) {Log.i("tang",String.format("onNotificationSent:%s,%s,%s", device.getName(), device.getAddress(), status));}@Overridepublic void onMtuChanged(BluetoothDevice device, int mtu) {Log.i("tang",String.format("onMtuChanged:%s,%s,%s", device.getName(), device.getAddress(), mtu));}};

我做完了这些事之后,我发现,还有坑。我每次修改蓝牙本机名字之后,前几次没啥事儿,小伙伴突然给我说,不行了。一下回到解放前的名字了。我思考啊,思考啊。。。我觉得应该是发送广播里面设置名字的锅,它发广播只有一次,但是名字早给我发出去了,那我之后设置的不是都是打水漂?但是我仍然没把这一点想通,但是时间不等人啊,压力驱使我死马当活马医啊。

然后我就在每次修改名字的时候,发送一次广播,断开前一次的广播,重新发一次广播。很鸡肋,但是却有效,要是有小伙伴知道怎么处理,麻烦给我说一下。哎~但愿天堂没有bug。

不多就是这样,我遇到的就是这些坑。

基于BluetoothKit-蓝牙设备不可见,蓝牙方法不可调,蓝牙修改本机名字,蓝牙连接监听的问题相关推荐

  1. Dcloud HTML5 监听蓝牙设备 调用 原生安卓实现 - aspirant - 博客园

    最近一直搞Dcloud ,这是HTML5版本的开发,打包时候,可以打包成 apk 和ipa 分别运行在安卓和ios 机器上面, 但是这里面的资料很少,遇到问题,之后只能自己钻研总结, 现在有这么一个需 ...

  2. 监听返回app_基于 Redis 消息队列实现 Laravel 事件监听及底层源码探究

    在 Laravel 中,除了使用 dispatch 辅助函数通过 Illuminate\Bus\Dispatcher 显式推送队列任务外,还可以通过事件监听的方式隐式进行队列任务推送,在这个场景下,事 ...

  3. oracle本地连接地址,关于Oracle本地连接出现与监听有关的问题的解决方法探讨,oracle本地连接...

    关于Oracle本地连接出现与监听有关的问题的解决方法探讨,oracle本地连接 关于Oracle本地连接出现与监听有关的问题的解决方法探讨 监听的作用: 用于应用桌面即用户与数据库服务器建立连接的媒 ...

  4. windows7 ORA-12514 TNS 监听程序当前无法识别连接描述符中请求服务 的解决方法

    windows7 ORA-12514 TNS 监听程序当前无法识别连接描述符中请求服务 的解决方法 参考文章: (1)windows7 ORA-12514 TNS 监听程序当前无法识别连接描述符中请求 ...

  5. Bootstrap3 滚动监听插件的方法

    滚动监听插件的方法 通过 JavaScript 调用滚动监控插件时,如果监听对象的DOM节点有增删元素的操作,则需要调用 .scrollspy('refresh') 方法来更新DOM. <scr ...

  6. Bootstrap 滚动监听插件Scrollspy 的方法

    方法 通过 JavaScript 调用滚动监控插件时,如果监听对象的DOM节点有增删元素的操作,则需要调用 .scrollspy('refresh') 方法来更新DOM. <script> ...

  7. android 监听home back,Android中监听Home键的4种方法总结

    昨天需要处理一个问题,需要监听home键.最开始想到使用onKeydonwn这个方法.但是发现home不能这样处理,onKeydonwn可以处理菜单键和back键,但home不能.因为home键是系统 ...

  8. 客户端连接服务器,配置出错“连接超时”或者“无监听程序”解决方法

    这两天在进行Oracle的客户端配置,服务器OS为Windows XP 64,客户端OS为Win7 64,oracle版本为11.2. 先说下服务器端自己的疑惑,由于自己是新手,很多都不明白是怎么个回 ...

  9. js:如何监听history的pushState方法和replaceState方法。(高阶函数封装+自定义事件)

    出现原因: 想要监听路由变化就需要监听history的pushState和replaceState事件,但是原生并没有支持,此时,我们就得自己添加事件监听. 解决方法: 高阶函数封装自定义事件: co ...

最新文章

  1. GNN与Transformer融合促进药物发现 | 2022几何图机器学习展望
  2. for根据ID去重_Vue中v-for配合key使用的重要性
  3. 【Luogu】P3369 【模板】普通平衡树(树状数组)
  4. C#中修改文件或文件夹的权限,为指定用户、用户组添加完全控制权限
  5. linux死锁的例子,操作系统教程—Linux实例分析 孟庆昌 第8章 死锁new.ppt
  6. JavaScript开发工具--Aptana
  7. 云计算学习笔记004---hadoop的简介,以及安装,用命令实现对hdfs系统进行文件的上传下载
  8. MySQL 5.6 主从报错一例
  9. 关于《精武门》的回忆
  10. idea 2019最新版无法打开报错问题,Error occurred during initialization of VM Initial heap size set to a larger va
  11. oracle数据导出工具sqluldr2安装及使用
  12. Gerber文件解析
  13. 51单片机通过74HC595控制8位数码管,在任意位置显示数字
  14. 京冀津城际铁路网规划大全
  15. 【转】Windows10彻底关闭休眠功能
  16. 记一次m3u8文件转mp4的经历
  17. PC上阅读电子书的软件:Sumatra PDF和calibre
  18. 和平精英为什么进不去显示无法连接服务器,和平精英为什么进不去 进不去解决方法[多图]...
  19. Pandas学习-Day3
  20. 《苏菲的世界》读后感

热门文章

  1. P1 无符号整数二进制补码
  2. 智慧城市将开创城市新形态
  3. linux vim基础设置
  4. 第三次SLA文档学习
  5. php生鲜超市系统,毕业论文:基于PHP平台下的Ajax开发实践—网上生鲜超市系统的开发...
  6. Nodejs学习沉淀(二)——核心功能模块理解
  7. 金蝶K3 WISH移动商务信息配置——维那多
  8. SettingsProvider单编生效
  9. i7和i5(学习资料)
  10. mysql 去除微秒_Django2.2-DateTimeField去掉微秒