Kotlin Multiplatform 三方库 connectivity 的 OpenHarmony 鸿蒙化适配指南
Kotlin Multiplatform 三方库 connectivity 的 OpenHarmony 鸿蒙化适配指南
库版本:jordond/connectivity 2.2.1 鸿蒙切片
2.2.1-ohos.1|验证环境:HarmonyOS Kotlin 2.2.21-1.0.0|Gradle 8.14.1|JDK 21|DevEco Studio 26.0.0|HarmonyOS 7.0.0(API 26)|模拟器127.0.0.1:5555(x86_64)
前两篇 KMP 适配分别验证了两件事。qrcode-kotlin 证明 ohos target 能跑纯计算:actual 不链系统 so,自己光栅、自己编 PNG。multiplatform-settings 证明同一套 Gradle / NAPI 骨架能接到设备上的 libohpreferences.so。这篇补第三块:系统网络状态。
我以为把 OH_NetConn_HasDefaultNet 五个函数名抄进 Kotlin,ArkTS 点一下就能出 Connected。然后发生了四件事。
第一,SDK 的 net_connection.h 喂给 cinterop,availability 宏把 clang 前端打爆。第二,HasDefaultNet 的出参是 int32_t*,第一版按 Preferences 那篇的 BooleanVar 去读,编得过,值是错的。第三,llvm-nm -D 里明明有 ConnectivityCall,ArkTS 直接 import Kotlin/Native so,业务函数根本不在导出表里。第四,native 和 ArkTS 都编过了,PackageHap 却申请 16GB 堆;权限漏写 GET_NETWORK_INFO,五个 C 调用一律返回 201,页面会装成「没网」。
网络这种库,编过不算数。rc 必须露在 JSON 里。Start / Stop 必须让 isMonitoring 翻转。有 default net 时 kind 必须是 Connected。本文按这条真实路径写。

四张是同一台 x86_64 模拟器、同一条以太网。大字一直是 Connected,变的是 JSON:netId=101、monitoring 从 true 翻到 false。只看大字会以为 Start / Stop 没生效。

一、环境搭建
本章不展开,直接引用官方入口:KMP&CMP 鸿蒙社区、HarmonyOS 应用开发导读。
本文实际使用:
| 项 | 值 |
|---|---|
| 语言 / 框架 | Kotlin Multiplatform,HarmonyOS Kotlin 2.2.21-1.0.0 |
| 插件仓库 | https://maven.eazytec-cloud.com/nexus/repository/maven-public/ |
| JDK | Temurin 21(写进 gradle.properties 的 org.gradle.java.home) |
| Gradle Wrapper | 8.14.1 |
| DevEco Studio | 26.0.0 |
| HarmonyOS SDK | 7.0.0(API 26) |
| 真机 ABI | ohosArm64 → arm64-v8a |
| 模拟器 ABI | ohosX64 → x86_64 |
| HAP 打包 JDK | DevEco 自带 JBR,不要用 JDK 8 |
| 系统库 | libnet_connection.so(设备 sysroot,不打进 HAP) |
| 权限 | ohos.permission.GET_NETWORK_INFO |
ohosArm64() / ohosX64() 只存在于这套定制 Kotlin Gradle Plugin。用 Maven Central 上的官方 2.2.21 写这两行,配置期就会 Unresolved reference。这是判断工具链有没有接对的第一根探针。
二、应用背景
跨端业务里,「现在有没有网」出现的频率不比键值存储低。启动页要不要打登录、同步按钮要不要转圈、弱网要不要换小图,全都先问一句网络状态。Android 走 ConnectivityManager,Apple 走 NWPathMonitor。Jordon de Hoog 的 connectivity 把差异收口成一个很小的接口:
val net = Connectivity()
net.start()
when (val status = net.status()) {
is Connectivity.Status.Connected -> println("metered=${status.metered}")
Connectivity.Status.Disconnected -> println("offline")
}
net.stop()
commonMain 里的业务代码不需要知道底下是 ConnectivityManager、NWPathMonitor,还是鸿蒙的 OH_NetConn_*。鸿蒙缺的只是一个 actual。
三条常见路线我都否掉了。
在 ArkTS 里直接 @ohos.net.connection,那是使用文,不是适配。KMP 业务跑在 Kotlin/Native 线程,没有 JS runtime,也不能把 Ability 的 Context 传进 commonMain。
ohos actual 走 NAPI 回调 ArkTS 网络模块,等于每次快照跨一次语言,生命周期更乱。KN sharedLib 里也没有 Ability Context。
把 JVM 的 NetworkInterface 原样当鸿蒙实现,那是 L1 冒烟,不是设备上的默认网。JvmConnectivity 留着只给 jvmTest 对照,metered 在 JVM 上恒为 false。
对位的公开能力是 C API:network/netmanager/net_connection.h,动态库 libnet_connection.so。HasDefaultNet / GetDefaultNet / IsDefaultNetMetered / GetNetCapabilities / GetAllNets 从 API 11 开始。目标设备 API 26,够用。
选题时用 git ls-remote 扫过 CPF-KMP-CMP 和 oh-tpc:已有 Ksoup、kotlin-result、kotlin-multiplatform-diff、Kermit、okio、kotlinx-datetime、SQLiter,以及本系列的 qrcode-kotlin、multiplatform-settings,没有 jordond/connectivity。这是增量题,不是重复领激励。方向锁定 KMP 三方库适配,不是 CMP,不是 Flutter connectivity_plus,也不是「拿 ArkTS connection 写业务」的使用文。
上游完整 API 绑死 kotlinx.coroutines.flow.SharedFlow。本轮 HarmonyOS Kotlin 树没有官方 kotlinx-coroutines ohos 制品。与其交一个「看起来在 collect、回调永远不来」的半残 Flow,不如把 2.2.1-ohos.1 写成无协程切片:Status 模型保留,status() 改成同步快照,Flow 明确划出范围。
三、接口分析

