鸿蒙学习实战之路-蓝牙设备配对与连接完全指南
配对要确认:配对时系统会弹出对话框,得用户同意才行,就像买菜得你点头确认价格一样Profile 要选对:不同设备支持不同的 Profile,别连错了连接要及时:配对后尽快连接,30 秒内效果最好状态要监听:配对和连接状态都会变,得盯着点才知道成功没。
鸿蒙学习实战之路-蓝牙设备配对与连接完全指南
最近好多朋友问我:“西兰花啊,我找到了蓝牙设备,但咋配对连接啊?” 害,这问题可问对人了!上回咱们学会了"找菜"(扫描设备),今天就来学"买菜"(配对设备)和"炒菜"(连接设备)~
🥦 先唠唠配对连接是啥
配对就像给菜称重量、算钱,确认这菜是你的了;连接就像把菜拎回家,可以开始用了。只有完成配对和连接,才能真正和蓝牙设备通信~
第一步:配齐"锅碗瓢盆"(申请权限+导入模块)
1.1 先搞定权限
就像上回说的,用蓝牙得先洗手(申请权限)。在 module.json5 里加上:
{
"module": {
"requestPermissions": [
{
"name": "ohos.permission.ACCESS_BLUETOOTH"
}
]
}
}
1.2 导入必要的模块
这次需要的模块更多了,就像炒复杂的菜要准备更多调料:
import {
connection,
a2dp,
hfp,
hid,
baseProfile,
constant,
common,
} from "@kit.ConnectivityKit";
import { BusinessError } from "@kit.BasicServicesKit";
🥦 西兰花小贴士:
这些模块分别对应不同的蓝牙功能:
connection:蓝牙连接的核心模块a2dp:音频传输(比如连接音箱)hfp:免提通话(比如连接车载蓝牙)hid:人机交互设备(比如连接蓝牙键盘、鼠标)
第二步:“看秤”(订阅配对状态变化)
配对过程中状态会变来变去,得盯着点,就像买菜时盯着秤一样:
// 定义配对状态变化回调函数
function on配对状态变化(data: connection.BondStateParam) {
console.info("配对结果: " + JSON.stringify(data));
}
try {
// 发起订阅 - 相当于盯着秤看
connection.on("bondStateChange", on配对状态变化);
} catch (err) {
console.error(
"订阅失败: " +
(err as BusinessError).code +
", " +
(err as BusinessError).message
);
}
🥦 西兰花警告:
配对状态有好几种,其中 BOND_STATE_BONDED 才是配对成功的状态哦!
第三步:“付钱”(发起配对)
找到心仪的设备,就得付钱(发起配对)啦。鸿蒙提供了两种配对方式,看你喜欢哪种~
3.1 简单粗暴法(API 20 及以前)
不知道设备地址类型?没关系,用这个简单的方法:
// 通过扫描设备流程获取的设备地址
let 目标设备地址 = "11:22:33:44:55:66";
try {
// 发起配对 - 相当于付钱拿菜
connection.pairDevice(目标设备地址).then(
() => {
console.info("开始配对");
},
(error: BusinessError) => {
console.error(
"配对失败: errCode:" + error.code + ", errMessage:" + error.message
);
}
);
} catch (err) {
console.error(
"发起配对出错: errCode:" + err.code + ", errMessage:" + err.message
);
}
3.2 精确控制法(API 21 及以后)
知道设备地址类型?可以用这个更精确的方法:
// 定义设备地址和类型
let 设备信息: common.BluetoothAddress = {
address: "11:22:33:44:55:66", // 目标设备的MAC地址(实际或虚拟的都行)
addressType: common.BluetoothAddressType.REAL, // 地址类型:REAL是实际地址,VIRTUAL是虚拟地址
};
try {
// 发起配对
connection.pairDevice(设备信息).then(
() => {
console.info("开始配对");
},
(error: BusinessError) => {
console.error(
"配对失败: errCode:" + error.code + ", errMessage:" + error.message
);
}
);
} catch (err) {
console.error(
"发起配对出错: errCode:" + err.code + ", errMessage:" + err.message
);
}
🥦 西兰花小贴士:
蓝牙设备的实际 MAC 地址是隐私信息,系统会分配虚拟 MAC 地址。如果不确定,用第一种方法更安全~
第四步:“拎菜回家”(连接设备的 Profile)
配对成功后,得连接设备的 Profile 才能用它的功能。就像买了菜得拎回家,才能做饭~
4.1 先知道设备支持啥 Profile
不同设备支持不同的 Profile(功能),得先看看它有啥:
// 已配对的设备地址
let 已配对设备地址 = "XX:XX:XX:XX:XX:XX";
try {
// 查询设备支持的Profile
let 支持的Profile列表 = await connection.getRemoteProfileUuids(
已配对设备地址
);
console.info("设备支持的Profile: " + JSON.stringify(支持的Profile列表));
} catch (err) {
console.error(
"查询失败: errCode:" + err.code + ", errMessage:" + err.message
);
}
4.2 创建 Profile 实例
就像不同的菜要用不同的锅炒,不同的 Profile 要用不同的实例:
// 创建A2DP(音频)、HFP(免提)、HID(人机交互)实例
let a2dp音频实例 = a2dp.createA2dpSrcProfile();
let hfp免提实例 = hfp.createHfpAgProfile();
let hid交互实例 = hid.createHidHostProfile();
4.3 订阅连接状态变化
连接过程中状态也会变,得盯着点:
// 定义A2DP连接状态变化回调
function on音频连接变化(data: baseProfile.StateChangeParam) {
console.info(`音频连接状态: ${JSON.stringify(data)}`);
}
// 定义HFP连接状态变化回调
function on免提连接变化(data: baseProfile.StateChangeParam) {
console.info(`免提连接状态: ${JSON.stringify(data)}`);
}
// 定义HID连接状态变化回调
function on交互连接变化(data: baseProfile.StateChangeParam) {
console.info(`交互设备连接状态: ${JSON.stringify(data)}`);
}
try {
// 订阅连接状态变化
a2dp音频实例.on("connectionStateChange", on音频连接变化);
hfp免提实例.on("connectionStateChange", on免提连接变化);
hid交互实例.on("connectionStateChange", on交互连接变化);
} catch (err) {
console.error(
"订阅连接状态失败: " +
(err as BusinessError).code +
", " +
(err as BusinessError).message
);
}
4.4 发起连接
一切准备就绪,可以拎菜回家了:
try {
// 发起连接支持的Profile
connection.connectAllowedProfiles(已配对设备地址).then(
() => {
console.info("开始连接设备");
},
(error: BusinessError) => {
console.error(
"连接失败: errCode:" + error.code + ", errMessage:" + error.message
);
}
);
} catch (err) {
console.error(
"发起连接出错: errCode:" + err.code + ", errMessage:" + err.message
);
}
🥦 西兰花警告:
配对完成后 30 秒内连接效果最好,别搁太久了!
🥦 给你整个"预制菜"(完整工具类)
为了方便大家使用,我把上面的功能封装成了一个工具类,就像超市里的预制菜,拿回去直接炒就行~
import {
connection,
a2dp,
hfp,
hid,
baseProfile,
constant,
} from "@kit.ConnectivityKit";
import { BusinessError } from "@kit.BasicServicesKit";
export class 设备配对连接管理器 {
目标设备地址: string = "";
配对状态: connection.BondState = connection.BondState.BOND_STATE_INVALID;
// 创建Profile实例
音频实例 = a2dp.createA2dpSrcProfile();
免提实例 = hfp.createHfpAgProfile();
交互实例 = hid.createHidHostProfile();
// 定义配对状态变化回调函数
on配对状态变化 = (data: connection.BondStateParam) => {
console.info("配对结果: " + JSON.stringify(data));
if (data && data.deviceId == this.目标设备地址) {
this.配对状态 = data.state; // 保存目标设备的配对状态
}
};
// 发起配对
public 开始配对(设备地址: string) {
this.目标设备地址 = 设备地址;
try {
// 订阅配对状态变化
connection.on("bondStateChange", this.on配对状态变化);
} catch (err) {
console.error(
"订阅配对状态失败: " +
(err as BusinessError).code +
", " +
(err as BusinessError).message
);
}
try {
// 发起配对
connection.pairDevice(设备地址).then(
() => {
console.info("开始配对");
},
(error: BusinessError) => {
console.error(
"配对失败: errCode:" + error.code + ", errMessage:" + error.message
);
}
);
} catch (err) {
console.error(
"发起配对出错: errCode:" + err.code + ", errMessage:" + err.message
);
}
}
// 定义连接状态变化回调
on音频连接变化 = (data: baseProfile.StateChangeParam) => {
console.info(`音频连接状态: ${JSON.stringify(data)}`);
};
on免提连接变化 = (data: baseProfile.StateChangeParam) => {
console.info(`免提连接状态: ${JSON.stringify(data)}`);
};
on交互连接变化 = (data: baseProfile.StateChangeParam) => {
console.info(`交互设备连接状态: ${JSON.stringify(data)}`);
};
// 发起连接
public async 开始连接(设备地址: string) {
try {
// 查询设备支持的Profile
let 支持的Profile列表 = await connection.getRemoteProfileUuids(设备地址);
console.info("设备支持的Profile: " + JSON.stringify(支持的Profile列表));
let 可用Profile数量 = 0;
// 检查并订阅A2DP(音频)
if (
支持的Profile列表.some(
(uuid) =>
uuid == constant.ProfileUuids.PROFILE_UUID_A2DP_SINK.toLowerCase()
)
) {
console.info("设备支持音频传输");
可用Profile数量++;
this.音频实例.on("connectionStateChange", this.on音频连接变化);
}
// 检查并订阅HFP(免提)
if (
支持的Profile列表.some(
(uuid) =>
uuid == constant.ProfileUuids.PROFILE_UUID_HFP_HF.toLowerCase()
)
) {
console.info("设备支持免提通话");
可用Profile数量++;
this.免提实例.on("connectionStateChange", this.on免提连接变化);
}
// 检查并订阅HID(人机交互)
if (
支持的Profile列表.some(
(uuid) => uuid == constant.ProfileUuids.PROFILE_UUID_HID.toLowerCase()
) ||
支持的Profile列表.some(
(uuid) =>
uuid == constant.ProfileUuids.PROFILE_UUID_HOGP.toLowerCase()
)
) {
console.info("设备支持人机交互");
可用Profile数量++;
this.交互实例.on("connectionStateChange", this.on交互连接变化);
}
// 如果有可用的Profile,就发起连接
if (可用Profile数量 > 0) {
connection.connectAllowedProfiles(设备地址).then(
() => {
console.info("开始连接设备");
},
(error: BusinessError) => {
console.error(
"连接失败: errCode:" +
error.code +
", errMessage:" +
error.message
);
}
);
}
} catch (err) {
console.error(
"连接出错: errCode:" + err.code + ", errMessage:" + err.message
);
}
}
}
// 导出实例,方便全局使用
let 设备配对连接管理器实例 = new 设备配对连接管理器();
export default 设备配对连接管理器实例 as 设备配对连接管理器;
咋用这个工具类呢?
就像炒预制菜一样简单:
import 设备配对连接管理器 from "./PairDeviceManager";
// 开始配对设备
设备配对连接管理器.开始配对("11:22:33:44:55:66");
// 配对成功后,开始连接设备
设备配对连接管理器.开始连接("11:22:33:44:55:66");
是不是超简单?^^
🥦 最后再啰嗦两句
- 配对要确认:配对时系统会弹出对话框,得用户同意才行,就像买菜得你点头确认价格一样
- Profile 要选对:不同设备支持不同的 Profile,别连错了
- 连接要及时:配对后尽快连接,30 秒内效果最好
- 状态要监听:配对和连接状态都会变,得盯着点才知道成功没
📚 推荐资料
我是盐焗西兰花,
不教理论,只给你能跑的代码和避坑指南。
下期见!🥦
更多推荐




所有评论(0)