当前位置: 首页 > article >正文

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

现在需要实现个工具, 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 {

    @Override
    public void onAccessibilityEvent(AccessibilityEvent event) {
        if (event.getEventType() != AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED) {
            return;
        }

        AccessibilityNodeInfo rootNode = getRootInActiveWindow();
        if (rootNode != null) {
            handleBluetoothPairingDialog(rootNode);
        }
    }

    @Override
    public 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 中声明辅助功能服务,并配置权限:

<service
    android:name=".BluetoothPairingService"
    android:permission="android.permission.BIND_ACCESSIBILITY_SERVICE">
    <intent-filter>
        <action android:name="android.accessibilityservice.AccessibilityService" />
    </intent-filter>

    <meta-data
        android:name="android.accessibilityservice"
        android:resource="@xml/accessibility_service_config" />
</service>

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

<accessibility-service
    xmlns: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() {
                            @Override
                            public 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() {
            @Override
            public void onPairingSuccess() {
                pairingResult = true;
                connectLatch.countDown();
            }

            @Override
            public void onPairingFailure() {
                pairingResult = false;
                connectLatch.countDown();
            }
        });
        // 实际项目中在这里注册广播
        // registerReceiver(bluetoothReceiver, intentFilter);
    }

    // 注销广播接收器
    private void unregisterPairingBroadcastReceiver() {
        try {
            // 实际项目中在这里注销广播
            // unregisterReceiver(bluetoothReceiver);
        } catch (IllegalArgumentException e) {
            e.printStackTrace();
        }
    }
}
```


```

http://www.kler.cn/a/412844.html

相关文章:

  • Paddle Inference部署推理(八)
  • 基于Springboot的心灵治愈交流平台系统的设计与实现
  • 【青牛科技】TS223 单触摸键检测IC
  • Golang面经
  • Ansible--自动化运维工具
  • 【C#】CancellationTokenSource 为任务或线程提供一种优雅的方式来支持取消操作
  • 家庭事务管理微信小程序ssm+论文源码调试讲解
  • 一个比较舒服的 prettier 前端配置 vue js
  • Jenkins-Gitlab 前端项目自动化部署
  • docker快速安装zookeeper
  • 【前端学习笔记】ES6 新特性
  • 书生大模型实战营第四期-入门岛-4. maas课程任务
  • 自动化测试工具Ranorex Studio(四十三)-RANOREXPATH编辑器5
  • 区块链:波场-TRON链
  • Windows Git Bash使用脚手架创建项目时无法交互切换选项
  • 去哪儿大数据面试题及参考答案
  • 上天入地 灵途科技光电技术赋能空间感知
  • Android 单选框RadioGroup+RadioButton
  • 五,[GXYCTF2019]Ping Ping Ping1
  • 【Linux打怪升级记 | 报错02】-bash: 警告:setlocale: LC_TIME: 无法改变区域选项 (zh_CN.UTF-8)
  • Burp入门(3)-爬虫功能介绍
  • 面向对象进阶-抽象类
  • Python基础学习-11函数参数
  • 视觉经典神经网络与复现:深入解析与实践指南
  • node.js中实现router模块化管理
  • hive的cascade使用解释