按 Demo 实际打到的表面列,不把上游 statusUpdates、Compose 扩展件算进「已适配」。
| 能力 | 上游 API | 鸿蒙 actual | Demo 按钮 | 验收 |
|---|---|---|---|---|
| 当前状态 | suspend status() | 同步 status(),内部 HasDefaultNet | Status | kind=Connected 或 Disconnected |
| 计费 | Connected.metered | IsDefaultNetMetered | Metered | JSON metered 与 rc 同时给出 |
| 开始监听 | start() | 进程内 isMonitoring=true | Start | JSON monitoring=true |
| 停止监听 | stop() | isMonitoring=false | Stop | JSON monitoring=false |
| 派生属性 | isConnected / isDisconnected / isMetered | 原样 | Status | 断开时 isMetered 必须 false |
| 默认网 id | (上游无,诊断) | GetDefaultNet | Snapshot | netId ≥ 0 或失败为 -1 |
| 承载 / 能力 | (上游无,诊断) | GetNetCapabilities | Capabilities | bearers 含 WIFI / CELLULAR / ETHERNET 等 |
| 全部网络 | (上游无,诊断) | GetAllNets | AllNets | allNetIds 非空或 rc 非 0 |
ArkTS 不直接碰这些 Kotlin API。它只调用一个 NAPI 函数:
import { ConnectivityCall } from 'libconnectivity_napi.so';
const raw: string = ConnectivityCall(JSON.stringify({ op: 'snapshot' })) as string;
op 取 snapshot / status / hasDefault / metered / capabilities / allNets / start / stop。返回 JSON:ok、kind、isConnected、isMetered、monitoring、netId、bearers、capabilities、五个 *Rc。UI 进程不懂 NetConn_NetHandle,Kotlin/Native 进程不懂 ArkUI。这是有意设计的。
四、六阶段路线图
- 工具链。 JDK 21 + HarmonyOS Kotlin 2.2.21-1.0.0 + Gradle 8.14.1。JDK 8/11 会在配置期直接死。
- 目标矩阵。 根项目收成
jvm()+ohosArm64()+ohosX64()。保留 JVM 是为了jvmTest当对照;砍掉 iOS / Android / JS,是因为这次要在 Windows 上闭环。 - cinterop。 手写
netconn_min.h,不要把 SDK 的net_connection.h整棵树丢进去。 - actual。
OhosConnectivity实现Connectivity。status()先采NetSnapshot,再投影成Connected/Disconnected。 - C ABI + NAPI。
example/nativeApp用sharedLib { baseName = "ohosconnectivity" }产出 so,@CName("ConnectivityCall")导出。CAdapter 不挂业务符号,必须再编libconnectivity_napi.so。 - 落盘。 HAP 打进两个 ABI 的 so,模拟器上八个按钮都有 JSON,有 default net 时
kind=Connected,Start/Stop 翻转 monitoring。

