20 问覆盖机制基础(env 线程绑定/Promise 风格/handle scope)、工程实践(下沉判断标准/崩溃符号还原/CMake 集成)与进阶设计(视频帧共享内存流/批量接口防频繁小调用)。
NAPI(Node-API / 原生 API)是鸿蒙应用「性能敏感场景」的必经之路:ArkTS 跑不动的计算(音视频编解码、加解密、图像处理、算法推理)都要下沉到 C/C++。面试中它考察两个维度:机制理解(数据怎么跨语言传递)与工程能力(线程、内存、崩溃怎么管)。这份 20 问覆盖两层。
一、机制与基础(7 问)
1. NAPI 是什么?与 Node.js 的 Node-API 什么关系?
鸿蒙 NAPI 基于 Node-API 规范演进,接口形态相似(napi_env/napi_value 等),但运行环境是 ArkTS 运行时而非 Node.js。可以理解为「同源规范、不同宿主」,代码迁移成本低但不能直接假设行为一致。
2. ArkTS 与 C/C++ 之间的数据怎么传递?
跨语言边界一切皆 napi_value。基本类型用 napi_get_value_* 系列提取;对象用属性读写;大块数据用 ArrayBuffer/TypedArray 零拷贝共享——这是性能题的得分点:小数据走值拷贝,大数据必须走共享内存路径。
3. 同步接口与异步接口怎么选?
耗时超过几毫秒的操作必须异步:同步调用会阻塞 ArkTS 主线程导致掉帧。异步模式:napi_create_async_work 创建异步任务,工作线程执行,完成后切回主线程回调。
4. Promise 风格的原生接口怎么实现?
napi_create_promise 拿到 deferred,异步任务完成后 napi_resolve_deferred。工程上比 callback 风格更符合 ArkTS 的 async/await 使用习惯。
5. napi_env 能在任意线程用吗?
不能。env 与线程绑定,子线程要用 napi_get_threadsafe_function 创建线程安全函数,或在异步任务的 complete 阶段(已切回主线程)再操作 env。这是 NAPI 面试最高频的坑题。
6. native 层的内存生命周期怎么管?
JS 对象持有的 native 指针用 napi_wrap 绑定,GC 回收时 unwrap finalize 回调释放;跨回调持有的引用必须 napi_create_reference 管理,用完 delete_reference。答出「不要裸 new 不还」即可及格。
7. handle scope 是干什么的?
管理 napi_value 的生命周期作用域(napi_open_handle_scope/close),长循环里不手动管理会内存膨胀;跨 scope 保留值要 escape scope。这类细节题答得出,说明真写过。

二、工程实践(7 问)
8. 什么场景值得下沉 native?判断标准?
计算密集(编解码/加密/压缩)、复用现有 C/C++ 库(OpenCV/FFmpeg/算法库)、需要稳定性能上限。纯业务逻辑下沉是负优化——跨边界开销大于收益。
9. 怎么测量「下沉到底值不值」?
先测 ArkTS 版耗时分解:如果大头在数据跨边界序列化而非计算,下沉反而更慢。用 Profiler 对比「总耗时 = 计算 + 边界开销」,边界开销占比高时考虑批量传输(一次传大块)摊薄成本。
10. 线程安全函数(thread-safe function)的原理?
它是一个「可跨线程调用的 JS 回调代理」:子线程调用 tsfn,内部排队,由 JS 线程按调度策略(每 tick 执行数量)实际执行。用于进度通知、数据流推送等子线程 → JS 的持续通信。
11. native 崩溃怎么排查?
崩溃日志带 native 堆栈(so 偏移地址)→ 用 addr2line/符号表还原 → 结合 addr2line 输出定位到源码行。与移动端崩溃治理同构:符号表归档是生命线。
12. C++ 库怎么集成到鸿蒙工程?
CMake 构建,CMakeLists.txt 声明源文件与三方库依赖,产物 so 打进 hap;三so 架构(arm64-v8a 等)按设备分发。预编译库注意 NDK 版本与 libc++_shared 的兼容。
13. NAPI 模块的单元测试怎么做?
纯 C++ 逻辑抽出来用原生测试框架测(不依赖 JS 边界);边界层用 ArkTS 测试驱动真实调用。原则:把可测的逻辑留在 native 内部测,边界层薄到不值得测。
14. 混合开发的模块怎么组织代码结构?
native 模块独立成 har/hso,对外只暴露 NAPI 头文件与 ArkTS 封装层;ArkTS 侧再包一层语义化 API。禁止业务代码直接摸 napi_* ——封装层隔离未来运行时变化。
三、进阶与设计(6 问)
15. 大数据流(如视频帧)持续跨边界的架构?
共享内存 + 通知模式:native 写 ArrayBuffer,用 tsfn 只发「有新帧」的轻量通知,ArkTS 侧按需读取;避免每帧深拷贝 + 每帧回调的双重开销。
16. native 层怎么调用 ArkTS/系统能力?
反向调用走 tsfn 把请求抛回 JS 线程执行,结果经异步任务带回。设计上尽量单向:native 只做计算,系统交互留在 ArkTS 层,避免双向依赖成环。
17. 多个 NAPI 模块之间能共享 native 代码吗?
可以,公共逻辑编译成独立 so,模块间链接共享;注意符号冲突(fvisibility 控制)与 libc++_shared 唯一性。
18. 性能敏感模块怎么防「跨语言频繁小调用」?
批处理接口设计(一次调用处理一批数据)、对象池减少边界分配、将循环整体下沉(在 native 里循环,而不是 ArkTS 循环里每次调 native)。这条是「设计题」的核心答案。
19. NAPI 模块的版本兼容策略?
接口用 napi_get_version 探测能力;新接口提供 ArkTS 侧降级实现;so 按设备架构分发并用 CI 矩阵覆盖多设备真机测试。
20. 给你一个「ArkTS 实现的图像滤镜,性能不达标」的案例,答题框架?
先测量(Profiler 定位热点:像素循环?内存分配?)→ 评估下沉收益(计算占比 vs 边界开销)→ 设计接口(整帧进、整帧出,Uint8Array 零拷贝,异步执行 + 进度通知)→ 建立性能回归基线防退化。有测量、有方案、有验证闭环,就是满分结构。
#鸿蒙面试#NAPI#原生开发#性能优化

