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

embedded/2024/11/28 21:58:01/

现在需要实现个工具, android设备要不断自动的去搜索附近蓝牙打印机,然后进行配对,连接打印数据.

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

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

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

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

整体效果如下:

【蓝牙循环自动搜索并配对连接工具】 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.ppmy.cn/embedded/141271.html

相关文章

黑马程序员Java项目实战《苍穹外卖》Day01

苍穹外卖-day01 课程内容 软件开发整体介绍苍穹外卖项目介绍开发环境搭建导入接口文档Swagger 项目整体效果展示&#xff1a; ​ 管理端-外卖商家使用 ​ 用户端-点餐用户使用 当我们完成该项目的学习&#xff0c;可以培养以下能力&#xff1a; 1. 软件开发整体介绍 作为一…

在 Spring Boot 中构造 API 响应的最佳实践

在平时的开发和项目中&#xff0c;我们一定会涉及到接口对接的功能&#xff0c;由于不同开发人员的编码习惯不同&#xff0c;API报文在项目中通常是"百花齐放"的。 不但增加工作难度&#xff0c;往往也是扯皮的大头&#xff0c;如果能统一报文格式&#xff0c;不但能…

【设计模式】【行为型模式(Behavioral Patterns)】之责任链模式(Chain of Responsibility Pattern)

1. 设计模式原理说明 责任链模式&#xff08;Chain of Responsibility Pattern&#xff09; 是一种行为设计模式&#xff0c;它允许你将请求沿着处理者链进行发送。每个处理者都可以处理请求&#xff0c;或者将其传递给链上的下一个处理者。这种模式使得多个对象都有机会处理请…

67 mysql 的 间隙锁

前言 我们这里主要是 来看一下 mysql 中的 间隙锁 间隙锁 主要存在的地方一般就是在 查询主键查询不到, 索引查询查询不到 的场景 然后 我们这里来调试一下 这里的整个流程, 间隙锁的加锁 以及 间隙锁的使用, 以及 间隙锁的释放 从逻辑上来说 间隙锁 锁定的是一个区间, 按照…

mysql-分析并解决mvcc更新丢失问题

多版本并发控制&#xff08;Multi-Version Concurrency Control, MVCC&#xff09;是现代数据库系统中常用的一种并发控制机制&#xff0c;用于提高并发性能和数据一致性。然而&#xff0c;MVCC 本身并不能完全解决更新丢失问题。让我们详细探讨一下这个问题的原因和背景。 更…

大语言模型(LLM)的训练微调 Fine Tuning -- part3 本地调用

以下代码示范如何调用已经微调后的大语言模型&#xff0c;调用本地模型 先决条件 已经有了本地训练好的大语言模型&#xff0c;如何训练可以参考我的博文 《生成式 AI》课程 作业6 大语言模型&#xff08;LLM&#xff09;的训练微调 Fine Tuning -- part2-CSDN博客文章浏览阅…

IDEA全局设置-解决maven加载过慢的问题

一、IDEA全局设置 注意&#xff1a;如果不是全局设置&#xff0c;仅仅针对某个项目有效&#xff1b;例在利用网上教程解决maven加载过慢的问题时&#xff0c;按步骤设置却得不到解决&#xff0c;原因就是没有在全局设置。 1.如何进行全局设置 a.在项目页面&#xff0c;点击f…

计算机网络 第4章 网络层

计算机网络 &#xff08;第八版&#xff09;谢希仁 第 4 章 网络层4.2.2 IP地址**无分类编址CIDR**IP地址的特点 4.2.3 IP地址与MAC地址4.2.4 ARP 地址解析协议4.2.5 IP数据报的格式题目2&#xff1a;IP数据报分片与重组题目&#xff1a;计算IP数据报的首部校验和(不正确未改) …