六步不能并成「把 ohos target 打开」。第 3 步头文件写错,第 4 步的 IntVar 读出来就是垃圾;第 5 步漏了 NAPI,第 6 步的按钮全是 undefined is not a function。每一步都有独立验收口,后文分层验收按这个顺序对。
applyDefaultHierarchyTemplate() 会在两个 ohos 目标之上生成 ohosMain。代码放这里,不要放 nativeMain:以后一旦加 linux,会把鸿蒙 Network Kit 强加给不该用的平台。
上游原工程还有 compose artifact 和 coroutines 实现。原样打开,本轮工具链会在用不到的目标上失败。本仓 settings.gradle.kts 只留 :connectivity 和 :example:nativeApp。版本号写成 2.2.1-ohos.1,不要冒充上游正式版。
五、三个关键决策
5.1 切片,而不是假装完整 fork
完整 fork 要把 statusUpdates: SharedFlow<Status> 和 Compose 一起搬过来。没有官方 coroutines ohos 制品,把 kotlinx-coroutines-core 写进 ohosMain,解析阶段就会缺 ohosArm64 / ohosX64 构件。Flow 只能假活。
所以公共接口收成:
public interface Connectivity {
public val isMonitoring: Boolean
public fun status(): Status
public fun start()
public fun stop()
public sealed interface Status {
public val isConnected: Boolean
public val isMetered: Boolean
public val isDisconnected: Boolean
public data class Connected(public val metered: Boolean) : Status
public data object Disconnected : Status
}
}
Status 密封类保持原样,包括三个派生属性。start() / stop() 名字也保持原样。缺的是 Flow 收集器和 suspend。谁在 commonMain 里 collect,编译期失败,好过运行期挂起。
这不是偷工。协程一旦有官方 ohos 构件,可以把 OhosConnectivity 再包一层 callbackFlow,把 OH_NetConn_RegisterDefaultNetConnCallback 接进去,不必改 common 模型。现在硬接,只会让 Demo 永远编不过。
5.2 精简头,出参用 IntVar
第一反应是 headers = net_connection.h。编不过,原因和 Preferences 那篇一样具体。
头文件用了 __availability__,还嵌套 net_connection_type.h。cinterop 自带的 clang 前端认不全。我们这轮真正要用的只有五个查询函数,回调和 DNS / 代理先不做。
所以 src/ohosMain/cinterop/ 下放了两份自己写的文件:netconn_min.h 按官方字段顺序手抄 NetConn_NetHandle、NetConn_NetCapabilities、NetConn_NetHandleList;netconn.def 的 headers 只指向这一份,并写 linkerOpts = -lnet_connection。
结构体字段顺序必须和官方头一致。NetConn_NetCapabilities 先是上下行带宽,再是 netCaps[32] + netCapsSize,再是 bearerTypes[32] + bearerTypesSize。写反了,cinterop 不会报错,运行时会把 capability 读成 bearer。
编过之后,Kotlin 侧还有几条从 dump 出来才知道的事实:
HasDefaultNet/IsDefaultNetMetered的出参是int32_t *,不是bool *。Kotlin 侧是IntVar,不是 Preferences 那篇的BooleanVar。写成BooleanVar能编过,读出来的值是错的。判定写成value != 0才算 true。NetConn_NetHandle是结构体值类型,字段netId直接读。- 定长数组用
caps.bearerTypes[i],不要当成List。 *Size字段必须先coerceIn(0, 32)再遍历。C 侧如果返回脏值,Kotlin 会越界,模拟器直接 abort。

