Android 蓝牙开发-传输数据

概述
传统蓝牙是通过建立REFCCOM sockect来进行通信的,类似于socket通信,一台设备需要开放服务器套接字并处于listen状态,而另一台设备使用服务器的MAC地址发起连接。连接建立后,服务器和客户端就都通过对BluetoothSocket进行读写操作来进行通信。
实现步骤
要通过蓝牙来传输数据整体流程如下:
首次, 未配对状态
已配对状态
| 关键代码
蓝牙权限
<uses-permission android:name="android.permission.BLUETOOTH" /><uses-permission android:name="android.permission.BLUETOOTH_CONNECT" /><uses-permission android:name="android.permission.BLUETOOTH_ADMIN" /><uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
打开或关闭蓝牙
BluetoothAdapter ba = BluetoothAdapter.getDefaultAdapter(); ba.enable(); ba.disable();
扫描设备
BluetoothAdapter.getDefaultAdapter().startDiscovery();
监听设备扫描
//Device stae IntentFilter filter = new IntentFilter(BluetoothAdapter.ACTION_STATE_CHANGED); //for discovery . filter.addAction(BluetoothDevice.ACTION_FOUND); filter.addAction(BluetoothAdapter.ACTION_DISCOVERY_STARTED); filter.addAction(BluetoothAdapter.ACTION_DISCOVERY_FINISHED);
根据ACTION_FOUND实时获取扫描到的设备, 常规会获取设备的名称和MAC
BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
String name = device.getName();
String mac = device.getAddress();
| 发起设备配对
| 配对动作大部分情况下, 连接的双方设备都会有对应的弹出窗口, 让用户选择是否配对设备.
public static boolean startPair(BluetoothDevice dev){Logger.d(TAG, "startPair(" + dev.getName() + ")");if(dev != null){try {Method createBond = BluetoothDevice.class.getDeclaredMethod("createBond");if(createBond != null){Object r = createBond.invoke(dev);return (Boolean)r;}} catch (NoSuchMethodException e) {e.printStackTrace();} catch (InvocationTargetException e) {e.printStackTrace();} catch (IllegalAccessException e) {e.printStackTrace();}}return false;
}
| 连接并发送数据
//获取已绑定的设备
Set<BluetoothDevice> devs = BluetoothAdapter.getDefaultAdapter().getBondedDevices();
BluetoothDevice target = null;//通过名称查找目标设备
for(BluetoothDevice d : devs){if(StringTools.isNotEmpty(d.getName()) && d.getName().startsWith("TARGET-")){target = d;break;}
}if(target != null) {final UUID MY_UUID = UUID.fromString("00001101-0000-1000-8000-00XXXXXXXXXX");try {BluetoothSocket socket = target.createRfcommSocketToServiceRecord(MY_UUID);socket.connect();OutputStream outputStream = socket.getOutputStream();outputStream.write(msg.getBytes());Logger.d(TAG, "sendMsgByBluetooth " + msg);outputStream.flush();InputStream is = socket.getInputStream();byte[] cache = new byte[512];int r = is.read(cache);Logger.d(TAG, "receive response: " + new String(cache));sleepx(500);is.close();outputStream.close();} catch (IOException e) {e.printStackTrace();}
}
基于系统源码的服务端
PS: 源码端可以通过获取system权限, 完成设备配对流程
监听广播并执行配对
public static final String BLUETOOTH_PARING_QUEST = "android.bluetooth.device.action.PAIRING_REQUEST";@Overridepublic void onReceive(Context context, Intent intent) {BluetoothDevice device = BluetoothTools.onPairRequest(intent);int mType = intent.getIntExtra(BluetoothDevice.EXTRA_PAIRING_VARIANT, BluetoothDevice.ERROR);int mPasskey = intent.getIntExtra(BluetoothDevice.EXTRA_PAIRING_KEY, BluetoothDevice.ERROR);BluetoothTools.pair(device, mType, mPasskeyFormatted);}
BluetoothTools.java (反射系统蓝牙部分接口)
private static void setRemoteOutOfBandData(BluetoothDevice dev){try {Method setRemoteOutOfBandData = BluetoothDevice.class.getDeclaredMethod("setRemoteOutOfBandData");if(setRemoteOutOfBandData != null){setRemoteOutOfBandData.invoke(dev);}} catch (NoSuchMethodException e) {e.printStackTrace();} catch (InvocationTargetException e) {e.printStackTrace();} catch (IllegalAccessException e) {e.printStackTrace();}}private static void setPairingConfirmation(BluetoothDevice dev, boolean bool){try {Method setPairingConfirmation = BluetoothDevice.class.getDeclaredMethod("setPairingConfirmation", Boolean.TYPE);if(setPairingConfirmation != null){setPairingConfirmation.invoke(dev, bool);}} catch (NoSuchMethodException e) {e.printStackTrace();} catch (InvocationTargetException e) {e.printStackTrace();} catch (IllegalAccessException e) {e.printStackTrace();}}private static void setPasskey(BluetoothDevice dev, int passKey){try {Method setPasskey = BluetoothDevice.class.getDeclaredMethod("setPasskey", Integer.TYPE);if(setPasskey != null){setPasskey.invoke(dev, passKey);}} catch (NoSuchMethodException e) {e.printStackTrace();} catch (InvocationTargetException e) {e.printStackTrace();} catch (IllegalAccessException e) {e.printStackTrace();}}private static byte[] convertPinToBytes(String val){try {Method convertPinToBytes = BluetoothDevice.class.getDeclaredMethod("convertPinToBytes", String.class);if(convertPinToBytes != null){return (byte[])convertPinToBytes.invoke(null, val);}} catch (NoSuchMethodException e) {e.printStackTrace();} catch (InvocationTargetException e) {e.printStackTrace();} catch (IllegalAccessException e) {e.printStackTrace();}return null;}private static void setPin(BluetoothDevice mDevice, byte[] bytes){try {Method setPin = BluetoothDevice.class.getDeclaredMethod("setPin", byte[].class);if(setPin != null){setPin.invoke(mDevice, (Object)bytes);}} catch (NoSuchMethodException e) {e.printStackTrace();} catch (InvocationTargetException e) {e.printStackTrace();} catch (IllegalAccessException e) {e.printStackTrace();}}public static void pair(BluetoothDevice dev, int type, String password){onPair(type, password, dev);}private static void onPair(int mType, String value, BluetoothDevice mDevice) {switch (mType) {case BluetoothDevice.PAIRING_VARIANT_PIN:byte[] pinBytes = convertPinToBytes(value);if (pinBytes == null) {return;}setPin(mDevice, pinBytes);break;case PAIRING_VARIANT_PASSKEY:int passkey = Integer.parseInt(value);setPasskey(mDevice, passkey);break;case BluetoothDevice.PAIRING_VARIANT_PASSKEY_CONFIRMATION:case PAIRING_VARIANT_CONSENT:setPairingConfirmation(mDevice, true);break;case PAIRING_VARIANT_DISPLAY_PASSKEY:case PAIRING_VARIANT_DISPLAY_PIN:// Do nothing.break;case PAIRING_VARIANT_OOB_CONSENT:setRemoteOutOfBandData(mDevice);break;default:Logger.e(TAG, "Incorrect pairing type received");}}
同样, 在完成配对后, 可以通过BluetoothServerSocket 来接收来自客户端的连接实现通讯:
final UUID MY_UUID = UUID.fromString("00001101-0000-1000-8000-00XXXXXXXXXX");
BluetoothServerSocket serverSocket = BluetoothAdapter.listenUsingRfcommWithServiceRecord("MyApp", MY_UUID);
Logger.d(TAG, "listen: waiting for new connect...");
BluetoothSocket clientSocket = serverSocket.accept();
InputStream inputStream = mSocket.getInputStream();
byte[] cache = new byte[512];
inputStream.read(cache);
//省略读写和关闭代码...
-
关于UUID
UUID.fromString方法用于将字符串转换为UUID对象。UUID(通用唯一标识符)是一个 128 位的值,通常表示为 32 个十六进制数字,分为五组,形式为 8-4-4-4-12 的字符串。字符串格式要求如下:
- 长度必须是 36 个字符。
- 字符串必须以连字符(-)分隔为五组,每组字符数分别为 8、4、4、4 和 12。
- 所有字符都必须是十六进制数字(0-9 和 a-f 或 A-F)。
示例:
import java.util.UUID;public class Main {public static void main(String[] args) {String uuidString = "123e4567-e89b-12d3-a456-426614174000";UUID uuid = UUID.fromString(uuidString);System.out.println("UUID: " + uuid);} }如果你尝试使用不符合格式的字符串,
UUID.fromString方法将抛出IllegalArgumentException。例如:import java.util.UUID;public class Main {public static void main(String[] args) {String invalidUuidString = "123e4567-e89b-12d3-a456-4266141740"; // 缺少一个字符try {UUID uuid = UUID.fromString(invalidUuidString);System.out.println("UUID: " + uuid);} catch (IllegalArgumentException e) {System.out.println("Invalid UUID string: " + invalidUuidString);}} }在这个例子中,
invalidUuidString不符合 UUID 字符串格式要求,因此UUID.fromString方法将抛出IllegalArgumentException。
PS:UUID对字符大小写不敏感
参考系统源码 packages/apps/Settings
配对窗口: AndroidManifest.xml<activity android:name=".bluetooth.BluetoothPairingDialog"android:excludeFromRecents="true"android:windowSoftInputMode="stateVisible|adjustResize"android:theme="@*android:style/Theme.DeviceDefault.Settings.Dialog.NoActionBar"><intent-filter android:priority="1"><action android:name="android.bluetooth.device.action.PAIRING_REQUEST" /><category android:name="android.intent.category.DEFAULT" /></intent-filter></activity>src/com/android/settings/bluetooth/BluetoothParingController.java
src/com/android/settings/bluetooth/BluetoothParingDialogFragment.java
参考
android蓝牙开发 蓝牙设备的查找和连接
Android蓝牙通信机制详解
相关文章:
Android 蓝牙开发-传输数据
概述 传统蓝牙是通过建立REFCCOM sockect来进行通信的,类似于socket通信,一台设备需要开放服务器套接字并处于listen状态,而另一台设备使用服务器的MAC地址发起连接。连接建立后,服务器和客户端就都通过对BluetoothSocket进行读写…...
webrtc获取IceCandidate流程
在WebRTC(Web Real-Time Communication)中,ICECandidate是一个关键概念,它用于描述在建立点对点(P2P)连接时可以考虑的潜在通信端点。以下是关于WebRTC中ICECandidate的详细解释: 一、ICECandidate的定义 ICECandidate对象通常包含以下关键属性: foundation:用于唯一…...
每天40分玩转Django:Django静态文件
Django静态文件 一、今日学习内容概述 学习模块重要程度主要内容静态文件配置⭐⭐⭐⭐⭐基础设置、路径配置CDN集成⭐⭐⭐⭐⭐CDN配置、资源优化静态文件处理⭐⭐⭐⭐压缩、版本控制部署优化⭐⭐⭐⭐性能优化、缓存策略 二、基础配置 # settings.py import os# 静态文件配置…...
Linux 线程池
1.概念介绍 线程池是一种多线程处理形式,它维护着多个线程,这些线程处于等待状态,随时准备接受任务并执行。线程池的主要目的是为了提高系统的性能和资源利用率,避免在处理短时间任务时频繁创建和销毁线程所带来的开销。 线程池…...
windows使用zip包安装MySQL
windows通过zip包安装MySQL windows通过zip包安装MySQL下载MySQL的zip安装包创建安装目录和数据目录解压zip安装包创建配置目录 etc 和 配置文件 my.ini安装MySQL进入解压后的bin目录执行命令初始化执行命令安装 验证安装查看服务已安装 启动MySQL查看服务运行情况修改密码创建…...
深度学习实战之超分辨率算法(tensorflow)——ESPCN
espcn原理算法请参考上一篇论文,这里主要给实现。 数据集如下:尺寸相等即可 针对数据集,生成样本代码preeate_data.py import imageio from scipy import misc, ndimage import numpy as np import imghdr import shutil import os import…...
Android unitTest 单元测试用例编写(初始)
文章目录 了解测试相关库导入依赖库新建测试文件示例执行查看结果网页结果其他 本片讲解的重点是unitTest,而不是androidTest哦 了解测试相关库 androidx.compose.ui:ui-test-junit4: 用于Compose UI的JUnit 4测试库。 它提供了测试Compose UI组件的工具和API。 and…...
C++简明教程(10)(初识类)
类的教程 C 类的完整教程 C 中,类(class)是面向对象编程的核心概念,用于定义对象的属性(数据成员)和行为(成员函数)。本教程将带你从零开始,循序渐进地学习如何定义和使…...
光谱相机的工作原理
光谱相机的工作原理主要基于不同物质对不同波长光的吸收、反射和透射特性存在差异,以下是其具体工作过程: 一、光的收集 目标物体在光源照射下,其表面会对光产生吸收、反射和透射等相互作用。光谱相机的光学系统(如透镜、反射镜…...
【Linux进程】基于管道实现进程池
目录 前言 1. 进程池 1.1 基本结构: 1.2. 池化技术 1.3. 思路分析 1.4. 代码实现 总结 前言 上篇文章介绍了管道及其使用,本文在管道的基础上,通过匿名管道来实现一个进程池; 1. 进程池 父进程创建一组子进程,子进…...
软件测试之单元测试
🍅 点击文末小卡片,免费获取软件测试全套资料,资料在手,涨薪更快 一、何为单测 测试有黑盒测试和白盒测试之分,黑盒测试顾名思义就是我们不了解盒子的内部结构,我们通过文档或者对该功能的理解,…...
vscode+编程AI配置、使用说明
文章目录 [toc]1、概述2、github copilot2.1 配置2.2 使用文档2.3 使用说明 3、文心快码(Baidu Comate)3.1 配置3.2 使用文档3.3 使用说明 4、豆包(MarsCode)4.1 配置4.2 使用文档4.3 使用说明 5、通义灵码(TONGYI Lin…...
007-spring-bean的相关配置(重要)
spring-bean的相关配置...
【唐叔学算法】第19天:交换排序-冒泡排序与快速排序的深度解析及Java实现
引言 排序算法是计算机科学中的基础问题,而交换排序作为其中一类经典的排序方法,因其简单直观的思想和易于实现的特点,在初学者中广受欢迎。交换排序的核心思想是通过不断交换相邻元素来达到排序的目的。本文将深入探讨两种典型的交换排序算…...
合并 Python 中的字典
合并 Python 中的字典 如何在 Python 中合并字典? 这取决于你对“合并”一词的具体定义。 在 Python 中使用 | 操作符合并字典 首先,让我们讨论合并字典的最简单方法,这通常已经足够满足你的需求。 以下是两个字典: >>…...
使用Python实现自动化文档生成工具:提升文档编写效率的利器
友友们好! 我的新专栏《Python进阶》正式启动啦!这是一个专为那些渴望提升Python技能的朋友们量身打造的专栏,无论你是已经有一定基础的开发者,还是希望深入挖掘Python潜力的爱好者,这里都将是你不可错过的宝藏。 在这个专栏中,你将会找到: ● 深入解析:每一篇文章都将…...
uniapp使用live-pusher实现模拟人脸识别效果
需求: 1、前端实现模拟用户人脸识别,识别成功后抓取视频流或认证的一张静态图给服务端。 2、服务端调用第三方活体认证接口,验证前端传递的人脸是否存在,把认证结果反馈给前端。 3、前端根据服务端返回的状态,显示在…...
【JavaSE】【网络原理】初识网络
目录 一、网络互联二、局域网与广域网三、网络通信基础3.1 IP地址3.2 端口号3.3 网络协议3.4 五元组 四、协议分层4.1 OSI七层网络模型4.2 TCP/IP五层(四层)网络模型4.3 网络设备 五、网络数据通信基本流程。5.1 封装和分用5.2 简述过程 一、网络互联 网络互联: 网…...
鸿蒙之路的坑
1、系统 Windows 10 家庭版不可用模拟器 对应的解决方案【坑】 升级系统版本 直接更改密钥可自动升级系统 密钥找对应系统的(例:windows 10专业版) 升级完之后要激活 坑1、升级完后事先创建好的模拟器还是无法启动 解决:删除模拟…...
Python生日祝福烟花
1. 实现效果 2. 素材加载 2个图片和3个音频 shoot_image pygame.image.load(shoot(已去底).jpg) # 加载拼接的发射图像 flower_image pygame.image.load(flower.jpg) # 加载拼接的烟花图 烟花不好去底 # 调整图像的像素为原图的1/2 因为图像相对于界面来说有些大 shoo…...
超短脉冲激光自聚焦效应
前言与目录 强激光引起自聚焦效应机理 超短脉冲激光在脆性材料内部加工时引起的自聚焦效应,这是一种非线性光学现象,主要涉及光学克尔效应和材料的非线性光学特性。 自聚焦效应可以产生局部的强光场,对材料产生非线性响应,可能…...
CTF show Web 红包题第六弹
提示 1.不是SQL注入 2.需要找关键源码 思路 进入页面发现是一个登录框,很难让人不联想到SQL注入,但提示都说了不是SQL注入,所以就不往这方面想了 先查看一下网页源码,发现一段JavaScript代码,有一个关键类ctfs…...
Cesium1.95中高性能加载1500个点
一、基本方式: 图标使用.png比.svg性能要好 <template><div id"cesiumContainer"></div><div class"toolbar"><button id"resetButton">重新生成点</button><span id"countDisplay&qu…...
【解密LSTM、GRU如何解决传统RNN梯度消失问题】
解密LSTM与GRU:如何让RNN变得更聪明? 在深度学习的世界里,循环神经网络(RNN)以其卓越的序列数据处理能力广泛应用于自然语言处理、时间序列预测等领域。然而,传统RNN存在的一个严重问题——梯度消失&#…...
el-switch文字内置
el-switch文字内置 效果 vue <div style"color:#ffffff;font-size:14px;float:left;margin-bottom:5px;margin-right:5px;">自动加载</div> <el-switch v-model"value" active-color"#3E99FB" inactive-color"#DCDFE6"…...
对WWDC 2025 Keynote 内容的预测
借助我们以往对苹果公司发展路径的深入研究经验,以及大语言模型的分析能力,我们系统梳理了多年来苹果 WWDC 主题演讲的规律。在 WWDC 2025 即将揭幕之际,我们让 ChatGPT 对今年的 Keynote 内容进行了一个初步预测,聊作存档。等到明…...
自然语言处理——循环神经网络
自然语言处理——循环神经网络 循环神经网络应用到基于机器学习的自然语言处理任务序列到类别同步的序列到序列模式异步的序列到序列模式 参数学习和长程依赖问题基于门控的循环神经网络门控循环单元(GRU)长短期记忆神经网络(LSTM)…...
Spring AI与Spring Modulith核心技术解析
Spring AI核心架构解析 Spring AI(https://spring.io/projects/spring-ai)作为Spring生态中的AI集成框架,其核心设计理念是通过模块化架构降低AI应用的开发复杂度。与Python生态中的LangChain/LlamaIndex等工具类似,但特别为多语…...
ABAP设计模式之---“简单设计原则(Simple Design)”
“Simple Design”(简单设计)是软件开发中的一个重要理念,倡导以最简单的方式实现软件功能,以确保代码清晰易懂、易维护,并在项目需求变化时能够快速适应。 其核心目标是避免复杂和过度设计,遵循“让事情保…...
让回归模型不再被异常值“带跑偏“,MSE和Cauchy损失函数在噪声数据环境下的实战对比
在机器学习的回归分析中,损失函数的选择对模型性能具有决定性影响。均方误差(MSE)作为经典的损失函数,在处理干净数据时表现优异,但在面对包含异常值的噪声数据时,其对大误差的二次惩罚机制往往导致模型参数…...
