Android 蓝牙工具类封装:支持经典蓝牙与 BLE,兼容高版本权限

为了优化经典蓝牙(Classic Bluetooth)和低功耗蓝牙(Bluetooth Low Energy, BLE)的操作,我们可以将功能封装到一个工具类中,支持扫描、连接、通信,并兼容高版本 Android 的动态权限申请。以下是完整的工具类实现。

  1. 工具类功能
    经典蓝牙:

扫描设备。

连接设备。

发送和接收数据。

BLE 蓝牙:

扫描设备。

连接设备。

发送和接收数据(通过 GATT 特征值)。

权限管理:

动态申请权限(包括 ACCESS_FINE_LOCATION、BLUETOOTH_SCAN、BLUETOOTH_CONNECT)。

高版本兼容:

支持 Android 12 及以上版本的权限要求。

  1. 工具类实现
    2.1 BluetoothHelper.java
import android.Manifest;
import android.annotation.SuppressLint;
import android.bluetooth.*;
import android.bluetooth.le.BluetoothLeScanner;
import android.bluetooth.le.ScanCallback;
import android.bluetooth.le.ScanResult;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.PackageManager;
import android.os.Build;
import android.os.Handler;
import android.os.Looper;
import androidx.annotation.NonNull;
import androidx.core.app.ActivityCompat;
import androidx.core.content.ContextCompat;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;public class BluetoothHelper {private static final UUID MY_UUID = UUID.fromString("00001101-0000-1000-8000-00805F9B34FB"); // SPP UUIDprivate static final int PERMISSION_REQUEST_CODE = 100;private Context context;private BluetoothAdapter bluetoothAdapter;private BluetoothLeScanner bleScanner;private BluetoothSocket bluetoothSocket;private BluetoothGatt bluetoothGatt;private Handler handler;private List<BluetoothDevice> classicDevices = new ArrayList<>();private List<BluetoothDevice> bleDevices = new ArrayList<>();// BLE 相关变量private BluetoothGattCharacteristic writeCharacteristic;private BluetoothGattCharacteristic notifyCharacteristic;// 回调接口public interface ScanCallback {void onClassicDeviceFound(BluetoothDevice device);void onBleDeviceFound(BluetoothDevice device);void onScanFailed(String error);}public interface ConnectionCallback {void onConnected();void onConnectionFailed(String error);}public interface DataCallback {void onDataReceived(byte[] data);void onDataSent(boolean success);}public BluetoothHelper(Context context) {this.context = context;this.handler = new Handler(Looper.getMainLooper());this.bluetoothAdapter = BluetoothAdapter.getDefaultAdapter();if (bluetoothAdapter != null) {this.bleScanner = bluetoothAdapter.getBluetoothLeScanner();}}// 检查权限public boolean checkPermissions() {if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {return ContextCompat.checkSelfPermission(context, Manifest.permission.BLUETOOTH_SCAN) == PackageManager.PERMISSION_GRANTED &&ContextCompat.checkSelfPermission(context, Manifest.permission.BLUETOOTH_CONNECT) == PackageManager.PERMISSION_GRANTED &&ContextCompat.checkSelfPermission(context, Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED;} else {return ContextCompat.checkSelfPermission(context, Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED;}}// 请求权限public void requestPermissions() {if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {ActivityCompat.requestPermissions((MainActivity) context,new String[]{Manifest.permission.BLUETOOTH_SCAN,Manifest.permission.BLUETOOTH_CONNECT,Manifest.permission.ACCESS_FINE_LOCATION},PERMISSION_REQUEST_CODE);} else {ActivityCompat.requestPermissions((MainActivity) context,new String[]{Manifest.permission.ACCESS_FINE_LOCATION},PERMISSION_REQUEST_CODE);}}// 开始扫描经典蓝牙设备public void startClassicScan(ScanCallback callback) {if (!checkPermissions()) {callback.onScanFailed("Permissions not granted");return;}classicDevices.clear();IntentFilter filter = new IntentFilter(BluetoothDevice.ACTION_FOUND);context.registerReceiver(new BroadcastReceiver() {@Overridepublic void onReceive(Context context, Intent intent) {String action = intent.getAction();if (BluetoothDevice.ACTION_FOUND.equals(action)) {BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);classicDevices.add(device);callback.onClassicDeviceFound(device);}}}, filter);bluetoothAdapter.startDiscovery();}// 开始扫描 BLE 设备@SuppressLint("MissingPermission")public void startBleScan(ScanCallback callback) {if (!checkPermissions()) {callback.onScanFailed("Permissions not granted");return;}bleDevices.clear();bleScanner.startScan(new ScanCallback() {@Overridepublic void onScanResult(int callbackType, ScanResult result) {BluetoothDevice device = result.getDevice();bleDevices.add(device);callback.onBleDeviceFound(device);}@Overridepublic void onScanFailed(int errorCode) {callback.onScanFailed("BLE scan failed with error code: " + errorCode);}});}// 停止扫描@SuppressLint("MissingPermission")public void stopScan() {bluetoothAdapter.cancelDiscovery();if (bleScanner != null) {bleScanner.stopScan(null);}}// 连接经典蓝牙设备public void connectClassicDevice(BluetoothDevice device, ConnectionCallback callback) {new Thread(() -> {try {bluetoothSocket = device.createRfcommSocketToServiceRecord(MY_UUID);bluetoothSocket.connect();handler.post(callback::onConnected);} catch (IOException e) {handler.post(() -> callback.onConnectionFailed(e.getMessage()));}}).start();}// 连接 BLE 设备@SuppressLint("MissingPermission")public void connectBleDevice(BluetoothDevice device, BluetoothGattCallback gattCallback) {bluetoothGatt = device.connectGatt(context, false, gattCallback);}// 发送数据(经典蓝牙)public void sendClassicData(byte[] data, DataCallback callback) {new Thread(() -> {try {OutputStream outputStream = bluetoothSocket.getOutputStream();outputStream.write(data);handler.post(() -> callback.onDataSent(true));} catch (IOException e) {handler.post(() -> callback.onDataSent(false));}}).start();}// 接收数据(经典蓝牙)public void receiveClassicData(DataCallback callback) {new Thread(() -> {try {InputStream inputStream = bluetoothSocket.getInputStream();byte[] buffer = new byte[1024];int bytes;while ((bytes = inputStream.read(buffer)) != -1) {byte[] receivedData = new byte[bytes];System.arraycopy(buffer, 0, receivedData, 0, bytes);handler.post(() -> callback.onDataReceived(receivedData));}} catch (IOException e) {handler.post(() -> callback.onDataReceived(null));}}).start();}// 发送数据(BLE)@SuppressLint("MissingPermission")public void sendBleData(byte[] data, DataCallback callback) {if (writeCharacteristic != null && bluetoothGatt != null) {writeCharacteristic.setValue(data);bluetoothGatt.writeCharacteristic(writeCharacteristic);handler.post(() -> callback.onDataSent(true));} else {handler.post(() -> callback.onDataSent(false));}}// 启用通知以接收数据(BLE)@SuppressLint("MissingPermission")public void enableBleNotifications(BluetoothGattCharacteristic characteristic, DataCallback callback) {if (bluetoothGatt != null) {bluetoothGatt.setCharacteristicNotification(characteristic, true);BluetoothGattDescriptor descriptor = characteristic.getDescriptor(UUID.fromString("00002902-0000-1000-8000-00805f9b34fb"));if (descriptor != null) {descriptor.setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE);bluetoothGatt.writeDescriptor(descriptor);}}}// 断开连接public void disconnect() {try {if (bluetoothSocket != null) {bluetoothSocket.close();}if (bluetoothGatt != null) {bluetoothGatt.disconnect();bluetoothGatt.close();}} catch (IOException e) {e.printStackTrace();}}
}

