Android 蓝牙开发-传输数据

在这里插入图片描述

概述

    传统蓝牙是通过建立REFCCOM sockect来进行通信的,类似于socket通信,一台设备需要开放服务器套接字并处于listen状态,而另一台设备使用服务器的MAC地址发起连接。连接建立后,服务器和客户端就都通过对BluetoothSocket进行读写操作来进行通信。

实现步骤

      要通过蓝牙来传输数据整体流程如下:

首次, 未配对状态

Created with Raphaël 2.3.0 开始 打开蓝牙 蓝牙已打开? 扫描 找到设备? 配对 配对成功? 连接 发送/读取数据 结束 yes no yes no yes no

已配对状态

Created with Raphaël 2.3.0 开始 打开蓝牙 蓝牙已打开? 获取配对设备 连接 发送/读取数据 结束 yes no

| 关键代码

蓝牙权限

    <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";
   @Override
   public 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 的字符串。

    字符串格式要求如下:

    1. 长度必须是 36 个字符。
    2. 字符串必须以连字符(-)分隔为五组,每组字符数分别为 8、4、4、4 和 12。
    3. 所有字符都必须是十六进制数字(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蓝牙通信机制详解

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Java圣诞树

目录 写在前面 技术需求 程序设计 代码分析 一、代码结构与主要功能概述 二、代码功能分解与分析 1. 类与常量定义 2. 绘制树的主逻辑 3. 彩色球的绘制 4. 动态效果的实现 5. 窗口初始化 三、关键特性与优点 四、总结 写在后面 写在前面 Java语言绘制精美圣诞树…

认识计算机网络

单单看这一个词语&#xff0c;有熟悉又陌生&#xff0c;让我们来重新认识一下这位大角色——计算机网络。 一、是什么 以及 怎么来的 计算机网络是指将地理位置不同的具有独立功能的多台计算机及其外部设备&#xff0c;通过通信线路和通信设备连接起来&#xff0c;在网络操作…

【再谈设计模式】享元模式~对象共享的优化妙手

一、引言 在软件开发过程中&#xff0c;我们常常面临着创建大量细粒度对象的情况&#xff0c;这可能会导致内存占用过高、性能下降等问题。享元模式&#xff08;Flyweight Pattern&#xff09;就像是一位空间管理大师&#xff0c;它能够在不影响功能的前提下&#xff0c;有效地…

用Python写炸金花游戏

文章目录 **代码分解与讲解**1. **扑克牌的生成与洗牌**2. **给玩家发牌**3. **打印玩家的手牌**4. **定义牌的优先级**5. **判断牌型**6. **确定牌型优先级**7. **比较两手牌的大小**8. **打印结果** 完整代码 以下游戏规则&#xff1a; 那么我们要实现的功能&#xff0c;就是…

WebRTC服务质量(07)- 重传机制(04) 接收NACK消息

WebRTC服务质量&#xff08;01&#xff09;- Qos概述 WebRTC服务质量&#xff08;02&#xff09;- RTP协议 WebRTC服务质量&#xff08;03&#xff09;- RTCP协议 WebRTC服务质量&#xff08;04&#xff09;- 重传机制&#xff08;01) RTX NACK概述 WebRTC服务质量&#xff08;…

Cadence学习笔记 11 PCB中器件放置

基于Cadence 17.4&#xff0c;四层板4路HDMI电路 更多Cadence学习笔记&#xff1a;Cadence学习笔记 1 原理图库绘制Cadence学习笔记 2 PCB封装绘制Cadence学习笔记 3 MCU主控原理图绘制Cadence学习笔记 4 单片机原理图绘制Cadence学习笔记 5 四路HDMI原理图绘制Cadence学习笔记…

Docker 入门:如何使用 Docker 容器化 AI 项目(二)

四、将 AI 项目容器化&#xff1a;示例实践 - 完整的图像分类与 API 服务 让我们通过一个更完整的 AI 项目示例&#xff0c;展示如何将 AI 项目容器化。我们以一个基于 TensorFlow 的图像分类模型为例&#xff0c;演示如何将训练、推理、以及 API 服务过程容器化。 4.1 创建 …

三层交换机配置

一&#xff0c;三层交换 概念&#xff1a;三层交换技术就是&#xff1a;二层交换技术三层转发技术(路由器功能)。它解决了局域网中网段划分之后&#xff0c;网段中子网必须依赖路由器进行管理的局面&#xff0c;解决了传统路由器低速&#xff0c;复杂所造成的网络瓶颈问题。 …

LabVIEW应用在工业车间

LabVIEW作为一种图形化编程语言&#xff0c;以其强大的数据采集和硬件集成功能广泛应用于工业自动化领域。在工业车间中&#xff0c;LabVIEW不仅能够实现快速开发&#xff0c;还能通过灵活的硬件接口和直观的用户界面提升生产效率和设备管理水平。尽管其高成本和初期学习门槛可…

【CSS in Depth 2 精译_094】16.2:CSS 变换在动效中的应用(下)——导航菜单的文本标签“飞入”特效与交错渲染效果的实现

当前内容所在位置&#xff08;可进入专栏查看其他译好的章节内容&#xff09; 第五部分 添加动效 ✔️【第 16 章 变换】 ✔️ 16.1 旋转、平移、缩放与倾斜 16.1.1 变换原点的更改16.1.2 多重变换的设置16.1.3 单个变换属性的设置 16.2 变换在动效中的应用 16.2.1 放大图标&am…

Qt使用QZipWriter和QZipReader来解压、压缩文件

首先感谢这位博主的无私奉献&#xff1a;Qt - 实现压缩文件、文件夹和解压缩操作 - [BORUTO] - 博客园 多文件和目录压缩时&#xff0c;不改变原始文件和目录的相对位置结构&#xff0c;需要在addFile和addDirectory时&#xff0c;需要带上相对路径&#xff0c;如下&#xff1…

PH热榜 | 2024-12-23

1. Websparks 标语&#xff1a;让你的创意变为现实的AI软件工程师 介绍&#xff1a;现在&#xff0c;构建网页应用从未如此简单快捷&#xff01;WebSparks是一个基于人工智能的平台&#xff0c;它能让开发者、设计师&#xff0c;甚至不懂编程的人&#xff0c;都能在很短的时间…

Opencv之对图片的处理和运算

Opencv实现对图片的处理和修改 目录 Opencv实现对图片的处理和修改灰度图读取灰度图转换灰度图 RBG图单通道图方法一方法二 单通道图显色合并单通道图 图片截取图片打码图片组合缩放格式1格式2 图像运算图像ma[m:n,x:y]b[m1:n1,x1:y1] add加权运算 灰度图 读取灰度图 imread(‘…

OpenLinkSaas使用手册-Git工具

在OpenLinkSaas的工具箱里面&#xff0c;最基础的一个就是Git仓库管理。Git仓库功能让git使用更加简单和强大&#xff0c;不仅可以使用常规的commit/pull/push/branch等功能外&#xff0c;还连接了Git仓库供应商的能力。 OpenLinkSass支持使用国内主流的Git仓库供应商的账号登录…

WebRTC服务质量(12)- Pacer机制(04) 向Pacer中插入数据

WebRTC服务质量&#xff08;01&#xff09;- Qos概述 WebRTC服务质量&#xff08;02&#xff09;- RTP协议 WebRTC服务质量&#xff08;03&#xff09;- RTCP协议 WebRTC服务质量&#xff08;04&#xff09;- 重传机制&#xff08;01) RTX NACK概述 WebRTC服务质量&#xff08;…

protobuf学习使用

1、概述 protobuf是Google开发的一种语言中立、平台无关、可扩展的序列化结构数据格式。允许定义一次数据结构&#xff0c;然后可以使用各种支持的语言来生成代码&#xff0c;以轻松地读写这些结构到一个二进制流中&#xff0c;如网络传输或文件&#xff0c;Protobuf支持多种编…

CTFHUB-web进阶-php

我们用蚁剑中的这个插件来做这些关卡 一.LD_PRELOAD 发现这里有一句话木马&#xff0c;并且把ant给了我们&#xff0c;我们直接连接蚁剑 右键 选择模式&#xff0c;都可以试一下&#xff0c;这里第一个就可以 点击开始 我们进入到目录&#xff0c;刷新一下&#xff0c;会有一个…

相机、镜头参数详解以及相关计算公式

一、工业相机参数 1、分辨率 相机每次采集图像的像素点数&#xff0c;也是指这个相机总共有多少个感光晶片。在采集图像时&#xff0c;相机的分辨率对检测精度有很大的影响&#xff0c;在对同样打的视场成像时&#xff0c;分辨率越高&#xff0c;对细节的展示越明显。 相机像素…

取多个集合的交集

1.我们取多个集合的交集&#xff0c;先把各个集合放入list中 List < Set < String > > listnew ArrayList<>();HashSet<String> set1new HashSet<>();set1.add( "A" );set1.add("B" );set1.add("C" );HashSet<…

leetcode之hot100---206环形链表(C++)

思路一&#xff1a;哈希表 遍历链表&#xff0c;同时借助哈希表判断当前遍历到的节点是否已经被访问过&#xff0c;如果当前节点已被访问过&#xff0c;则说明存在环 /*** Definition for singly-linked list.* struct ListNode {* int val;* ListNode *next;* L…