鸿蒙星闪服务(NearLink Kit)介绍
本文基于华为官方文档整理,详细介绍HarmonyOS NearLink Kit的核心概念、API接口和开发实践。
一、NearLink Kit简介
1.1 什么是NearLink Kit
NearLink Kit(星闪服务)提供一种低功耗、高速率的短距离通信服务,支持星闪设备之间的连接、数据交互。
- 中心设备:可以通过扫描发现外围设备,并发起连接
- 外围设备:可以通过发送广播的方式被中心设备发现,连接后进行数据传输
1.2 典型应用场景
-
智能外设连接
- 中心设备和外围设备鼠标通过星闪配对连接后,使用鼠标作为输入控制中心设备
- 中心设备和外围设备手写笔通过星闪配对连接后,使用手写笔作为输入操作中心设备
-
智能座舱
- 车内设备间的低延迟通信
- 车钥匙无感解锁
-
工业物联网
- 传感器数据实时采集
- 多设备并发连接
1.3 约束与限制
- 设备限制:NearLink Kit支持的设备品类包括Phone、PC/2in1、TV、Tablet 和 Wearable
- 运行环境:当前NearLink Kit相关能力只支持在真机上运行,暂不支持在模拟器上运行
- 设备支持确认:进入"设置 > 多设备协同"界面,确认"星闪"选项存在
二、核心术语
2.1 Data Transfer
基于应用交互端口信息管理协议,提供基于交互端口的数据传输能力。
2.2 SSAP
SparkLink Service Access Protocol(星闪服务交互协议):
- 定义了服务结构的发现过程和访问过程,以及过程中需要使用的信令
- 通过服务管理,星闪设备之间能够实现服务层面的互联互通
- 采用服务端-客户端交互模型,服务端提供服务能力,客户端通过SSAP定义的信令和流程获取、理解和访问服务端提供的服务
2.3 星闪标准服务UUID格式
通用唯一标识(UUID)用来指示条目描述的具体内容。星闪支持的UUID格式形如:
37BEA880-FC70-11EA-B720-00000000FDEE
- 前112比特由基础标识决定:
37BEA880-FC70-11EA-B720-000000000000(固定值) - 后16比特通用唯一标识指示标准服务或标准服务成员
三、开发准备
3.1 环境要求
- DevEco Studio:最新版本
- SDK:支持NearLink Kit的SDK版本
- 真机设备:确认设备支持星闪功能
3.2 权限配置
在module.json5中声明权限
{
"module": {
"requestPermissions": [
{
"name": "ohos.permission.ACCESS_NEARLINK",
"reason": "$string:nearlink_permission_reason",
"usedScene": {
"abilities": [
"EntryAbility"
],
"when": "inuse"
}
}
]
}
}
动态申请权限
开发者需要在应用中动态申请星闪权限 ohos.permission.ACCESS_NEARLINK,具体申请方式请参考官方文档"向用户申请授权"。
四、核心API接口
4.1 模块导入
// 管理模块
import { manager } from '@kit.NearLinkKit';
// 广播模块
import { advertising } from '@kit.NearLinkKit';
// 扫描模块
import { scan } from '@kit.NearLinkKit';
// 数据传输模块
import { dataTransfer } from '@kit.NearLinkKit';
// 远端设备模块
import { remoteDevice } from '@kit.NearLinkKit';
// 错误处理
import { BusinessError } from '@kit.BasicServicesKit';
4.2 星闪状态管理API
| 接口名 | 描述 |
|---|---|
getState(): NearlinkState | 主动查询星闪开关状态 |
on(type: 'stateChange', callback: Callback<NearlinkState>): void | 订阅星闪开关状态变化事件 |
off(type: 'stateChange', callback?: Callback<NearlinkState>): void | 取消订阅星闪开关状态变化事件 |
4.3 广播API
| 接口名 | 描述 |
|---|---|
startAdvertising(advertisingParams: AdvertisingParams): Promise<number> | 启动星闪广播 |
stopAdvertising(advertisingId: number): Promise<void> | 停止星闪广播 |
on(type: 'advertisingStateChange', callback: Callback<AdvertisingStateChangeInfo>): void | 订阅星闪广播状态变化事件 |
off(type: 'advertisingStateChange', callback?: Callback<AdvertisingStateChangeInfo>): void | 取消订阅星闪广播状态变化事件 |
4.4 扫描API
| 接口名 | 描述 |
|---|---|
startScan(filters: Array<ScanFilters>, options?: ScanOptions): Promise<void> | 启动星闪扫描 |
stopScan(): Promise<void> | 停止星闪扫描 |
on(type: 'deviceFound', callback: Callback<Array<ScanResults>>): void | 订阅扫描结果 |
off(type: 'deviceFound', callback?: Callback<Array<ScanResults>>): void | 取消订阅扫描结果 |
4.5 数据传输API
| 接口名 | 描述 |
|---|---|
createPort(uuid: string): void | 注册端口服务 |
connect(params: ConnectionParams): Promise<void> | 连接远端设备,建立端口通道 |
writeData(params: DataParams): Promise<void> | 通过设备地址和UUID向远端设备发数据 |
on(type: 'connectionStateChanged', callback: Callback<ConnectionResult>): void | 订阅端口通道连接状态变更事件 |
on(type: 'readData', callback: Callback<DataParams>): void | 订阅端口通道数据接收事件 |
4.6 远端设备API
| 接口名 | 描述 |
|---|---|
createRemoteDevice(address: string): RemoteDevice | 创建远端设备对象 |
startPairing(): Promise<void> | 发起配对 |
getAcbState(): Promise<AcbState> | 查询链路加密状态 |
五、开发流程与实现
5.1 查询星闪开关状态
sequenceDiagram
participant App as 应用
participant Manager as NearLink Manager
participant System as 系统
App->>Manager: 1. 主动查询getState()
Manager->>System: 查询星闪状态
System-->>Manager: 返回状态
Manager-->>App: STATE_ON/STATE_OFF
App->>Manager: 2. 订阅on('stateChange')
Manager-->>App: 订阅成功
User->>System: 用户切换星闪开关
System->>Manager: 状态变化通知
Manager-->>App: 回调通知新状态
代码实现
import { manager } from '@kit.NearLinkKit';
import { BusinessError } from '@kit.BasicServicesKit';
// 方式1:主动查询星闪状态
function queryNearlinkState() {
try {
let state: manager.NearlinkState = manager.getState();
console.info('state = ' + JSON.stringify(state));
} catch (err) {
console.error('errCode: ' + (err as BusinessError).code + ', errMessage: ' + (err as BusinessError).message);
}
}
// 方式2:订阅星闪开关状态变化
let onReceiveEvent: (data: manager.NearlinkState) => void = (data: manager.NearlinkState) => {
console.info('nearlink state = ' + JSON.stringify(data));
}
try {
manager.on('stateChange', onReceiveEvent);
} catch (err) {
console.error('errCode: ' + (err as BusinessError).code + ', errMessage: ' + (err as BusinessError).message);
}
// 取消订阅
try {
manager.off('stateChange', onReceiveEvent);
} catch (err) {
console.error('errCode: ' + (err as BusinessError).code + ', errMessage: ' + (err as BusinessError).message);
}
5.2 发送星闪广播
sequenceDiagram
participant App as 外围设备应用
participant Advertising as Advertising模块
participant Radio as 星闪无线电
App->>Advertising: 1. 订阅on('advertisingStateChange')
Advertising-->>App: 订阅成功
App->>Advertising: 2. 构造广播参数
App->>Advertising: 3. startAdvertising()
Advertising->>Radio: 启动广播发射
Radio-->>Advertising: 广播启动成功
Advertising-->>App: 返回advertisingId
loop 持续广播
Radio->>Radio: 周期性发送广播包
end
App->>Advertising: 4. stopAdvertising(advertisingId)
Advertising->>Radio: 停止广播
Radio-->>Advertising: 广播已停止
Advertising-->>App: 停止成功
代码实现
import { advertising } from '@kit.NearLinkKit';
import { BusinessError } from '@kit.BasicServicesKit';
// 1. 订阅星闪广播状态变化事件
let onReceiveEvent: (data: advertising.AdvertisingStateChangeInfo) => void =
(data: advertising.AdvertisingStateChangeInfo) => {
console.info('advertisingId:' + data.advertisingId);
console.info('advertisingState:' + data.state);
}
try {
advertising.on('advertisingStateChange', onReceiveEvent);
} catch (err) {
console.error('errCode: ' + (err as BusinessError).code + ', errMessage: ' + (err as BusinessError).message);
}
// 2. 构造广播参数及数据
let manufactureValueBuffer = new Uint8Array(4);
manufactureValueBuffer[0] = 1;
manufactureValueBuffer[1] = 2;
manufactureValueBuffer[2] = 3;
manufactureValueBuffer[3] = 4;
let serviceValueBuffer = new Uint8Array(4);
serviceValueBuffer[0] = 4;
serviceValueBuffer[1] = 6;
serviceValueBuffer[2] = 7;
serviceValueBuffer[3] = 8;
let setting: advertising.AdvertisingSettings = {
interval: 5000,
power: advertising.TxPowerMode.ADV_TX_POWER_LOW
};
let manufactureDataUnit: advertising.ManufacturerData = {
manufacturerId: 4567,
manufacturerData: manufactureValueBuffer.buffer
};
let serviceDataUnit: advertising.ServiceData = {
serviceUuid: "37bea880-fc70-11ea-b720-000000001234",
serviceData: serviceValueBuffer.buffer
};
let advData: advertising.AdvertisingData = {
serviceUuids: ["37bea880-fc70-11ea-b720-000000001234"],
manufactureData: [manufactureDataUnit],
serviceData: [serviceDataUnit]
};
let advertisingParams: advertising.AdvertisingParams = {
advertisingSettings: setting,
advertisingData: advData
};
// 3. 启动广播
let advertisingId: number = 0;
try {
advertising.startAdvertising(advertisingParams).then((id: number) => {
advertisingId = id;
console.info("start advertising success, id: " + id);
}).catch((err: BusinessError) => {
console.error('errCode: ' + err.code + ', errMessage: ' + err.message);
});
} catch (err) {
console.error('errCode: ' + (err as BusinessError).code + ', errMessage: ' + (err as BusinessError).message);
}
// 4. 停止广播
try {
advertising.stopAdvertising(advertisingId).then(() => {
console.info("stop advertising success");
}).catch((err: BusinessError) => {
console.error('errCode: ' + err.code + ', errMessage: ' + err.message);
});
} catch (err) {
console.error('errCode: ' + (err as BusinessError).code + ', errMessage: ' + (err as BusinessError).message);
}
5.3 发起星闪扫描
sequenceDiagram
participant App as 中心设备应用
participant Scan as Scan模块
participant Radio as 星闪无线电
participant Peripheral as 外围设备
App->>Scan: 1. 订阅on('deviceFound')
Scan-->>App: 订阅成功
App->>Scan: 2. 配置扫描过滤器
App->>Scan: 3. startScan()
Scan->>Radio: 启动扫描
loop 扫描周期
Radio->>Peripheral: 发送扫描请求
Peripheral-->>Radio: 返回广播数据
Radio->>Scan: 上报扫描结果
Scan->>Scan: 应用过滤器筛选
Scan-->>App: 回调deviceFound
end
App->>Scan: 4. stopScan()
Scan->>Radio: 停止扫描
Radio-->>Scan: 扫描已停止
Scan-->>App: 停止成功
代码实现
import { scan } from '@kit.NearLinkKit';
import { BusinessError } from '@kit.BasicServicesKit';
// 1. 订阅扫描结果
let onReceiveEvent: (data: Array<scan.ScanResults>) => void =
(data: Array<scan.ScanResults>) => {
console.info('scan result addr:' + data[0].address + ' name:' + data[0].deviceName);
}
try {
scan.on("deviceFound", onReceiveEvent);
// 订阅星闪扫描结果。返回的扫描结果中携带的地址为远端设备随机地址。
} catch (err) {
console.error('errCode: ' + (err as BusinessError).code + ', errMessage: ' + (err as BusinessError).message);
}
// 2. 配置扫描参数
// 扫描过滤器至少携带一个过滤条件,否则扫描过滤器无效
// 过滤器可以配置多组,组之间的条件是或的关系
// 一组过滤器内的条件是与的关系
let scanFilter1: scan.ScanFilters = {
address: "11:22:33:44:AA:BB", // 期望扫描到的外围设备1的地址
deviceName: "deviceName1" // 期望扫描到的外围设备1的名称
};
let scanFilter2: scan.ScanFilters = {
address: "22:33:44:AB:CD:EF", // 期望扫描到的外围设备2的地址
deviceName: "deviceName2" // 期望扫描到的外围设备2的名称
};
let scanOptions: scan.ScanOptions = {
scanMode: scan.ScanMode.SCAN_MODE_LOW_POWER
}
// 3. 开启星闪扫描
try {
scan.startScan([scanFilter1, scanFilter2], scanOptions).then(() => {
console.info("start scan success");
}).catch((err: BusinessError) => {
console.error('errCode: ' + err.code + ', errMessage: ' + err.message);
});
} catch (err) {
console.error('errCode: ' + (err as BusinessError).code + ', errMessage: ' + (err as BusinessError).message);
}
// 4. 停止星闪扫描
try {
scan.stopScan().then(() => {
console.info("stop scan success");
}).catch((err: BusinessError) => {
console.error('errCode: ' + err.code + ', errMessage: ' + err.message);
});
} catch (err) {
console.error('errCode: ' + (err as BusinessError).code + ', errMessage: ' + (err as BusinessError).message);
}
5.4 使用星闪传输数据
sequenceDiagram
participant ClientApp as 客户端应用
participant ServerApp as 服务端应用
participant DataTransfer as DataTransfer模块
participant Radio as 星闪无线电
ServerApp->>DataTransfer: 1. createPort(uuid)
DataTransfer-->>ServerApp: 端口注册成功
ClientApp->>DataTransfer: 2. createPort(uuid)
DataTransfer-->>ClientApp: 端口注册成功
ServerApp->>DataTransfer: 3. on('connectionStateChanged')
ServerApp->>DataTransfer: 4. on('readData')
ClientApp->>DataTransfer: 5. on('connectionStateChanged')
ClientApp->>DataTransfer: 6. on('readData')
ClientApp->>DataTransfer: 7. connect(params)
DataTransfer->>Radio: 建立连接
Radio->>Radio: 链路建立
Radio-->>DataTransfer: 连接成功
DataTransfer-->>ServerApp: connectionStateChanged回调
DataTransfer-->>ClientApp: connectionStateChanged回调
ClientApp->>DataTransfer: 8. writeData(params)
DataTransfer->>Radio: 发送数据
Radio-->>ServerApp: 数据到达
DataTransfer-->>ServerApp: readData回调
ServerApp->>DataTransfer: 9. writeData(params)
DataTransfer->>Radio: 发送响应数据
Radio-->>ClientApp: 数据到达
DataTransfer-->>ClientApp: readData回调
场景说明
星闪设备间已建立起逻辑链路基础上,支持应用基于Nearlink技术进行设备间的数据传输。
重要提示:
- 数据传输通道不保证链路加密。如需加密数传,需先进行配对流程,通过
startPairing接口发起 - 链路是否加密可通过
getAcbState接口查询,ENCRYPTED状态表示链路已加密
完整代码实现
import { dataTransfer } from '@kit.NearLinkKit';
import { remoteDevice } from '@kit.NearLinkKit';
import { BusinessError } from '@kit.BasicServicesKit';
// 步骤1:导入相关模块(已在上方完成)
// 步骤2:与远端设备配对加密(可选,如需加密数传,则需执行此步骤)
let addr: string = '00:11:22:33:AA:FF'; // 扫描获取到的远端设备地址
let device: remoteDevice.RemoteDevice;
try {
device = remoteDevice.createRemoteDevice(addr);
device.startPairing().then(() => {
console.info('start pairing success');
}).catch((err: BusinessError) => {
console.error('errCode: ' + err.code + ', errMessage: ' + err.message);
});
} catch (err) {
console.error('errCode: ' + (err as BusinessError).code + ', errMessage: ' + (err as BusinessError).message);
}
// 步骤3:注册端口通道,发送端和接收端均需注册,并需保证发送端和接收端UUID相同
let serviceUuid: string = 'FFFFFFFF-FC70-11EA-B720-000078951234'; // 星闪服务UUID
try {
dataTransfer.createPort(serviceUuid);
console.info('create port success');
} catch (err) {
console.error('errCode: ' + (err as BusinessError).code + ', errMessage: ' + (err as BusinessError).message);
}
// 步骤4:订阅端口通道连接状态变更事件
let onReceiveConnectionStateEvent: (data: dataTransfer.ConnectionResult) => void =
(data: dataTransfer.ConnectionResult) => {
console.info('data: ' + JSON.stringify(data));
}
try {
dataTransfer.on('connectionStateChanged', onReceiveConnectionStateEvent);
} catch (err) {
console.error('errCode: ' + (err as BusinessError).code + ', errMessage: ' + (err as BusinessError).message);
}
// 步骤5:订阅端口通道数据接收事件
let onReceiveReadDataEvent: (data: dataTransfer.DataParams) => void =
(data: dataTransfer.DataParams) => {
console.info('data: ' + JSON.stringify(data));
}
try {
dataTransfer.on('readData', onReceiveReadDataEvent);
} catch (err) {
console.error('errCode: ' + (err as BusinessError).code + ', errMessage: ' + (err as BusinessError).message);
}
// 步骤6:连接远端设备,建立端口通道
let connectionParams: dataTransfer.ConnectionParams = {
address: addr, // 扫描获取到的远端设备地址
uuid: serviceUuid, // 星闪服务UUID
mtu: 1024, // 期望发送数据包的字节大小,可选参数
};
try {
dataTransfer.connect(connectionParams).then(() => {
console.info('connect success');
}).catch((err: BusinessError) => {
console.error('errCode: ' + err.code + ', errMessage: ' + err.message);
});
} catch (err) {
console.error('errCode: ' + (err as BusinessError).code + ', errMessage: ' + (err as BusinessError).message);
}
// 步骤7:通过设备地址和UUID向远端设备发数据
let transferValueBuffer: Uint8Array = new Uint8Array(4);
transferValueBuffer[0] = 1;
transferValueBuffer[1] = 2;
transferValueBuffer[2] = 3;
transferValueBuffer[3] = 4;
let dataParams: dataTransfer.DataParams = {
address: addr, // 星闪远端设备地址
uuid: serviceUuid, // 星闪服务UUID
data: transferValueBuffer.buffer, // 星闪设备间传输的数据
};
try {
dataTransfer.writeData(dataParams).then(() => {
console.info('writeData success');
}).catch((err: BusinessError) => {
console.error('errCode: ' + err.code + ', errMessage: ' + err.message);
});
} catch (err) {
console.error('errCode: ' + (err as BusinessError).code + ', errMessage: ' + (err as BusinessError).message);
}
六、实战案例:智能车钥匙
6.1 应用场景
利用星闪技术实现智能车钥匙功能:
- 用户携带手机靠近车辆时,自动解锁
- 用户远离车辆时,自动上锁
- 基于RSSI信号强度判断用户与车辆的距离
6.2 完整实现
import { scan } from '@kit.NearLinkKit';
import { dataTransfer } from '@kit.NearLinkKit';
import { remoteDevice } from '@kit.NearLinkKit';
import { BusinessError } from '@kit.BasicServicesKit';
class SmartCarKey {
private serviceUuid: string = '37BEA880-FC70-11EA-B720-000078951234';
private carAddress: string = '';
private rssiThresholdNear: number = -50; // 靠近阈值
private rssiThresholdFar: number = -70; // 远离阈值
private isConnected: boolean = false;
private currentRssi: number = -100;
// 1. 启动车钥匙服务
async start(): Promise<void> {
// 1.1 注册数据端口
this.registerPort();
// 1.2 订阅扫描结果
this.subscribeDeviceFound();
// 1.3 订阅连接状态和数据接收
this.subscribeConnection();
// 1.4 开始扫描车辆
await this.startScanCar();
}
// 注册数据端口
private registerPort(): void {
try {
dataTransfer.createPort(this.serviceUuid);
console.info('Port registered successfully');
} catch (err) {
console.error('Register port failed:', (err as BusinessError).message);
}
}
// 订阅设备发现
private subscribeDeviceFound(): void {
let onDeviceFound: (data: Array<scan.ScanResults>) => void =
(data: Array<scan.ScanResults>) => {
for (let device of data) {
if (device.deviceName === 'HarmonyOS_Car') {
this.carAddress = device.address;
this.currentRssi = device.rssi;
console.info(`Found car: ${device.address}, RSSI: ${device.rssi}`);
// 发现目标车辆后,停止扫描并连接
this.stopScan();
this.connectToCar();
}
}
}
try {
scan.on('deviceFound', onDeviceFound);
} catch (err) {
console.error('Subscribe device found failed:', (err as BusinessError).message);
}
}
// 订阅连接和数据
private subscribeConnection(): void {
// 订阅连接状态变化
let onConnectionStateChanged: (data: dataTransfer.ConnectionResult) => void =
(data: dataTransfer.ConnectionResult) => {
console.info('Connection state:', JSON.stringify(data));
this.isConnected = (data.state === 'CONNECTED');
if (this.isConnected) {
// 连接成功后,开始监控距离
this.startDistanceMonitoring();
}
}
// 订阅数据接收
let onReadData: (data: dataTransfer.DataParams) => void =
(data: dataTransfer.DataParams) => {
console.info('Received data:', JSON.stringify(data));
// 处理车辆返回的响应
this.handleCarResponse(data);
}
try {
dataTransfer.on('connectionStateChanged', onConnectionStateChanged);
dataTransfer.on('readData', onReadData);
} catch (err) {
console.error('Subscribe connection failed:', (err as BusinessError).message);
}
}
// 开始扫描车辆
private async startScanCar(): Promise<void> {
let scanFilter: scan.ScanFilters = {
deviceName: 'HarmonyOS_Car'
};
let scanOptions: scan.ScanOptions = {
scanMode: scan.ScanMode.SCAN_MODE_LOW_POWER
};
try {
await scan.startScan([scanFilter], scanOptions);
console.info('Start scan car');
} catch (err) {
console.error('Start scan failed:', (err as BusinessError).message);
}
}
// 停止扫描
private async stopScan(): Promise<void> {
try {
await scan.stopScan();
console.info('Stop scan');
} catch (err) {
console.error('Stop scan failed:', (err as BusinessError).message);
}
}
// 连接到车辆
private async connectToCar(): Promise<void> {
let connectionParams: dataTransfer.ConnectionParams = {
address: this.carAddress,
uuid: this.serviceUuid,
mtu: 512
};
try {
await dataTransfer.connect(connectionParams);
console.info('Connected to car');
} catch (err) {
console.error('Connect failed:', (err as BusinessError).message);
}
}
// 开始距离监控
private startDistanceMonitoring(): void {
setInterval(() => {
if (!this.isConnected) return;
// 根据RSSI判断距离
if (this.currentRssi > this.rssiThresholdNear) {
// 用户靠近车辆,发送解锁命令
this.unlockCar();
} else if (this.currentRssi < this.rssiThresholdFar) {
// 用户远离车辆,发送上锁命令
this.lockCar();
}
}, 1000);
}
// 解锁车辆
private async unlockCar(): Promise<void> {
let command = {
action: 'unlock',
timestamp: Date.now()
};
await this.sendCommand(command);
console.info('Unlock command sent');
}
// 上锁车辆
private async lockCar(): Promise<void> {
let command = {
action: 'lock',
timestamp: Date.now()
};
await this.sendCommand(command);
console.info('Lock command sent');
}
// 发送命令
private async sendCommand(command: any): Promise<void> {
let encoder = new TextEncoder();
let commandData = encoder.encode(JSON.stringify(command));
let dataParams: dataTransfer.DataParams = {
address: this.carAddress,
uuid: this.serviceUuid,
data: commandData.buffer
};
try {
await dataTransfer.writeData(dataParams);
} catch (err) {
console.error('Send command failed:', (err as BusinessError).message);
}
}
// 处理车辆响应
private handleCarResponse(data: dataTransfer.DataParams): void {
let decoder = new TextDecoder();
let response = decoder.decode(data.data);
console.info('Car response:', response);
}
}
// 使用示例
const smartCarKey = new SmartCarKey();
smartCarKey.start();
七、常见问题与解决方案
7.1 连续进行数据传输时数据发送失败
问题描述:连续多次调用 writeData 接口可能会导致发送队列拥塞,从而发送失败。
解决方案:通过设置数据发送间隔来解决连续传输数据时失败的问题。使用 setInterval 设置函数调用的时间间隔,建议的数据发送时间间隔为10ms。
class DataTransmissionManager {
private sendQueue: Array<dataTransfer.DataParams> = [];
private isSending: boolean = false;
// 添加数据到发送队列
addToQueue(dataParams: dataTransfer.DataParams): void {
this.sendQueue.push(dataParams);
if (!this.isSending) {
this.startSending();
}
}
// 开始发送
private startSending(): void {
this.isSending = true;
const sendInterval = setInterval(() => {
if (this.sendQueue.length === 0) {
clearInterval(sendInterval);
this.isSending = false;
return;
}
const dataParams = this.sendQueue.shift()!;
dataTransfer.writeData(dataParams).then(() => {
console.info('Data sent successfully');
}).catch((err: BusinessError) => {
console.error('Send failed:', err.message);
});
}, 10); // 10ms间隔
}
}
7.2 设备扫描不到
可能原因:
- 星闪开关未打开
- 设备不支持星闪功能
- 扫描过滤器配置不正确
- 外围设备未发送广播
解决方案:
async function troubleshootScan(): Promise<void> {
// 1. 检查星闪状态
let state = manager.getState();
if (state !== manager.NearlinkState.STATE_ON) {
console.error('NearLink is not enabled');
return;
}
// 2. 使用宽松的过滤器
let scanFilter: scan.ScanFilters = {
// 不设置过多过滤条件,先尝试扫描所有设备
};
// 3. 使用高功耗扫描模式
let scanOptions: scan.ScanOptions = {
scanMode: scan.ScanMode.SCAN_MODE_HIGH_POWER
};
await scan.startScan([scanFilter], scanOptions);
}
7.3 链路加密问题
场景:需要确保数据传输安全,防止数据被窃听。
解决方案:
async function ensureSecureConnection(device: remoteDevice.RemoteDevice): Promise<boolean> {
try {
// 1. 发起配对
await device.startPairing();
console.info('Pairing initiated');
// 2. 等待配对完成(用户确认)
await new Promise(resolve => setTimeout(resolve, 5000));
// 3. 检查链路加密状态
let acbState = await device.getAcbState();
if (acbState === remoteDevice.AcbState.ENCRYPTED) {
console.info('Link is encrypted');
return true;
} else {
console.warn('Link is not encrypted');
return false;
}
} catch (err) {
console.error('Ensure secure connection failed:', (err as BusinessError).message);
return false;
}
}
八、最佳实践
8.1 资源管理
class NearlinkResourceManager {
private connections: Map<string, boolean> = new Map();
// 添加连接
addConnection(address: string): void {
this.connections.set(address, true);
}
// 清理所有资源
cleanup(): void {
// 停止扫描
scan.stopScan().catch(err => {
console.error('Stop scan failed:', err);
});
// 停止广播
// advertising.stopAdvertising()...
// 清理连接记录
this.connections.clear();
console.info('Resources cleaned up');
}
// 在应用退出时调用
onAppExit(): void {
this.cleanup();
}
}
8.2 错误处理
enum NearlinkErrorCode {
PERMISSION_DENIED = 201,
STATE_OFF = 2900001,
DEVICE_NOT_FOUND = 2900002,
CONNECTION_FAILED = 2900003,
}
class ErrorHandler {
static handle(error: BusinessError): void {
switch (error.code) {
case NearlinkErrorCode.PERMISSION_DENIED:
console.error('权限被拒绝,请在设置中开启星闪权限');
break;
case NearlinkErrorCode.STATE_OFF:
console.error('星闪未开启,请在设置中开启星闪');
break;
case NearlinkErrorCode.DEVICE_NOT_FOUND:
console.error('设备未找到,请确认设备已开启星闪广播');
break;
case NearlinkErrorCode.CONNECTION_FAILED:
console.error('连接失败,请检查设备距离和信号强度');
break;
default:
console.error(`未知错误: ${error.code} - ${error.message}`);
}
}
}
8.3 日志管理
class NearlinkLogger {
private static readonly TAG = 'NearLink';
static info(message: string, ...args: any[]): void {
console.info(`[${this.TAG}] ${message}`, ...args);
}
static error(message: string, error?: any): void {
if (error) {
console.error(`[${this.TAG}] ${message}`, error);
} else {
console.error(`[${this.TAG}] ${message}`);
}
}
static debug(message: string, ...args: any[]): void {
console.debug(`[${this.TAG}] ${message}`, ...args);
}
}
九、参考资源
9.1 官方文档
9.2 开发工具
9.3 相关资源
总结
NearLink Kit为HarmonyOS应用提供了强大的短距离通信能力。通过本文的学习,您可以掌握:
- ✅ NearLink Kit的核心概念和术语
- ✅ 完整的开发准备流程(权限配置、环境准备)
- ✅ 核心API接口的使用方法
- ✅ 广播、扫描、数据传输的完整开发流程
- ✅ 实战案例:智能车钥匙的完整实现
- ✅ 常见问题的排查和解决方案
- ✅ 最佳实践:资源管理、错误处理、日志管理
关键要点:
- 使用
dataTransfer.on('readData', callback)接收数据(不是dataReceived) - 数据发送间隔建议设置为10ms,避免队列拥塞
- 敏感数据传输前需要通过
startPairing进行配对加密 - 扫描过滤器至少需要一个过滤条件,组之间是"或"关系,组内是"与"关系
- UUID格式遵循星闪标准:
37BEA880-FC70-11EA-B720-00000000XXXX
随着星闪生态的不断完善,NearLink Kit将为万物互联提供更多可能性。建议开发者持续关注官方文档更新,探索更多创新应用场景。