2.2 使用示例
扫描设备

BluetoothHelper bluetoothHelper = new BluetoothHelper(this);// 扫描经典蓝牙设备
bluetoothHelper.startClassicScan(new BluetoothHelper.ScanCallback() {@Overridepublic void onClassicDeviceFound(BluetoothDevice device) {Log.d("BluetoothHelper", "Classic Device Found: " + device.getName());}@Overridepublic void onBleDeviceFound(BluetoothDevice device) {Log.d("BluetoothHelper", "BLE Device Found: " + device.getName());}@Overridepublic void onScanFailed(String error) {Log.e("BluetoothHelper", "Scan Failed: " + error);}
});// 扫描 BLE 设备
bluetoothHelper.startBleScan(new BluetoothHelper.ScanCallback() {@Overridepublic void onClassicDeviceFound(BluetoothDevice device) {Log.d("BluetoothHelper", "Classic Device Found: " + device.getName());}@Overridepublic void onBleDeviceFound(BluetoothDevice device) {Log.d("BluetoothHelper", "BLE Device Found: " + device.getName());}@Overridepublic void onScanFailed(String error) {Log.e("BluetoothHelper", "Scan Failed: " + error);}
});

连接设备

// 连接经典蓝牙设备
bluetoothHelper.connectClassicDevice(device, new BluetoothHelper.ConnectionCallback() {@Overridepublic void onConnected() {Log.d("BluetoothHelper", "Classic Device Connected");}@Overridepublic void onConnectionFailed(String error) {Log.e("BluetoothHelper", "Connection Failed: " + error);}
});// 连接 BLE 设备
bluetoothHelper.connectBleDevice(device, new BluetoothGattCallback() {@Overridepublic void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) {if (newState == BluetoothProfile.STATE_CONNECTED) {Log.d("BluetoothHelper", "BLE Device Connected");} else if (newState == BluetoothProfile.STATE_DISCONNECTED) {Log.d("BluetoothHelper", "BLE Device Disconnected");}}
});

