
很多人第一次打开华为 NearLink 的官方文档,会觉得这个功能挺简单:引入 @kit.ConnectKit 下的 nearlink 模块,调用 discover 方法,就能发现周围设备了。但实际写代码的时候,你会发现几个诡异的问题:
HarmonyOS NearLink 服务不是蓝牙的一个变种,也不是 Wi-Fi Direct 的替换。它的底层机制、权限模型和生命周期管理,和传统短距通信有很大区别。这篇文章不讲官方文档已经有的那些“左对齐”的文字,直接从代码开始,带你跑通第一个能工作的星闪示例。
传统短距通信方案在 IoT 场景下有几个硬伤:
NearLink(星闪)结合了蓝牙的易用性和 Wi-Fi 的高吞吐,同时通过新的空口设计解决了多设备并发和低时延问题。
| 特性 | 传统 BLE | NearLink |
|---|---|---|
| 设备发现速度 | 秒级 | 百毫秒级 |
| 单连接传输 | 1~2 Mbps | 可达 20 Mbps |
| 并发设备数 | 有限(基站模式) | 可达数十个 |
| 时延 | 10ms+ | 微秒级 |
并不是所有场景都需要上 NearLink。如果你只是做一个简单的手机-手环通知同步,BLE 完全够用。但如果你遇到 多设备同步、高带宽传输、低时延控制 这类需求,NearLink 是更合适的选择。
注意:模拟器不支持星闪硬件能力,测试必须用真机。
我们先做一个最简单的功能:启动设备发现,并把扫描到的设备信息打印到日志里。
在 entry/src/main/module.json5 中添加以下权限:
其中 ACCESS_NEARLINK 是核心权限,ACCESS_FINE_LOCATION 用于扫描周围的设备(这是系统级限制,不仅仅是为了定位),ACCESS_BLUETOOTH 是为了兼容部分低版本设备。
这里需要注意:即使你的应用只在后台扫描,也必须主动申请定位权限。否则 nearlink.discover 的回调不会接收到任何设备信息,且不会抛出异常——这就是让人困惑的地方。
创建 src/main/ets/model/NearLinkDiscover.ts,代码如下:
这段代码做了几件事:
nearbus.discover 的调用,把原始回调包装成统一的 DiscoverResult 结构。isRunning 状态避免重复启动。onError 回调中自动将状态重置为 false,防止错误状态下状态不同步。为什么不用直接在页面里写 nearbus.discover?
实际项目里,设备发现通常不是一次性操作,而是和页面生命周期强相关。如果直接写在 AboutToAppear 里,离开页面时忘了调用 stopDiscover,会导致后台持续扫描,增加功耗且可能引起系统层面的回调异常。
创建 src/main/ets/pages/Index.ets:
关键点:
build() 中创建 new NearLinkDiscover(),因为 build 会频繁触发,每触发一次就会注册一个新的回调对象,导致回调混乱。这里把它声明在 private 字段里,只实例化一次。aboutToDisappear() 里必须停掉发现,否则退出页面后回调仍然会触发,而此时 @State 引用的 this 已经可能无效,引起错误。现象:所有权限在 module.json5 里都加了,日志里不打印任何发现结果,也没有异常。
原因:ACCESS_FINE_LOCATION 属于敏感权限,module.json5 里声明后,还需要在运行时动态请求(对于 API 23+ 的目标设备,HarmonyOS NEXT 也遵循此规则)。如果用户没有在首次弹出时点“始终允许”,后续扫描不会返回设备。
解法:在页面 aboutToAppear 里,使用 abilityAccessCtrl 请求定位权限。
现象:第一次进入页面能正常发现设备,返回后再打开页面,设备列表为空,且日志里没有 found device。
原因:nearbus.discover 在同一个 Ability 实例中不允许重复调用。第一次退出时如果 stopDiscover 没有被正确调用(例如 aboutToDisappear 中忘记处理),系统会认为发现会话仍然存在,第二次调用 discover 会静默失败。
解法:务必在 aboutToDisappear、页面显式退出(如点击返回键)以及错误回调中停掉发现。并且可以考虑在 aboutToAppear 中先调用一次 stopDiscover 以保证状态干净。
不要在构建函数中创建长生命周期对象。@State 变量的变更会触发 build() 重新执行,如果每次重建都 new 一个 NearLinkDiscover,会导致回调函数中的 this 指向错误实例,设备列表永远不会更新。
设备列表使用不可变更新模式。在回调里写 this.deviceList = [...this.deviceList, newDevice],而不是直接 push,因为 @State 只有在整体赋值时才触发重新渲染。直接修改数组内容不会更新 UI。
在 onError 中一定要重置状态。如果发现过程中出现错误(例如网络断开、系统资源不足),回调会收到错误。如果不重置 isRunning = false,用户就无法再次启动发现,体验很差。
Q:为什么真机上能发现设备,但连接不上?
A:本篇文章只覆盖了设备发现部分。连接操作需要额外的 nearbus.connect API,且需要双方设备上都运行了支持 NearLink 的 service。后面会单独写一篇连接与数据通信的实战。
Q:模拟器上跑 code,但日志里没有任何输出?
A:模拟器没有星闪硬件,nearbus.discover 会返回错误(错误码通常是 -1 或 0,取决于版本)。必须用真机,且设备需支持星闪(目前仅少数旗舰机型)。
Q:为什么我一打开这个页面,手机就发热?
A:nearbus.discover 默认会开启高频扫描,功耗较高。如果你不需要持续扫描,可以在发现到足够多的设备后手动调用 stopDiscovery,或者使用 scanInterval 参数降低频率(该参数在部分 API 版本中支持)。
如果你也遇到类似问题,可以重点检查定位权限是否已明确授予,以及 stopDiscovery 是否在正确时机调用。官方文档对中断场景描述得比较少,建议结合实际运行效果一起验证。
原文作者:不羁的木木;如有侵权,请联系管理员删除。