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

news2024/12/27 3:25:17

生产上遇到个问题, 某些蓝牙模块不能在低温下进行连接, 所以需要实现个工具 , 一次性自动检测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 {

    @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.coloradmin.cn/o/2251538.html

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

相关文章

SAP SD学习笔记15 - 返品处理流程2 - 参照请求传票(发票)来生成返品传票

上一章讲了返品处理&#xff08;退货处理&#xff09;的流程。 SAP SD学习笔记14 - 返品处理&#xff08;退货处理&#xff09;的流程以及系统实操&#xff0c;比如 返品传票&#xff1b;请求Block标记&#xff1b;收到退货之后的处理&#xff0c;请求传票的登录_sap 销售返品…

在 ASP.NET C# Web API 中实现 Serilog 以增强请求和响应的日志记录

介绍 日志记录是任何 Web 应用程序的关键方面。它有助于调试、性能监控和了解用户交互。在 ASP.NET C# 中&#xff0c;集成 Serilog 作为记录请求和响应&#xff08;包括传入和传出的数据&#xff09;的中间件可以显著提高 Web API 的可观察性和故障排除能力。 在过去的几周里&…

Y20030018基于Java+Springboot+mysql+jsp+layui的家政服务系统的设计与实现 源代码 文档

家政服务系统的设计与实现 1.摘要2.开发目的和意义3.系统功能设计4.系统界面截图5.源码获取 1.摘要 随着人们生活水平的提高&#xff0c;老龄化、少子化等多重因素影响&#xff0c;我国对家政服务人群的需求与日俱增。家政服务行业对我国的就业和社会效益贡献也与日俱增&#…

基于AutoEncode自编码器的端到端无线通信系统matlab误码率仿真

目录 1.算法仿真效果 2.算法涉及理论知识概要 3.MATLAB核心程序 4.完整算法代码文件获得 1.算法仿真效果 matlab2022a仿真结果如下&#xff08;完整代码运行后无水印&#xff09;&#xff1a; 仿真操作步骤可参考程序配套的操作视频。 2.算法涉及理论知识概要 自编码器是…

深度学习与持续学习:人工智能的未来与研究方向

文章目录 1. 持续学习与深度学习1.1 深度学习的局限1.2 持续学习的定义 2. 目标与心智2.1 奖励假说2.2 心智的构成 3. 对研究方法的建议3.1 日常写作记录3.2 中立对待流行趋势 1. 持续学习与深度学习 1.1 深度学习的局限 深度学习注重“瞬时学习”&#xff0c;如ChatGPT虽在语…

使用C#开发VTK笔记(三)-使用VTK创建可视化界面和简单点线面体模型

一.使用VTK创建可视化界面 1.创建C#可视化界面 使用Visual Studio 2022用.net 8.0创建窗体应用 放入两个水平分割的Panel左侧演示VTK 右侧放按钮 按照第一章节环境搭建的步骤,加入对应Kitware的dll并导入依赖 2.初始化Vtk窗口 引用Kitware.VTK,Form1_Load中,创建渲染窗口控…

ctrl键和大写键互换解决方法

电脑卡住之后突然发现Ctrl键和大小写键&#xff08;CapsLock&#xff09;互换了&#xff0c;后面试了几种方法都没解决这个问题&#xff0c;最后在万能的贴吧中找到解决方法——键位复位。 108和87键位复位操作&#xff1a; 1.先按住FN不放&#xff0c; 然后&#xff0c;再按住…

PVE相关名词通俗表述方式———多处细节实验(方便理解)

PVE设置初期&#xff0c;对CIDR、 网关、 LinuxBridge、VLAN等很有困惑的朋友一定很需要一篇能够全面通俗易懂的方式去理解PVE 中Linux网桥的工作方式&#xff0c;就像操作一个英雄&#xff0c;多个技能&#xff0c;还是需要一点点去学习理解的&#xff0c;如果你上来就对着别人…

UG NX二次开发(C++)-Ufun函数-UF_MODL_ask_face_props说明

文章目录 1、前言2、UF_MODL_ask_face_props的函数说明3、UF_MODL_ask_face_props的应用3.1 获取归一化曲面上的点3.2 获取非归一化曲面上的点1、前言 UF_MODL_ask_face_props是获取曲面上的微分几何的函数,其能根据曲面上的参数,获取点、矢量等,也是经常用到的一个函数,但…

软件测试技术面试题及参考答案整理

一、什么是兼容性测试?兼容性测试侧重哪些方面? 参考答案&#xff1a; 兼容测试主要是检查软件在不同的硬件平台、软件平台上是否可以正常的运行&#xff0c;即是通常说的软件的可移植性。 兼容的类型&#xff0c;如果细分的话&#xff0c;有平台的兼容&#xff0c;网络兼…

【面试题】2025年百度校招Java后端面试题

文章目录 前言一、网络IO1、服务器处理并发请求有哪几种方式&#xff1f;2、说一下select&#xff0c;poll&#xff0c;epoll的区别&#xff1f;3、Java 有一种现代的处理方式&#xff0c;属于异步I/O&#xff0c;是什么&#xff1f;redis&#xff0c;nginx&#xff0c;netty 是…

HDU Go Running(最小点覆盖 + 网络流优化)

题目大意&#xff1a;有一条无限长跑道&#xff0c;每个人可以规定自己跑步的方向&#xff0c;起点&#xff0c;跑步起止时间。每个人跑步的速度都是1m/s。最后从监控人员哪里得到了n个报告&#xff0c;每个报告给出了某人在某一时候所在的位置&#xff0c;问跑步的最少可能人数…

.NET9 - Swagger平替Scalar详解(四)

书接上回&#xff0c;上一章介绍了Swagger代替品Scalar&#xff0c;在使用中遇到不少问题&#xff0c;今天单独分享一下之前Swagger中常用的功能如何在Scalar中使用。 下面我们将围绕文档版本说明、接口分类、接口描述、参数描述、枚举类型、文件上传、JWT认证等方面详细讲解。…

【JAVA] 杂谈: java中的拷贝(克隆方法)