经典蓝牙发送和接收数据

// 发送数据(经典蓝牙)
bluetoothHelper.sendClassicData("Hello Bluetooth".getBytes(), new BluetoothHelper.DataCallback() {@Overridepublic void onDataReceived(byte[] data) {// 处理接收到的数据}@Overridepublic void onDataSent(boolean success) {Log.d("BluetoothHelper", "Data Sent: " + success);}
});// 接收数据(经典蓝牙)
bluetoothHelper.receiveClassicData(new BluetoothHelper.DataCallback() {@Overridepublic void onDataReceived(byte[] data) {Log.d("BluetoothHelper", "Data Received: " + new String(data));}@Overridepublic void onDataSent(boolean success) {// 无需处理}
});

BLE蓝牙发送和接收数据

// 连接 BLE 设备
bluetoothHelper.connectBleDevice(device, new BluetoothGattCallback() {@Overridepublic void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) {if (newState == BluetoothProfile.STATE_CONNECTED) {gatt.discoverServices();}}@Overridepublic void onServicesDiscovered(BluetoothGatt gatt, int status) {if (status == BluetoothGatt.GATT_SUCCESS) {BluetoothGattService service = gatt.getService(UUID.fromString("你的服务UUID"));if (service != null) {writeCharacteristic = service.getCharacteristic(UUID.fromString("写特征值UUID"));notifyCharacteristic = service.getCharacteristic(UUID.fromString("通知特征值UUID"));// 启用通知bluetoothHelper.enableBleNotifications(notifyCharacteristic, new BluetoothHelper.DataCallback() {@Overridepublic void onDataReceived(byte[] data) {Log.d("BluetoothHelper", "BLE Data Received: " + new String(data));}@Overridepublic void onDataSent(boolean success) {// 无需处理}});}}}@Overridepublic void onCharacteristicChanged(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) {byte[] data = characteristic.getValue();Log.d("BluetoothHelper", "BLE Notification Data: " + new String(data));}
});// 发送数据(BLE)
bluetoothHelper.sendBleData("Hello BLE".getBytes(), new BluetoothHelper.DataCallback() {@Overridepublic void onDataReceived(byte[] data) {// 无需处理}@Overridepublic void onDataSent(boolean success) {Log.d("BluetoothHelper", "BLE Data Sent: " + success);}
});

2.3 注意事项
权限管理:

在 Android 12 及以上版本,需要动态申请 BLUETOOTH_SCAN 和 BLUETOOTH_CONNECT 权限。

在 Android 11 及以下版本,需要动态申请 ACCESS_FINE_LOCATION 权限。

高版本兼容:

使用 @SuppressLint(“MissingPermission”) 忽略权限检查,确保在实际运行时已授予权限。

线程管理:

经典蓝牙的通信操作应在后台线程中进行,避免阻塞主线程。
通过以上工具类,你可以轻松实现经典蓝牙和 BLE 的扫描、连接和通信功能,并兼容高版本 Android 的权限要求。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/diannao/72940.shtml

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

相关文章

STM32 CAN模块原理与应用详解

目录 概述 一、CAN模块核心原理 1. CAN协议基础 2. STM32 CAN控制器结构 3. 波特率配置 二、CAN模块配置步骤&#xff08;基于HAL库&#xff09; 1. 初始化CAN外设 2. 配置过滤器 3. 启动CAN通信 三、数据收发实现 1. 发送数据帧 2. 接收数据帧&#xff08;中断方式…

PostgreSQL_安装部署

一、Windows系统下安装 1.下载安装包 登录PostgreSQL: Downloads官网&#xff1a; 选择14.12版本&#xff0c;点击下载&#xff1a; 2.安装PostgrSQL14.12 双击exe安装包程序&#xff0c;准备安装&#xff1a; 选择安装路径&#xff1a; 选择想安装的工具&#xff1a; 选择数…

init arry的作用,可以没有init arry嘛?(面试题)

https://bbs.kanxue.com/thread-282657.htm 对init_array段调用的方法进行Hook https://bbs.kanxue.com/thread-191092.htm init_array原理简单说明 https://bbs.kanxue.com/thread-280135.htm frida hook init_array自吐新解 init_array 的作用&#xff0c;以及是否可以没有 i…

蓝桥杯真题0团建dfs+哈希表/邻接表

dfs邻接表储存或者哈希表的运用&#xff0c;考察我们对数据的存储 本题核心就是在求从根节点开始的两棵树相同的最长序列&#xff0c;首先确定用dfs进行深搜&#xff0c;对于节点的形式可以用邻接表&#xff0c;邻接矩阵&#xff0c;哈希表来进行存储数据。下面看代码 邻接表 …

使用 AIStor、MLflow 和 KServe 将模型部署到 Kubernetes

在之前几篇关于 MLOps 工具的文章中&#xff0c;我展示了有多少流行的 MLOps 工具跟踪与模型训练实验相关的指标。我还展示了他们如何使用 MinIO 来存储作为模型训练管道一部分的非结构化数据。但是&#xff0c;一个好的 MLOps 工具应该做的不仅仅是管理您的实验、数据集和模型…

kali linux web扫描工具

Kali Linux是一款专为网络安全领域而打造的操作系统&#xff0c;提供了众多优秀的安全工具&#xff0c;其中就包括了强大的web扫描工具。Web扫描是网络安全检测的一个重要环节&#xff0c;它可以帮助安全专家检测网站的漏洞&#xff0c;提升网站的安全性。 Kali Linux中集成了…

Linux losetup循环设备

好的&#xff0c;以下是命令的中文解释和使用步骤&#xff1a; 命令解释&#xff1a; losetup -r /dev/loop0 /system/app.bin&#xff1a; losetup 是一个用于将文件与循环设备&#xff08;loop device&#xff09;关联的命令。-r 选项表示将循环设备设置为只读模式。/dev/lo…

【js逆向】

地址&#xff1a;aHR0cHM6Ly93d3cud2VpYm90b3AuY24vMi4wLw f12进入 debugger&#xff0c;过debugger 查看预览数据 全局搜索 请求网址中的 api.weibotop.cn 在下方疑似找到了加密和解密的函数 断点调试 控制台输出 那个n就是 常见的 cryptoJs库 const cryptoJs require(cry…

1.Intel BIOS 开发指南详细介绍

1. 引言 目的: Intel BIOS 开发指南旨在为开发者提供详细的指导,帮助他们理解和实现 Intel 平台上的 BIOS 功能。 适用对象: 适用于希望开发、调试和优化 BIOS 的硬件工程师、软件工程师和系统集成商。 版本信息: 确保你使用的是最新版本的指南,以获取最新的信息和最佳实…

deepseek在pycharm中的配置和简单应用

对于最常用的调试python脚本开发环境pycharm&#xff0c;如何接入deepseek是我们窥探ai代码编写的第一步&#xff0c;熟悉起来总没坏处。 1、官网安装pycharm社区版&#xff08;免费&#xff09;&#xff0c;如果需要安装专业版&#xff0c;需要另外找破解码。 2、安装Ollama…

【论文阅读】多模态——LSeg

文献基本信息 标题&#xff1a;Language-Driven Semantic Segmentation作者&#xff1a;Boyi Li、Kilian Q. Weinberger、Serge Belongie、Vladlen Koltun、Ren Ranftl单位&#xff1a;Cornell University、University of Copenhagen、Apple、Intel Labs会议/期刊&#xff1a;…

【MySQL基础-1】MySQL 用户管理指南:创建用户、修改密码与权限分配

MySQL 作为广泛使用的关系型数据库管理系统&#xff0c;用户管理和权限分配是其核心功能之一。合理创建用户、修改密码以及分配权限&#xff0c;不仅能保障数据库的安全性&#xff0c;还能有效控制用户的操作范围。本文将详细介绍如何在 MySQL 中创建用户、修改用户密码以及分配…

影刀RPA编码版与流程版解析

影刀RPA编码版是影刀RPA的一个高级版本&#xff0c;它结合了流程版的可视化操作和编码版的强大灵活性&#xff0c;以下是对影刀RPA编码版的详细介绍&#xff1a; 1. 功能对比 流程版&#xff1a; 可视化操作&#xff1a;通过拖拽式流程设计器&#xff0c;用户可以像搭积木一样…

20天 - TCP 和 UDP 有什么区别?说说 TCP 的三次握手?TCP 是用来解决什么问题?

TCP 和 UDP 有什么区别&#xff1f; TCP&#xff08;传输控制协议&#xff09;和 UDP&#xff08;用户数据报协议&#xff09;都是传输层的网络协议&#xff0c;它们的主要区别如下&#xff1a; 连接方式 TCP&#xff1a;面向连接的协议&#xff0c;类似于打电话&#xff0c…

【MySQL_05】语法简述(是语法,不详细介绍各种语句)

文章目录 一、基本规则二、标识符规则三、数据类型四、运算符五、关键字六、SQL 语句的通用语法结构 历史文章点击&#x1f449;&#xff1a;SQL &#x1f408;‍⬛github&#xff1a;https://github.com/mysql &#x1f4bb;官网&#xff1a; https://www.mysql.com &#…

JavaScript中的生成器函数详解

在 JavaScript 中&#xff0c;生成器函数 Generator Function 是一种特殊的函数&#xff0c;它允许你在函数执行过程中暂停和恢复。生成器函数通过 function* 语法定义&#xff0c;并使用 yield 关键字来控制函数的执行流程。生成器函数返回一个生成器对象&#xff0c;该对象遵…

计算机网络——交换机

一、什么是交换机&#xff1f; 交换机&#xff08;Switch&#xff09;是局域网&#xff08;LAN&#xff09;中的核心设备&#xff0c;负责在 数据链路层&#xff08;OSI第二层&#xff09;高效转发数据帧。它像一位“智能交通警察”&#xff0c;根据设备的 MAC地址 精准引导数…

Git合并工具在开发中的使用指南

在团队协作开发中&#xff0c;Git 是最常用的版本控制工具&#xff0c;而代码合并&#xff08;Merge&#xff09;是多人协作不可避免的环节。当多个开发者同时修改同一文件的相同区域时&#xff0c;Git 无法自动完成合并&#xff0c;此时需要借助合并工具&#xff08;Merge Too…

实现多语言适配

1.在res下创建多语言资源文件&#xff1a; 2.选择需要的语言 然后得到多种语言适配string文件&#xff1a; 3.代码设置多语言 object LanguageHelper {/*** 获取适配的 Context*/fun getAttachBaseContext(context: Context): Context {return if (Build.VERSION.SDK_INT > …

【学习方法一】

学习方法一 一、通用高效学习法二、学科专项方法三、工具与技术辅助四、习惯与心理策略五、避免常见误区总结六、进阶学习策略七、解决学习痛点八、场景化学习法九、资源与工具推荐十、个性化学习调整十一、长期学习心态十二、常见问题QA十三、应对特殊挑战的学习法十四、健康与…