蓝牙循环搜索并连接. Android辅助功能以及锁的灵活运用

生产上遇到个问题, 某些蓝牙模块不能在低温下进行连接, 所以需要实现个工具 , 一次性自动检测150个蓝牙, 那么android设备就要不断自动的去搜索附近蓝牙模块,然后进行配对,再进行连接,连接成功后断开,去下一个蓝牙模块进行配对连接, 直到搜索出来的蓝牙都配对连接完毕.

根据测试发现有两个技术难点

  1. 第一个是一些设备链接打印机后,会弹出进行配对的对话框,有些设备还会让你输入配对密码进行配对,如果用人工去点击,就不是自动去搜索配对,并打印了.
  2. 第二个难点是循环去搜索并配对连接, 这其中与蓝牙设备配对后进行连接不是线性的, 因为配对成功或失败是通过广播监听到的,当配对成功后才能进行去链接, 通常用的方式就是循环遍历, 但是在A设备进行配对时, 就不能让代码往下走,不能让代码进行B设备的配对,需要等待A设备配对成功或失败后,才能进行B设备的操作.

针对第一个难点, 我采用的方式是用Android辅助功能去实现, 当弹出配对窗口后,判断是否有输入框,有的话就给他默认输入四个0,然后模拟点击确定或者配对按钮.

针对第二个难点,我采用的是锁机制, 具体来说就是使用CountDownLatch 去实现. CountDownLatch 是 Java 中的一个并发工具类,用于协调多个线程之间的同步。其作用是让某一个线程等待多个线程的操作完成之后再执行。它可以使一个或多个线程等待一组事件的发生,而其他的线程则可以触发这组事件。 在这里是现在循环末尾使用CountDownLatch加锁, 在配对成功或失败的时候释放锁.

PS : 10月上旬,vivo 推出的手机智能体——PhoneGPT,也可以听取用户指令执行买咖啡的操作;10 月下旬,荣耀 CEO 赵明在发布会现场也在使用智能助手 YOYO 一句话点咖啡。 其实这其中的一部分原理应该也使用了android 的辅助功能, 只不过厂商修改了rom, 这样就不用反复的需要用户授权. 并且结合了AI语义分析,最终实现的点咖啡操作

整体效果如下:

【蓝牙循环自动搜索并配对连接工具】 https://www.bilibili.com/video/BV1mQBXYaEAe/?share_source=copy_web&vd_source=3af38580f3cad2ae68e8ddb378d82938

蓝牙循环自动搜索并配对连接工具

好 下面就是关键代码:

第一个问题 利用辅助功能实现自动输入密码配对:


实现步骤

1. 创建辅助功能服务

创建一个继承自 AccessibilityService 的类,用于监听系统事件。

public class BluetoothPairingService extends AccessibilityService {@Overridepublic void onAccessibilityEvent(AccessibilityEvent event) {if (event.getEventType() != AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED) {return;}AccessibilityNodeInfo rootNode = getRootInActiveWindow();if (rootNode != null) {handleBluetoothPairingDialog(rootNode);}}@Overridepublic void onInterrupt() {// 服务中断时的处理逻辑}private void handleBluetoothPairingDialog(AccessibilityNodeInfo rootNode) {// 搜索提示内容或按钮List<AccessibilityNodeInfo> nodeList;// 自动输入 PIN 码nodeList = rootNode.findAccessibilityNodeInfosByText("Enter PIN"); // 根据具体文本修改if (!nodeList.isEmpty()) {AccessibilityNodeInfo pinField = nodeList.get(0).getParent().findFocus(AccessibilityNodeInfo.FOCUS_INPUT);if (pinField != null) {pinField.performAction(AccessibilityNodeInfo.ACTION_SET_TEXT, createSetTextArguments("1234")); // 替换为目标 PIN}}// 点击配对按钮nodeList = rootNode.findAccessibilityNodeInfosByText("Pair"); // 根据具体按钮文本修改if (!nodeList.isEmpty()) {for (AccessibilityNodeInfo node : nodeList) {if (node.isClickable()) {node.performAction(AccessibilityNodeInfo.ACTION_CLICK);}}}}private Bundle createSetTextArguments(String text) {Bundle arguments = new Bundle();arguments.putCharSequence(AccessibilityNodeInfo.ACTION_ARGUMENT_SET_TEXT_CHARSEQUENCE, text);return arguments;}
}

2. 配置 AccessibilityService

AndroidManifest.xml 中声明辅助功能服务,并配置权限:

<serviceandroid:name=".BluetoothPairingService"android:permission="android.permission.BIND_ACCESSIBILITY_SERVICE"><intent-filter><action android:name="android.accessibilityservice.AccessibilityService" /></intent-filter><meta-dataandroid:name="android.accessibilityservice"android:resource="@xml/accessibility_service_config" />
</service>

创建 res/xml/accessibility_service_config.xml 文件,配置服务行为:

<accessibility-servicexmlns:android="http://schemas.android.com/apk/res/android"android:accessibilityEventTypes="typeWindowStateChanged"android:accessibilityFeedbackType="feedbackGeneric"android:notificationTimeout="100"android:canRetrieveWindowContent="true"android:description="@string/accessibility_service_description"android:settingsActivity=".SettingsActivity" />

3. 启动辅助功能

用户需要手动开启辅助功能服务:

  • 进入 设置 > 辅助功能 > 已安装的服务,选择你的应用并启用服务。

4. 蓝牙配对流程
  1. 开启蓝牙设备扫描,并触发系统的配对弹窗。
  2. 辅助功能服务会捕获弹窗界面,通过文本或控件 ID 定位 PIN 输入框和按钮。
  3. 自动输入 PIN 并点击“配对”按钮完成操作。

注意事项

  1. 权限要求

    • 需要动态申请 蓝牙相关权限

      <uses-permission android:name="android.permission.BLUETOOTH" />
      <uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
      <uses-permission android:name="android.permission.BLUETOOTH_CONNECT" />

第二个问题, 循环实现不断搜索蓝牙并配对连接

偷懒了 我就写一下伪代码了

代码实现,结合广播接收器来处理配对成功或失败的情况,同时利用两个 CountDownLatch 实现循环搜索和配对操作。

---### **代码实现**```java
import android.bluetooth.BluetoothDevice;
import java.util.List;
import java.util.concurrent.CountDownLatch;public class BluetoothManager {private CountDownLatch searchLatch;  // 搜索信号锁private CountDownLatch connectLatch; // 配对信号锁private volatile boolean pairingResult = false; // 配对结果private BluetoothReceiver bluetoothReceiver; // 蓝牙广播接收器public void startBluetoothProcess() {new Thread(() -> {while (true) {try {// 重置搜索信号锁searchLatch = new CountDownLatch(1);// 开始蓝牙搜索(必须在主线程)runOnUiThread(() -> {bluetoothSearchDialogUtil.showBluetoothSearchDialog("Searching...");bluetoothSearchDialogUtil.setBlueToothSearchListener(new BluetoothSearchDialogUtil.BluetoothSearchListener() {@Overridepublic void onBluetoothDeviceFound(List<BluetoothDevice> discoveredDevices) {if (discoveredDevices != null && !discoveredDevices.isEmpty()) {handleDiscoveredDevices(discoveredDevices);}searchLatch.countDown(); // 搜索完成}});});// 等待搜索完成searchLatch.await();// 获取搜索到的设备List<BluetoothDevice> devicesList = getDiscoveredDevices();if (devicesList == null || devicesList.isEmpty()) {System.out.println("No devices found, restarting search...");continue;}// 遍历设备并进行配对连接for (BluetoothDevice device : devicesList) {// 重置连接信号锁connectLatch = new CountDownLatch(1);// 注册广播接收器registerPairingBroadcastReceiver();// 开始配对连接runOnUiThread(() -> startPairing(device));// 等待配对完成connectLatch.await();// 处理配对结果if (pairingResult) {System.out.println(device.getName() + " paired successfully.");} else {System.out.println(device.getName() + " pairing failed.");}// 注销广播接收器unregisterPairingBroadcastReceiver();}System.out.println("Cycle complete. Restarting search...");} catch (InterruptedException e) {e.printStackTrace();}}}).start();}// 模拟主线程操作的方法private void runOnUiThread(Runnable action) {new Thread(action).start();}// 保存搜索到的设备private void handleDiscoveredDevices(List<BluetoothDevice> devices) {// 保存逻辑(全局列表或其他方式)}// 获取保存的设备列表private List<BluetoothDevice> getDiscoveredDevices() {// 返回保存的设备列表return null;}// 开始配对private void startPairing(BluetoothDevice device) {try {device.createBond(); // 开始配对} catch (Exception e) {e.printStackTrace();connectLatch.countDown(); // 出错时释放信号锁}}// 注册广播接收器private void registerPairingBroadcastReceiver() {bluetoothReceiver = new BluetoothReceiver();bluetoothReceiver.setOnPairingListener(new BluetoothReceiver.PairingListener() {@Overridepublic void onPairingSuccess() {pairingResult = true;connectLatch.countDown();}@Overridepublic void onPairingFailure() {pairingResult = false;connectLatch.countDown();}});// 实际项目中在这里注册广播// registerReceiver(bluetoothReceiver, intentFilter);}// 注销广播接收器private void unregisterPairingBroadcastReceiver() {try {// 实际项目中在这里注销广播// unregisterReceiver(bluetoothReceiver);} catch (IllegalArgumentException e) {e.printStackTrace();}}
}
``````

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/diannao/62565.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Linux内核机制自学笔记

摘抄于大学期间记录在QQ空间的一篇自学笔记&#xff0c;当前清理空间&#xff0c;先搬移过来&#xff0c;也不知道到底是对是错了。 1、Linux内存管理 在计算机的世界&#xff0c;内存犹如一条长河&#xff0c;在这条长河中&#xff0c;cpu将这条长河划分成了段和页。cpu要将一…

oracle日期格式查询

文章目录 TO_CHARyyyy-MM-ddyyyy-MM-dd HH24:MI:SS TO_CHAR 在Oracle数据库中&#xff0c;可以使用TO_CHAR函数来格式化日期。TO_CHAR函数可以将DATE或TIMESTAMP数据类型转换为字符串&#xff0c;并按照指定的格式显示。 yyyy-MM-dd 以下是一个简单的例子&#xff0c;假设我…

ES----安装 elasticsearch入门,elasticsearch安装,centos安装es,centos安装elasticsearch

ES 如需要对应资源&#xff0c;请评论留言&#xff0c;或再最后视频中关注获取 1. 安装 1.1 安装es 创建网络&#xff08;centos系统&#xff0c;docker环境&#xff09; docker network create es-netdocker安装es —如果下载失败&#xff0c;请看我的docker配置镜像的文章…

Milvus×Florence:一文读懂如何构建多任务视觉模型

近两年来多任务学习&#xff08;Multi-task learning&#xff09;正取代传统的单任务学习&#xff08;single-task learning&#xff09;&#xff0c;逐渐成为人工智能领域的主流研究方向。其原因在于&#xff0c;多任务学习可以让我们以最少的人力投入&#xff0c;获得尽可能多…

172页PPT集团数字化转型采购供应链及财务管控业务流程指南

一、供应商管理与数字化转型 1.1供应商管理数字化的重要性与挑战 重要性&#xff1a; 效率提升&#xff1a; 数字化可以提高供应商管理的效率&#xff0c;通过自动化流程减少手动操作&#xff0c;加快决策速度。透明度增强&#xff1a; 数字化工具可以提供实时数据&#xff…

手机镜头组如此突出,考虑恢复以前设计

现在手头看重照相。结果导致的问题就是&#xff0c;在背部要突出很高&#xff0c;以容纳镜头组件。这种设计真的好吗&#xff1f;并不见得。真实照片&#xff1a; VIVO X200系列镜头组照片-CSDN博客 考虑到现在镜头的情形&#xff0c;我建议恢复以前的设计&#xff0c;就是把镜…

【机器学习】机器学习基础

什么是机器学习&#xff1f; 机器学习&#xff08;Machine Learning, ML&#xff09;是一种人工智能&#xff08;AI&#xff09;的分支&#xff0c;指计算机通过数据学习规律并做出预测或决策&#xff0c;而无需明确编程。它的核心目标是让机器能够从经验中学习&#xff0c;逐…

设计模式 外观模式 门面模式

结构性模式-外观模式 门面模式 适用场景&#xff1a;如果你需要一个指向复杂子系统的直接接口&#xff0c; 且该接口的功能有限&#xff0c; 则可以使用外观模式。 不用关心后面的查询具体操作 /*** 聚合查询接口*/ RestController RequestMapping("/search") Slf…

基于开源云原生数据仓库 ByConity 体验多种数据分析场景

基于开源云原生数据仓库 ByConity 体验多种数据分析场景 业务背景什么是 ByConity上手实测环境要求测试操作远程登录 ECS 服务器windows10 自带连接工具 执行查询 ByConity 相对于 ELT 能力的优化提升并行度任务级重试并行写入简化数据链路 业务背景 大家都知道&#xff0c;在…

【051】基于51单片机温度计【Proteus仿真+Keil程序+报告+原理图】

☆、设计硬件组成&#xff1a;51单片机最小系统DS18B20温度传感器LCD1602液晶显示按键设置蜂鸣器LED灯。 1、本设计采用STC89C51/52、AT89C51/52、AT89S51/52作为主控芯片&#xff1b; 2、采用DS18B20温度传感器测量温度&#xff0c;并且通过LCD1602实时显示温度&#xff1b;…

HarmonyOS(61) 组件间状态共享的分类以及状态选择器的选取优先级

状态共享 状态共享的分类状态共享选择器State与prop\Link\ObservedObjectLink组合的区别合理选择装饰器的顺序参考资料 状态共享的分类 HarmonyOS的组件之间是可以共享状态数据了&#xff0c;不同的组件之间&#xff0c;状态共享的场景也不一样&#xff0c;根据共享范围从小到…

Redis(4):主从复制

一、主从复制概述 主从复制&#xff0c;是指将一台Redis服务器的数据&#xff0c;复制到其他的Redis服务器。前者称为主节点(master)&#xff0c;后者称为从节点(slave)&#xff1b;数据的复制是单向的&#xff0c;只能由主节点到从节点。   默认情况下&#xff0c;每台Redis…

14 —— Webpack解析别名

import {checkPhone, checkCode} from ../src/utils/check.js 这么使用相对路径不安全 —— 在webpack.config.js中配置解析别名来代表src绝对路径

雪花算法详解:分布式系统中高效唯一的ID生成方案

文章目录 原理与结构工作流程优势局限性应对高并发的方法适用场景 雪花算法&#xff08;Snowflake Algorithm&#xff09;是由Twitter开发的一种分布式全局唯一ID生成方案&#xff0c;旨在解决在分布式系统中快速、无冲突地生成唯一标识符的问题。它通过巧妙的设计&#xff0c;…

静态页面 和 动态页面(Java Web开发)

1. 静态页面 1.1 什么是静态页面&#xff1f; 静态页面是指 HTML 文件直接存放在服务器上&#xff0c;不依赖后端逻辑处理而生成内容。客户端浏览器请求静态页面时&#xff0c;服务器直接将文件发送到客户端&#xff0c;浏览器负责渲染页面。 特点&#xff1a; 固定内容&am…

Spring Boot优雅读取配置信息 @EnableConfigurationProperties

很多时候我们需要将一些常用的配置信息比如oss等相关配置信息放到配置文件中。常用的有以下几种&#xff0c;相信大家比较熟悉&#xff1a; 1、Value(“${property}”) 读取比较简单的配置信息&#xff1a; 2、ConfigurationProperties(prefix “property”)读取配置信息并与 …

贴片电阻(片式厚膜电阻)生产工艺流程

贴片电阻&#xff08;片式厚膜电阻&#xff09;生产工艺流程 1.基体处理--->印刷电极--->电极烧结2.电阻体印刷--->电阻体烧结3.一次玻璃印刷--->一次玻璃烧结4.激光调阻5.二次玻璃印刷--->二次玻璃烧结6.字码印刷--->字码烧结7.折条8.端电极涂覆9.折粒10.电…

系统思考—结构影响行为

看到这张图&#xff0c;我不禁在思考&#xff1a; 动机和纪律有什么区别&#xff1f; 它们背后隐藏的系统结构是什么&#xff1f; 结构如何影响我们的行为&#xff1f; 更重要的是&#xff0c;我们能如何设计一个系统结构&#xff0c;引导自己走向Discipline的趋势?

transformers bert-base-uncased情感分析

一、使用huggingface中的预训练模型&#xff0c;先要安装transformers、torch和SentencePiece pip install transformers pip install torch pip install SentencePiece 手动下载&#xff1a;https://huggingface.co/google-bert/bert-base-uncased/tree/main 添加以目录&…

Android笔记(三十四):封装带省略号图标结尾的TextView

背景 项目需求需要实现在文本末尾显示一个icon&#xff0c;如果文本很长时则在省略号后面显示icon&#xff0c;使用TextView自带的drawableEnd可以实现&#xff0c;但是如果文本换行了则会显示在TextView垂直居中的位置&#xff0c;不满足要求&#xff0c;于是有了本篇的自定义…