左边六条我都踩过。最隐蔽的是「五个 rc 全是 201」:函数名对、so 也链上了,页面却写成断网。那不是 HasDefaultNet 返回 false,是权限没声明,C API 拒绝回答。右边六条是后来能在模拟器上看到 rc=0 的最低条件。
链接时,nativeApp 的 sharedLib 必须再加 -L$sysroot/usr/lib/<abi>-linux-ohos -lnet_connection。只在 def 文件写 linkerOpts,有的发行包不会把它传到最终 so。这些符号运行时由系统 libnet_connection.so 提供,不要把系统库打进 HAP。
5.3 业务桥走 @CName + NAPI,只信 HasDefaultNet
qrcode 和 settings 已经验证过:HarmonyOS Kotlin 的 CAdapter 只导出 stdlib 的 Checksum / Zip / GZip。@CName("ConnectivityCall") 在 ELF 里看得到,ArkTS import KN so 却调不到。缺的是 NAPI 表面,不是 C 符号。
投影只有一句:有默认网就是 Connected(metered),否则 Disconnected。不要用「bearers 里有 WIFI」当连通条件。不要用 GetDefaultNet 的 netId == 0 当断网——文档没保证 0 是非法值。GetDefaultNet 失败只让 netId=-1,不把 Connected 打成 Disconnected。IsDefaultNetMetered 失败则 metered=false,但 rc 留在 JSON。没有合法 handle 时不要调 GetNetCapabilities,官方文档写的是 401。
库不过问能不能访问公网,那是 VALIDATED 能力位的事。应用层若要「能上网」,自己看 capabilities,不要改 Status 语义。
start() / stop() 在本版只翻 isMonitoring。系统里有 OH_NetConn_RegisterDefaultNetConnCallback,回调在网络服务线程,和无协程切片的边界冲突。下一版若补 Flow,从这条 API 接,而不是在 ArkTS 里 setInterval 假装监听。
六、桥接怎么接
调用链从上到下是五层。中间少一层就会在 dlopen 或 undefined is not a function 上爆。
ArkTS → libconnectivity_napi.so (NAPI) → ConnectivityCall() → libohosconnectivity.so (KN) → libnet_connection.so