这篇文章我们来介绍什么是拷贝&#xff0c;并且实现浅拷贝到深拷贝。 目录 一、浅拷贝 1.1 clone 方法 1.2 实现浅拷贝&#xff1a; 1.2.1 重写 clone方法 1.2.2 实现接口 Cloneable 1.2.3 调用克隆方法 1.2.4 原理图&#xff1a;​ 1.3 浅拷贝的不足 1.3.1 增加引用…

记录QT5迁移到QT6.8上的一些问题

经常看到有的同学说网上的教程都是假的&#xff0c;巴拉巴拉&#xff0c;看看人家发布时间&#xff0c;Qt官方的API都会有所变动&#xff0c;多搜索&#xff0c;多总结&#xff0c;再修改记录。 下次遇到问题多这样搜索 QT 4/5/6 xxx document&#xff0c;对比一下就知道…

【数据结构与算法】排序算法总结:冒泡 / 快排 / 直接插入 / 希尔 / 简单选择 / 堆排序 / 归并排序

1 排序 1.1 冒泡 内排序的交换排序类别 1.1.1 普通实现 public class BubbleSort {/*** 基本的 冒泡排序*/public static void bubbleSort(int[] srcArray) {int i,j; // 用于存放数组下标int temp 0; // 用于交换数值时临时存放值for(i0;i<srcArray.length-1;i){// j …

分布式储能监控系统为储能电站高效运维与精细化管理赋能

1、引言 随着全球对可持续发展和环境保护意识的增强&#xff0c;能源结构正在经历深刻的转型。传统化石能源因其不可再生性和环境污染问题而逐渐受到限制&#xff0c;而可再生能源如太阳能、风能等因其清洁、可持续的特性而受到广泛关注和推广。这一转型推动了储能技术的快速发…

SciAssess——评估大语言模型在科学文献处理中关于模型的记忆、理解和分析能力的基准

概述 大规模语言模型&#xff08;如 Llama、Gemini 和 GPT-4&#xff09;的最新进展因其卓越的自然语言理解和生成能力而备受关注。对这些模型进行评估对于确定其局限性和潜力以及促进进一步的技术进步非常重要。为此&#xff0c;人们提出了一些特定的基准来评估大规模语言模型…

TiDB 关联子查询及半连接的优化实践

导读 TiDB 针对子查询语句会执行多种子查询相关的优化 ( https://docs.pingcap.com/zh/tidb/stable/subquery-optimization )&#xff0c;以提升子查询的执行性能。半连接语句和关联子查询语句是常用的两类子查询&#xff0c;TiDB 优化器默认包含一些自动优化策略&#xff0c;…

Mac 配置pytorch ---- 保姆级教程

一、安装AnaConda 或者轻量级的 miniConda AnaConda安装地址&#xff1a;https://www.anaconda.com/ miniConda安装地址&#xff1a;https://mirrors.tuna.tsinghua.edu.cn/anaconda/miniconda 1) 以miniConda 为例&#xff1a; 进入地址后&#xff0c;找到最新的安装版本下载…