鸿蒙星闪服务(NearLink Kit)

137 阅读4分钟

鸿蒙星闪服务(NearLink Kit)介绍

本文基于华为官方文档整理,详细介绍HarmonyOS NearLink Kit的核心概念、API接口和开发实践。

一、NearLink Kit简介

1.1 什么是NearLink Kit

NearLink Kit(星闪服务)提供一种低功耗、高速率的短距离通信服务,支持星闪设备之间的连接、数据交互。

  • 中心设备:可以通过扫描发现外围设备,并发起连接
  • 外围设备:可以通过发送广播的方式被中心设备发现,连接后进行数据传输

1.2 典型应用场景

  1. 智能外设连接

    • 中心设备和外围设备鼠标通过星闪配对连接后,使用鼠标作为输入控制中心设备
    • 中心设备和外围设备手写笔通过星闪配对连接后,使用手写笔作为输入操作中心设备
  2. 智能座舱

    • 车内设备间的低延迟通信
    • 车钥匙无感解锁
  3. 工业物联网

    • 传感器数据实时采集
    • 多设备并发连接

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 环境要求

  1. DevEco Studio:最新版本
  2. SDK:支持NearLink Kit的SDK版本
  3. 真机设备:确认设备支持星闪功能

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技术进行设备间的数据传输。

重要提示

  1. 数据传输通道不保证链路加密。如需加密数传,需先进行配对流程,通过 startPairing 接口发起
  2. 链路是否加密可通过 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 设备扫描不到

可能原因

  1. 星闪开关未打开
  2. 设备不支持星闪功能
  3. 扫描过滤器配置不正确
  4. 外围设备未发送广播

解决方案

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应用提供了强大的短距离通信能力。通过本文的学习,您可以掌握:

  1. ✅ NearLink Kit的核心概念和术语
  2. ✅ 完整的开发准备流程(权限配置、环境准备)
  3. ✅ 核心API接口的使用方法
  4. ✅ 广播、扫描、数据传输的完整开发流程
  5. ✅ 实战案例:智能车钥匙的完整实现
  6. ✅ 常见问题的排查和解决方案
  7. ✅ 最佳实践:资源管理、错误处理、日志管理

关键要点

  • 使用 dataTransfer.on('readData', callback) 接收数据(不是 dataReceived
  • 数据发送间隔建议设置为10ms,避免队列拥塞
  • 敏感数据传输前需要通过 startPairing 进行配对加密
  • 扫描过滤器至少需要一个过滤条件,组之间是"或"关系,组内是"与"关系
  • UUID格式遵循星闪标准:37BEA880-FC70-11EA-B720-00000000XXXX

随着星闪生态的不断完善,NearLink Kit将为万物互联提供更多可能性。建议开发者持续关注官方文档更新,探索更多创新应用场景。