HAP 里真正要带的只有三份:libconnectivity_napi.so、libohosconnectivity.so、libc++_shared.so。libnet_connection.so 在设备 sysroot,打进包反而会和系统库抢加载。C++ 包装层不解析 JSON,也不调 OH_NetConn_*。解析、IntVar 判定、bearer 名字,都停在 Kotlin。
Demo 按官方 native 模块来:entry/src/main/cpp/napi_init.cpp 编成 libconnectivity_napi.so,内部链接 libohosconnectivity.so,调完用 DisposeString 把 KN 分配的 C 字符串释放掉。C++ 里不解析 JSON,也不调 OH_NetConn_*。解析和映射都在 Kotlin。
example/nativeApp 的 Bridge.kt 吃一小段 JSON,在 Kotlin 里走完整 OhosConnectivity:
@CName("ConnectivityCall")
fun connectivityCall(request: String): String = dispatch(request)
private val connectivity = OhosConnectivity()
// snapshot 一次打五个 C 调用,截图不依赖输入法
val snap = connectivity.snapshot()
有四个地方我写错过。
baseName是String,不是Property<String>。 写成baseName.set("ohosconnectivity")会在 configuration 阶段报Unresolved reference 'set'。正确是baseName = "ohosconnectivity"。@CName参数用String。 KN 生成const char*ABI。返回的 C 字符串必须DisposeString,NAPI 包装层已经做了,ArkTS 不用管。entry/build-profile.json5的abiFilters必须同时有arm64-v8a和x86_64。 本机 DevEco 模拟器是 x86_64。只编 arm64,安装会报9568347,看起来像签名问题,其实是 ABI 缺失。- ArkTS 严格模式禁止
Any。Index.ets用CallResult接口收字段,JSON 用JSON.parse(raw) as CallResult。不要写成any。
linkDebugSharedOhosArm64 / OhosX64 已经 finalizedBy 拷贝任务,会把 libohosconnectivity.so 和 Kotlin/Native 依赖的 libc++_shared.so 一起放进 entry/libs/<abi>/。漏掉 libc++,运行时是 Error loading shared library libc++_shared.so。
CMake 强制 -Wl,-rpath,$ORIGIN、BUILD_WITH_INSTALL_RPATH TRUE、IMPORTED_NO_SONAME TRUE。IMPORTED 库会把本机绝对路径写进 so,真机上那条路径毫无意义。nm_modname 必须是 connectivity_napi,和 oh-package.json5 对上。名字写错,ArkTS 会编译过、运行时报模块找不到。
JSON 解析是手写的极简扫描,只认字符串字段。不要在 KN 侧再拉 kotlinx.serialization——ohos 目标的依赖图会被重新打乱。对外截图只点按钮,不碰输入法。
七、踩坑表
| 现象 | 根因 | 处理 |
|---|---|---|
Unresolved reference: ohosArm64 | 用了官方 Kotlin 2.2.21 | 换成 HarmonyOS Kotlin 2.2.21-1.0.0,仓库放到 pluginManagement 第一位 |
| cinterop 喂 SDK 头直接死 | availability 属性 / 额外头 | 手写 netconn_min.h |
BooleanVar 读 HasDefaultNet | 出参是 int32_t * | 用 IntVar,value != 0 |
| 模拟器 abort | 数组没看 Size | coerceIn(0, 32) |
import KN so 调不到 ConnectivityCall | CAdapter 不导出业务符号 | ArkTS 改 import libconnectivity_napi.so |
9568347 安装失败 | HAP 缺 x86_64 | abiFilters 加上 x86_64,并链接 ohosX64 |
Error loading shared library libc++_shared.so | 只拷了 KN so | copy 任务同时带上 Konan 的 libc++_shared.so |
PackageHap:Unable to allocate 522624KB bitmaps ... 16723968KB heap | packing tool 起 JVM 时堆被要到 16GB | DevEco JBR + JAVA_TOOL_OPTIONS=-Xmx2g |
| 五个 rc 全是 201,看起来像没网 | 没申请 GET_NETWORK_INFO | module.json5 里 requestPermissions |
| 看起来像断网 | 把 netId==0 或带宽 0 当成离线 | 只信 HasDefaultNet |
| Start 之后大字不变 | 本版无 OS 回调 | 看 JSON monitoring 字段;再点 Snapshot |
snapshot_display 失败 | 本模拟器要求后缀 .jpeg | 不要用 .png |
以太网仍报 metered=true | 系统给的计费标记 | 原样上送,不要按 WIFI 去猜 |
权限是 system_grant,不用运行时弹窗,但必须声明。漏了它,五个 rc 全是 201,看起来像没网。Demo 把 rc 打在页面上,就是为了这一幕。
PackageHap 这一条值得单独说。native 和 ArkTS 都已经编过了,失败发生在最后把 so 塞进 HAP 的 Java 工具。日志原文是 Unable to allocate 522624KB bitmaps for parallel garbage collection for the requested 16723968KB heap。不是工程写错,是 JVM 参数。换 JBR 并限制 -Xmx2g 之后,PackageHap 几百毫秒就过。
八、Demo 验收:每个接口一张实拍
包名 org.terminator.ohos.connectivity,Ability EntryAbility。本机模拟器 hdc install 后:
hdc shell aa start -a EntryAbility -b org.terminator.ohos.connectivity
下面每张图都是从模拟器抠出来的,不是合成。状态栏时间 09:22 附近。字幕写的是 KMP connectivity + OH_NetConn C API。不要只贴一张启动图交差。网络库要看连通态、计费、能力位、Start/Stop 翻转。本次模拟器走以太网,大字 Connected,bearers=ETHERNET,五个 rc 全是 0。
8.1 Snapshot:一次打五个 C 调用
启动即 snapshot。状态卡 Connected,netId=101,bearers=ETHERNET,capabilities=INTERNET, NOT_VPN, VALIDATED,五个返回码全是 0。这一步过了,说明 NAPI → KN → OH_NetConn_HasDefaultNet 整条链是通的。如果是 失败: 开头,先查 so 有没有进 HAP,再查权限。
JSON 里 kind=Connected、hasDefaultRc=0。这是 status() 投影和诊断快照一次打通的证据。
8.2 Status:commonMain 表面
点 Status。op=status 的响应更短,只留 kind / isConnected / isMetered / isDisconnected / monitoring / netId。这是给 commonMain 看的表面,不带 bearer 数组。
断开时 isMetered 必须是 false。这是密封类派生属性的契约,不是 UI 自己算的。
8.3 HasDefault:权限探针
点 HasDefault。hasDefaultNet=true,rc=0。缺权限时这里会变成 rc=201,页面会装成没网。把 rc 画出来,就是为了把「没权限」和「没网」分开。
8.4 Metered:原样上送系统标记
点 Metered。metered=true,rc=0。模拟器以太网被标成计费网,以 C API 为准,不要在 Kotlin 里按 WIFI 去猜。真机 Wi-Fi 会不会仍是 true,要等签名后再测,现在不能编造。
8.5 Capabilities:bearer 与能力位
点 Capabilities。bearers=["ETHERNET"],capabilities=["INTERNET","NOT_VPN","VALIDATED"]。VALIDATED 表示系统认为这条默认网能上网,但 Status 本身不读这个位。带宽两个字段在本机模拟器上是 0,C 调用返回码是 0,说明函数成功了,只是这条虚拟网卡没填速率。不要写成「API 没接上」。
8.6 AllNets:诊断字段
点 AllNets。allNetIds=[101]。诊断字段,不参与 status() 投影。多网卡真机上这里会超过一个 id,本模拟器只有默认以太网。
8.7 Start:monitoring 翻成 true
点 Start。last op: start,JSON "monitoring":true。大字仍是 Connected,这是本版预期:start 还不注册 OS 回调。只看大字会误判没生效。
8.8 Stop:monitoring 翻回 false
点 Stop。last op: stop,JSON "monitoring":false。Start / Stop 必须成对截图。飞行模式能拍到 Disconnected 更好。部分模拟器关 WIFI 仍留虚拟以太网,HasDefaultNet 仍可能是 1。以 C API 为准。
九、分层验收
不要把「库编过」和「页面上 kind=Connected」混成一次验收。四层口子分开过。

