《HarmonyOS技术精讲-NearLink Kit(星闪服务)》第2篇:设备发现——星闪设备发现机制与API详解

📅 2026/7/17 1:19:51
《HarmonyOS技术精讲-NearLink Kit(星闪服务)》第2篇:设备发现——星闪设备发现机制与API详解
《HarmonyOS技术精讲-NearLink Kit星闪服务》第2篇设备发现——星闪设备发现机制与API详解问题HarmonyOS NEXT 开发里startDiscovery这个 API 经常被误用。很多人第一次接触NearLink Kit的设备发现能力时会发现官方示例能运行但放到实际项目里就遇到各种问题扫描结果重复、回调不触发、页面离开后扫描还在跑。这个功能本身不复杂但真正麻烦的是生命周期管理和扫描结果的去重处理。它解决什么问题NearLink Kit是 HarmonyOS 提供的星闪通信服务框架。设备发现是星闪应用的第一步——扫描周边的星闪设备并获取设备的基础信息比如设备名称、设备ID、信号强度等。特性传统蓝牙扫描星闪设备发现扫描模式被动扫描主动被动混合发现速度慢(约3-5秒)快(约1-2秒)设备信息量有限更丰富过滤能力有限支持多维度过滤有两个典型场景适合用这个 API智能家居设备配网前扫描周边设备会议系统发现附近的可投屏设备不适合的场景如果你只需要连接已知设备直接使用连接 API 即可没必要开启扫描。环境说明DevEco Studio 版本DevEco Studio 6.1.0 及以上 HarmonyOS SDK 版本HarmonyOS 6.1.0(23) 及以上 目标设备手机核心实现1. 准备工作权限配置在module.json5中添加权限声明。这一步容易漏掉很多人直接跑示例发现没反应原因就是权限没配。{module:{requestPermissions:[{name:ohos.permission.NEARLINK_DISCOVERY,reason:$string:permission_nearlink_discovery,usedScene:{abilities:[EntryAbility]}},{name:ohos.permission.INTERNET}]}}注意NEARLINK_DISCOVERY权限在 API 23 及以上版本才支持。如果跑在低版本设备上这个权限不会生效也不会报错扫描就直接不回调了。2. 发起设备发现扫描这一段代码用于封装星闪发现的核心逻辑。实际项目里建议写成一个单独的服务类不要直接把扫描逻辑塞进 UI 组件。// NearLinkDiscoverService.etsimport{nearLink}fromkit.ConnectivityKit;import{BusinessError}fromkit.BasicServicesKit;classNearLinkDevice{name:string;deviceId:string;rssi:number-100;manufacturerData:Uint8ArraynewUint8Array();}interfaceDiscoverResult{devices:NearLinkDevice[];}exportclassNearLinkDiscoverService{privatediscoveryId:number-1;privatediscoveredDevices:Mapstring,NearLinkDevicenewMap();privateonDeviceFound:((device:NearLinkDevice)void)|nullnull;privateonError:((error:string)void)|nullnull;// 注册发现监听registerListener(onFound:(device:NearLinkDevice)void,onError:(error:string)void):void{this.onDeviceFoundonFound;this.onErroronError;}// 启动发现startDiscovery():void{// 检查是否已经在扫描中if(this.discoveryId!-1){console.warn([NearLink] Discovery already running, skip);return;}// 构建发现回调constcallback:nearLink.IDiscoveryCallback{onError:(errCode:number,errMsg:string):void{console.error([NearLink] Discovery error:${errCode}-${errMsg});this.onError?.(Discovery error:${errMsg});},onFound:(device:nearLink.DiscoveredDevice):void{// 防重复逻辑用 deviceId 去重if(this.discoveredDevices.has(device.deviceId)){// 已存在的设备更新信号强度constexistingthis.discoveredDevices.get(device.deviceId)!;existing.rssidevice.rssi;return;}// 新设备加入列表并回调constnewDevice:NearLinkDevice{name:device.deviceName,deviceId:device.deviceId,rssi:device.rssi,manufacturerData:device.manufacturerData};this.discoveredDevices.set(device.deviceId,newDevice);this.onDeviceFound?.(newDevice);}};// 配置发现过滤条件可选constfilter:nearLink.DiscoveryFilter{type:nearLink.DiscoveryFilterType.DISCOVERY_FILTER_TYPE_MANUFACTURER,value:newUint8Array([0x01,0x02,0x03])// 示例制造商数据前缀};// 配置发现参数constconfig:nearLink.DiscoveryConfig{discoveryId:0,callback:callback,filter:filter// 不传 filter 则发现所有星闪设备};try{this.discoveryIdnearLink.startDiscovery(config);console.info([NearLink] Discovery started, id:${this.discoveryId});}catch(err){consterrorerrasBusinessError;console.error([NearLink] startDiscovery failed:${error.code}-${error.message});this.onError?.(startDiscovery failed:${error.message});}}// 停止发现stopDiscovery():void{if(this.discoveryId-1){console.warn([NearLink] No active discovery to stop);return;}try{nearLink.stopDiscovery(this.discoveryId);console.info([NearLink] Discovery stopped);this.discoveryId-1;// 清空缓存根据业务需要决定是否清空// this.discoveredDevices.clear();}catch(err){consterrorerrasBusinessError;console.error([NearLink] stopDiscovery failed:${error.code});}}// 清除设备缓存clearCache():void{this.discoveredDevices.clear();}// 获取已发现的设备列表getDiscoveredDevices():NearLinkDevice[]{returnArray.from(this.discoveredDevices.values());}}关键细节防重复逻辑onFound回调会反复上报同一个设备信号强度变化时也会再次回调。使用deviceId作为 Map 的 key 去重只更新已存在设备的 RSSI不重复添加。discoveryId 管理startDiscovery返回一个 id停止时要传入这个 id。如果页面销毁时没有保存这个 id后续无法正确停止扫描。filter 配置不传 filter 会扫描周边所有星闪设备。如果只关心某个制造商的产品用DISCOVERY_FILTER_TYPE_MANUFACTURER过滤效率更高。3. 在页面中使用这段代码展示如何在 UI 组件中调用发现服务并处理生命周期。// IndexPage.etsimport{NearLinkDiscoverService,NearLinkDevice}from./NearLinkDiscoverService;EntryComponentstruct IndexPage{Statedevices:NearLinkDevice[][];StateisScanning:booleanfalse;privatediscoverService:NearLinkDiscoverServicenewNearLinkDiscoverService();aboutToAppear():void{// 注册监听this.discoverService.registerListener((device:NearLinkDevice){// 回调中更新 UIState 自动触发刷新this.devicesthis.discoverService.getDiscoveredDevices();},(error:string){console.error([Page]${error});});}aboutToDisappear():void{// 页面离开时必须停止扫描否则后台持续耗电this.stopScan();}startScan():void{this.isScanningtrue;this.devices[];this.discoverService.clearCache();this.discoverService.startDiscovery();}stopScan():void{this.isScanningfalse;this.discoverService.stopDiscovery();}build(){Column(){Button(this.isScanning?停止扫描:开始扫描).onClick((){this.isScanning?this.stopScan():this.startScan();}).margin(16)List(){ForEach(this.devices,(device:NearLinkDevice){ListItem(){Row(){Text(device.name||未知设备).fontSize(16)Text(RSSI:${device.rssi}dBm).fontSize(12).fontColor(#666)}.padding(12).width(100%)}})}.layoutWeight(1)}.width(100%).height(100%)}}注意aboutToDisappear中的stopScan()调用。实际项目里很多人漏掉这一步导致页面返回后扫描还在持续不仅耗电还可能造成回调在已销毁页面中执行引发异常。4. 过滤条件完整示例如果不希望扫描所有设备可以用制造商数据过滤。这段代码展示如何只发现特定厂商的设备。// 只发现制造商ID为 0x0001 的设备示例constfilter:nearLink.DiscoveryFilter{type:nearLink.DiscoveryFilterType.DISCOVERY_FILTER_TYPE_MANUFACTURER,value:newUint8Array([0x00,0x01])};// 或者使用设备服务类型过滤constfilterByService:nearLink.DiscoveryFilter{type:nearLink.DiscoveryFilterType.DISCOVERY_FILTER_TYPE_SERVICE,value:newUint8Array([0x10,0x20])// 示例服务 UUID 前缀};常见问题问题 1页面返回后扫描未停止现象从扫描页面返回过一会儿发现手机还在发热日志显示扫描回调仍在触发。原因aboutToDisappear中没有调用stopDiscovery。HarmonyOS 不会在页面销毁时自动停止已启动的扫描需要开发者手动处理。解决方案严格在aboutToDisappear中停止扫描。如果服务类被多个页面共享需要在服务类里判断是否有页面还在使用扫描不要直接 stop。问题 2onFound 回调频繁触发同一设备现象一个设备在 3 秒内被回调 10 次页面列表疯狂刷新。原因星闪设备会主动广播每次收到广播都会触发回调。这不是 bug而是星闪协议本身的特性。解决方案使用deviceId做去重如上面的代码所示。注意只更新 RSSI 等动态数据不重复添加列表项。最佳实践不要把扫描逻辑直接写在 UI 组件里。扫描涉及状态管理、生命周期、回调处理拆成独立服务类后测试和复用都更方便。stopDiscovery 必须在页面销毁时调用。这不是可选的。如果忘记停止扫描会持续消耗电量而且回调中操作 UI 时可能因为页面已销毁而崩溃。回调中更新 UI 时注意线程。onFound运行在非 UI 线程不要直接操作 UI 组件。ArkTS 的State变量是线程安全的赋值即可触发刷新。但如果有复杂的 UI 操作建议使用setTimeout或postTask切回主线程。FAQQ为什么真机正常模拟器不生效A星闪能力依赖硬件支持模拟器不具备星闪硬件所以startDiscovery调用后不会触发任何回调。必须在 real device 上测试。Q为什么页面返回后状态丢失A检查aboutToDisappear是否正确调用了stopDiscovery。另外如果你在页面间切换时使用了NavStack注意onPageShow和onPageHide的生命周期建议在onPageHide中暂停扫描onPageShow中恢复。Q过滤条件设置后扫描不到设备怎么办A先去掉 filter测试是否能看到设备。如果能看见说明 filter 配置错误。确认你的 filter value 和设备广播的制造商数据完全匹配包括字节序和长度。如果你也遇到类似问题可以重点检查生命周期和状态同步逻辑。不同设备上的星闪广播行为可能存在差异建议真机测试。