蓝牙循环搜索并连接. 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();
        }
    }
}
```


```
相关推荐
雨白12 小时前
Android 快捷方式实战指南:静态、动态与固定快捷方式详解
android
hqk12 小时前
鸿蒙项目实战:手把手带你实现 WanAndroid 布局与交互
android·前端·harmonyos
LING13 小时前
RN容器启动优化实践
android·react native
恋猫de小郭15 小时前
Flutter 发布官方 Skills ,Flutter 在 AI 领域再添一助力
android·前端·flutter
Kapaseker20 小时前
一杯美式搞懂 Any、Unit、Nothing
android·kotlin
黄林晴20 小时前
你的 Android App 还没接 AI?Gemini API 接入全攻略
android
恋猫de小郭1 天前
2026 Flutter VS React Native ,同时在 AI 时代 VS Native 开发,你没见过的版本
android·前端·flutter
冬奇Lab1 天前
PowerManagerService(上):电源状态与WakeLock管理
android·源码阅读
BoomHe2 天前
Now in Android 架构模式全面分析
android·android jetpack
二流小码农2 天前
鸿蒙开发:上传一张参考图片便可实现页面功能
android·ios·harmonyos