L1 只证明密封类派生属性没写反。L2 用 nm 看 ConnectivityCall 在不在 ELF 里,在也不代表 ArkTS 能调。L3 打开 HAP 看三个 so 是不是两个 ABI 都有。L4 才看五个 *Rc 是不是 0。上一层绿、下一层红,不要回头改 commonMain。
| 层 | 命令 | 我这边的结果 |
|---|---|---|
| L1 | gradlew :connectivity:jvmTest | connectedStatusFlags、disconnectedStatusFlags、providerBackedStartStop、jvmFactoryReturnsSnapshot 绿。公共 API 语义在 JVM 上对照过 |
| L2 | gradlew :example:nativeApp:linkDebugSharedOhosArm64 :example:nativeApp:linkDebugSharedOhosX64 | 两个 ABI 的 so 都导出 ConnectivityCall,并拷到 entry/libs/<abi>/ |
| L3 编译 | hvigorw assembleHap -p module=entry@default -p product=default --no-daemon | HAP 内 arm64-v8a / x86_64 各含 libconnectivity_napi.so、libohosconnectivity.so、libc++_shared.so |
| L4 运行 | hdc install + 真点击 | Snapshot / Status / HasDefault / Metered / Capabilities / AllNets / Start / Stop 全部有实拍 |
打包必须用 DevEco JBR。本机默认 JAVA_HOME 若指向 JDK 8,或 packing tool 被要到 16GB 堆,PackageHap 会直接起不来。native 其实已经编过了,只是最后把 so 塞进 HAP 的那一步没起来。命令行里固定:
$env:JAVA_HOME = "C:\Program Files\Huawei\DevEco Studio\jbr"
$env:JAVA_TOOL_OPTIONS = "-Xmx2g"
命令行打出来的是 unsigned HAP。这个模拟器接受了 hdc install。真机和正式签名仍走 DevEco 的 "signingConfig": "default",不要在 build-profile.json5 里把这段改空。
Windows 上链出来的 ohos so 不能在本机 dlopen。对照测试走 jvmTest,设备行为走 HAP。不要在第 L4 失败时回头改 commonMain 模型——先 hilog 搜 ConnectivityNapi,看 JSON error 和五个 *Rc。
十、已知限制
- 不是 CMP。 没有 Compose 控件,没有联网动画。KMP 和 CMP 是两条配额线,这个库走 KMP。
- 没有协程。
statusUpdates: SharedFlow<Status>和suspend fun status()不在 2.2.1-ohos.1 里。等官方 coroutines ohos 构件。 - 没有默认网回调。
OH_NetConn_RegisterDefaultNetConnCallback没绑。Start / Stop 只切换本地标志。 - 没有 HTTP 探测。 上游
connectivity-http用实际请求判断「真的能上网」。本仓只读系统 default net。门户网络(PORTALcapability)能看见,但不会自动发探测包。 - JVM 的
metered恒为 false。 Java 没有可移植的计费网 API。JvmConnectivity只给宿主测试对照。 NetSnapshot只在 ohos 源集。netId/bearers/capabilities不是跨平台 API,不要在 commonMain 依赖它们。- 带宽在本模拟器上为 0。 返回码是成功,不要当成没接 API。
- 模拟器以太网报 metered=true。 原样上送,不在 actual 里改写。
- CAdapter 限制仍在。 即使 ELF 能看到
@CName("ConnectivityCall"),ArkTS 也必须走 NAPI。不要在下一篇里再踩一次还当新发现。 - 模拟器 ABI。 本地 DevEco 模拟器是 x86_64。只交 arm64 HAP,安装失败码经常被误判成证书问题。
- 真机签名未跑。 模拟器 unsigned 可装。对外演示请在 DevEco 配
signingConfig: default,再用 arm64 真机走一遍 Wi-Fi / 蜂窝 / 飞行模式。 - 仓库位置。 适配代码在 oh-tpc/jordond,社区组织仍是 CPF-KMP-CMP。不往 Flutter 组织塞 KMP 库。
有人会问:鸿蒙自己就有 netManager,为什么还要 KMP 这一层。答案不是性能,是边界。ArkTS NetConnection 的上下文是 UIAbility,API 是 Promise。KMP 业务拿到的是同步 C ABI,没有 JS runtime。把 KN 的 status() 回调到 ArkTS 再读,等于每次启动都要跨一次 NAPI。本适配站在 OH_NetConn 这一层。Demo 之所以还出现 ArkTS,只是因为要有一个能点的界面,和征文要求的全接口截图。真正给业务用的入口是 OhosConnectivity,不是 Index.ets。
十一、如何提 Issue / PR
上游功能问题优先去 jordond/connectivity。鸿蒙 actual、cinterop 头、NAPI 包装、Demo 安装问题开在适配仓库。不要把 ohosArm64 编译日志丢给上游——那不是他们的 target。上游 GitHub 的 CI 跑不了 ohosArm64(),不要把定制插件的 Gradle 硬推给上游当普通 PR。
请固定带这六项,否则很难判断是权限问题、ABI 问题,还是真的没网:

