《HarmonyOS技术精讲-NearLink Kit(星闪服务)》第1篇:初识星闪——NearLink Kit概述与Hello World

📅 2026/7/17 1:19:41
《HarmonyOS技术精讲-NearLink Kit(星闪服务)》第1篇:初识星闪——NearLink Kit概述与Hello World
初识星闪NearLink Kit不会凭空工作很多人第一次打开华为 NearLink 的官方文档会觉得这个功能挺简单引入kit.ConnectKit下的nearlink模块调用discover方法就能发现周围设备了。但实际写代码的时候你会发现几个诡异的问题模拟器上什么也扫不到。真机上好不容易扫到了页面一退回调就不走了。明明手机都贴在一起了就是发现不了。HarmonyOS NearLink 服务不是蓝牙的一个变种也不是 Wi-Fi Direct 的替换。它的底层机制、权限模型和生命周期管理和传统短距通信有很大区别。这篇文章不讲官方文档已经有的那些“左对齐”的文字直接从代码开始带你跑通第一个能工作的星闪示例。NearLink 解决什么问题传统短距通信方案在 IoT 场景下有几个硬伤Wi-Fi 直连配对流程长功耗高设备数多了容易冲突。经典蓝牙带宽有限不适合传图像或音频流。BLE功耗低但连接不稳定有效传输距离短。NearLink星闪结合了蓝牙的易用性和 Wi-Fi 的高吞吐同时通过新的空口设计解决了多设备并发和低时延问题。特性传统 BLENearLink设备发现速度秒级百毫秒级单连接传输1~2 Mbps可达 20 Mbps并发设备数有限基站模式可达数十个时延10ms微秒级并不是所有场景都需要上 NearLink。如果你只是做一个简单的手机-手环通知同步BLE 完全够用。但如果你遇到多设备同步、高带宽传输、低时延控制这类需求NearLink 是更合适的选择。环境说明DevEco Studio 版本DevEco Studio 6.1.0 及以上 HarmonyOS SDK 版本HarmonyOS 6.1.0(23) 及以上 目标设备至少两台真机手机、平板均可需支持星闪功能注意模拟器不支持星闪硬件能力测试必须用真机。核心实现第一个能工作的星闪 Demo我们先做一个最简单的功能启动设备发现并把扫描到的设备信息打印到日志里。1. 权限声明这一步很容易被跳过但必须做在entry/src/main/module.json5中添加以下权限requestPermissions: [ { name: ohos.permission.ACCESS_NEARLINK }, { name: ohos.permission.ACCESS_FINE_LOCATION }, { name: ohos.permission.ACCESS_BLUETOOTH } ]其中ACCESS_NEARLINK是核心权限ACCESS_FINE_LOCATION用于扫描周围的设备这是系统级限制不仅仅是为了定位ACCESS_BLUETOOTH是为了兼容部分低版本设备。这里需要注意即使你的应用只在后台扫描也必须主动申请定位权限。否则nearlink.discover的回调不会接收到任何设备信息且不会抛出异常——这就是让人困惑的地方。2. 编写发现模块创建src/main/ets/model/NearLinkDiscover.ts代码如下// NearLinkDiscover.ts// 负责启动和停止星闪设备发现并通过回调返回结果import{nearbus}fromkit.QosKit;// 注意实际引入模块名可能与文档略有差异interfaceDiscoverResult{deviceName:string;deviceId:string;rssi:number;}exportclassNearLinkDiscover{privateisRunning:booleanfalse;privateonDeviceFound:(device:DiscoverResult)void;privateonError:(err:string)void;constructor(onDeviceFound:(device:DiscoverResult)void,onError:(err:string)void){this.onDeviceFoundonDeviceFound;this.onErroronError;}startDiscovery():void{if(this.isRunning){console.warn([NearLinkDiscover] already running);return;}this.isRunningtrue;try{// 开始发现nearbus.discover({onDeviceFound:(device){constresult:DiscoverResult{deviceName:device.deviceName||Unknown,deviceId:device.deviceId,rssi:device.rssi??-100,};console.info([NearLinkDiscover] found device:${result.deviceName}, id:${result.deviceId});this.onDeviceFound(result);},onError:(error){consterrMsgdiscover error:${error.code}, message:${error.message};console.error([NearLinkDiscover]${errMsg});this.onError(errMsg);this.isRunningfalse;}});}catch(err){consterrMsgcatch exception:${err.code},${err.message};console.error([NearLinkDiscover]${errMsg});this.onError(errMsg);this.isRunningfalse;}}stopDiscovery():void{if(!this.isRunning){return;}try{nearbus.stopDiscover();console.info([NearLinkDiscover] stopped);}catch(err){console.error([NearLinkDiscover] stop error:${err.code},${err.message});}finally{this.isRunningfalse;}}isDiscovering():boolean{returnthis.isRunning;}}这段代码做了几件事封装了nearbus.discover的调用把原始回调包装成统一的DiscoverResult结构。使用isRunning状态避免重复启动。在onError回调中自动将状态重置为false防止错误状态下状态不同步。为什么不用直接在页面里写nearbus.discover实际项目里设备发现通常不是一次性操作而是和页面生命周期强相关。如果直接写在AboutToAppear里离开页面时忘了调用stopDiscover会导致后台持续扫描增加功耗且可能引起系统层面的回调异常。3. 在页面中使用创建src/main/ets/pages/Index.etsimport{NearLinkDiscover}from../model/NearLinkDiscover;EntryComponentstruct Index{StatediscoverState:string未开始;StatedeviceList:string[][];privatenearLinkDiscover:NearLinkDiscovernewNearLinkDiscover((device){this.deviceList[...this.deviceList,${device.deviceName}(信号:${device.rssi})];this.discoverState发现中;},(err){this.discoverState错误:${err};});build(){Column(){Text(状态:${this.discoverState}).fontSize(24).margin(20);Text(已发现设备:${this.deviceList.length}).fontSize(18);List({space:10}){ForEach(this.deviceList,(item:string){ListItem(){Text(item).fontSize(16).padding(10);}},(item:string)item);}.layoutWeight(1).width(100%);Button(开始发现).onClick((){if(!this.nearLinkDiscover.isDiscovering()){this.nearLinkDiscover.startDiscovery();this.discoverState扫描中...;}});}.width(100%).height(100%);}aboutToDisappear():void{// 页面销毁时停止发现防止内存泄漏和后台扫描this.nearLinkDiscover.stopDiscovery();}}关键点不要在build()中创建 new NearLinkDiscover()因为 build 会频繁触发每触发一次就会注册一个新的回调对象导致回调混乱。这里把它声明在private字段里只实例化一次。aboutToDisappear()里必须停掉发现否则退出页面后回调仍然会触发而此时State引用的this已经可能无效引起错误。常见问题问题 1权限都已声明但真机扫码无反应现象所有权限在module.json5里都加了日志里不打印任何发现结果也没有异常。原因ACCESS_FINE_LOCATION属于敏感权限module.json5里声明后还需要在运行时动态请求对于 API 23 的目标设备HarmonyOS NEXT 也遵循此规则。如果用户没有在首次弹出时点“始终允许”后续扫描不会返回设备。解法在页面aboutToAppear里使用abilityAccessCtrl请求定位权限。import{abilityAccessCtrl,common}fromkit.AbilityKit;import{bundleManager}fromkit.AbilityKit;requestLocationPermission():void{constcontext:common.UIAbilityContextgetContext(this)ascommon.UIAbilityContext;constatManagerabilityAccessCtrl.createAtManager();try{atManager.requestPermissionsFromUser(context,[ohos.permission.ACCESS_FINE_LOCATION]).then((data){if(data.authResults[0]!abilityAccessCtrl.GrantStatus.PERMISSION_GRANTED){console.warn([Permission] location not granted);}});}catch(err){console.error([Permission] request failed:${err.code});}}问题 2页面返回后再进入发现功能不工作现象第一次进入页面能正常发现设备返回后再打开页面设备列表为空且日志里没有found device。原因nearbus.discover在同一个Ability实例中不允许重复调用。第一次退出时如果stopDiscover没有被正确调用例如aboutToDisappear中忘记处理系统会认为发现会话仍然存在第二次调用discover会静默失败。解法务必在aboutToDisappear、页面显式退出如点击返回键以及错误回调中停掉发现。并且可以考虑在aboutToAppear中先调用一次stopDiscover以保证状态干净。aboutToAppear():void{this.nearLinkDiscover.stopDiscovery();// 确保状态重置}最佳实践不要在构建函数中创建长生命周期对象。State变量的变更会触发build()重新执行如果每次重建都 new 一个NearLinkDiscover会导致回调函数中的this指向错误实例设备列表永远不会更新。设备列表使用不可变更新模式。在回调里写this.deviceList [...this.deviceList, newDevice]而不是直接push因为State只有在整体赋值时才触发重新渲染。直接修改数组内容不会更新 UI。在onError中一定要重置状态。如果发现过程中出现错误例如网络断开、系统资源不足回调会收到错误。如果不重置isRunning false用户就无法再次启动发现体验很差。FAQQ为什么真机上能发现设备但连接不上A本篇文章只覆盖了设备发现部分。连接操作需要额外的nearbus.connectAPI且需要双方设备上都运行了支持 NearLink 的 service。后面会单独写一篇连接与数据通信的实战。Q模拟器上跑 code但日志里没有任何输出A模拟器没有星闪硬件nearbus.discover会返回错误错误码通常是-1或0取决于版本。必须用真机且设备需支持星闪目前仅少数旗舰机型。Q为什么我一打开这个页面手机就发热Anearbus.discover默认会开启高频扫描功耗较高。如果你不需要持续扫描可以在发现到足够多的设备后手动调用stopDiscovery或者使用scanInterval参数降低频率该参数在部分 API 版本中支持。如果你也遇到类似问题可以重点检查定位权限是否已明确授予以及stopDiscovery是否在正确时机调用。官方文档对中断场景描述得比较少建议结合实际运行效果一起验证。