现在需要实现个工具, android设备要不断自动的去搜索附近蓝牙打印机,然后进行配对,连接打印数据.
根据测试发现有两个技术难点
- 第一个是一些设备链接打印机后,会弹出进行配对的对话框,有些设备还会让你输入配对密码进行配对,如果用人工去点击,就不是自动去搜索配对,并打印了.
- 第二个难点是循环去搜索并配对连接, 这其中与蓝牙设备配对后进行连接不是线性的, 因为配对成功或失败是通过广播监听到的,当配对成功后才能进行去链接, 通常用的方式就是循环遍历, 但是在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. 蓝牙配对流程
- 开启蓝牙设备扫描,并触发系统的配对弹窗。
- 辅助功能服务会捕获弹窗界面,通过文本或控件 ID 定位 PIN 输入框和按钮。
- 自动输入 PIN 并点击"配对"按钮完成操作。
注意事项
-
权限要求
-
需要动态申请 蓝牙相关权限:
<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();
}
}
}
```
```