- ABI:真机
ohosArm64还是模拟器ohosX64 - 操作:
snapshot/status/hasDefault/metered/capabilities/allNets/start/stop - JSON 原文,尤其是五个
*Rc字段 module.json5是否声明GET_NETWORK_INFOhilog搜ConnectivityNapi/ConnectivityDemo- 界面实拍,不要只贴「没网」两个字
PR 建议拆开:src/ohosMain 的实现 / cinterop 是库本体;example/nativeApp 的 @CName 是 C ABI;example/harmonyApp 的 CMake 是 NAPI。不要把三种改动揉进同一个 commit。示例工程保持 "signingConfig": "default"。
克隆适配仓请走 AtomGit 组织页导入后再 git clone,不要写第三方镜像站地址。
十二、小结
这次适配没有发明新的网络协议。commonMain 里的 Status 模型原样工作。鸿蒙侧真正要补的是三块:
- 一份 cinterop 肯吃的精简头,以及 dump 出来才知道的
IntVar/ 定长数组访问方式 - 一个守住 snapshot 语义的
OhosConnectivity,只信HasDefaultNet,把 bearer 和 capability 原样上送 - 一层 CAdapter 不肯给的 NAPI 表面,外加双 ABI so,让 x86_64 模拟器也能装
前两块是常规 KMP。第三块是 HarmonyOS Kotlin 目前的现实,前两篇已经踩过,这篇用系统网络库又确认了一次。谁要是把「so 里有符号」当成「ArkTS 能调用」,或者把「编过」当成「真的读到了网卡」,会在 201 返回码和 9568347 上再浪费整下午。
和 qrcode-kotlin、multiplatform-settings 是同一条工具链上的三个样本。一个纯计算、不链系统 so;一个必须 dlopen 设备上的 libohpreferences.so;一个必须 dlopen 设备上的 libnet_connection.so。三篇一起看,才能判断 ohos target 是不是真的接上了,而不是复制了一份 so。
模拟器已经把 Snapshot / Status / HasDefault / Metered / Capabilities / AllNets / Start / Stop 跑通。仓库在 oh-tpc/jordond。下一步用 DevEco 默认签名在真机上再走一遍 Wi-Fi 和飞行模式。
如果只记住一件事:在 HarmonyOS Kotlin 这条链上,Kotlin/Native so 负责对系统 C API 读网卡,CMake NAPI so 负责被 ArkTS 看见,GET_NETWORK_INFO 负责让五个返回码是 0 而不是 201。三者缺一,截图上要么是断网,要么是装都装不上。
KMP&CMP 社区地址:https://atomgit.com/CPF-KMP-CMP
GitHub 上游:https://github.com/jordond/connectivity
鸿蒙适配版:https://atomgit.com/oh-tpc/jordond
示例工程:example/harmonyApp
欢迎加入 KMP&CMP 鸿蒙社区:https://atomgit.com/CPF-KMP-CMP
更多推荐



所有评论(0)