HarmonyOS 6 自定义人脸识别模型 1:XComponent 入门

作者:轻口味
  • 2026-04-03
    北京
  • 本文字数:5421 字

    阅读完需:约 18 分钟

HarmonyOS 6 自定义人脸识别模型1:XComponent入门

一、背景与核心价值

在 HarmonyOS 应用开发中,面对实时画面处理、复杂图形渲染、硬件资源直操作等场景(如人脸识别中的相机预览流解析、AI 模型推理结果叠加显示),传统 UI 组件往往难以满足性能与灵活性需求。而 XComponent 作为 HarmonyOS 提供的自定义渲染组件,恰好解决了这一痛点——它支持 EGL/OpenGLES 图形渲染与媒体数据写入,通过直接操作 NativeWindow 实现高效绘制,成为复杂场景开发的核心技术支撑。


本系列博客将以“自定义人脸识别模型”为目标,逐步拆解开发流程。第一篇作为入门篇,将聚焦 XComponent 的核心原理、两种应用场景与实战开发,为后续整合相机流、AI 推理模型打下基础。

二、XComponent 核心原理速览

2.1 什么是 XComponent?

XComponent 是 HarmonyOS 专为复杂自定义渲染设计的组件,核心作用是提供一个可直接操作的surface(绘图表面),开发者通过NativeWindow接口申请、提交绘制缓冲区(Buffer),最终由 XComponent 将surface整合到应用 UI 界面中。


其核心特性包括:


  • 两种渲染类型:

  • XComponentType.SURFACE:自定义绘制内容独立显示,适合全屏渲染(如游戏、相机预览);

  • XComponentType.TEXTURE:绘制内容与 XComponent 组件内容合成显示,适合局部叠加(如人脸识别框、水印)。

  • 跨层通信能力:支持 ArkTS 层与 Native 层的数据交互、事件回调,满足混合开发需求。

2.2 自绘制核心流程


图 1:XComponent 自绘制原理流程图

2.3 生命周期核心事件

XComponent 的生命周期与surface的创建、销毁强绑定,核心事件包括:


  • onLoad:surface 准备就绪时触发,可获取 Native 层方法上下文,用于初始化渲染环境;

  • onDestroy:组件销毁时触发,需在此释放NativeWindow、EGL 上下文等资源,避免内存泄漏。


两种场景的生命周期时序图:

ArkTS XComponent 生命周期时序图

对于需要在 ArkTS 侧使用已封装接口进行功能开发(如相机预览、视频播放等)或对跨语言性能损耗不敏感的跨语言开发,建议直接在 ArkTS 侧使用 XComponentController 管理 Surface 生命周期。


  • onSurfaceCreated 回调,触发时刻:XComponent 创建完成且创建好 Surface 后触发。ArkTS 侧 onSurfaceCreated 的时序如下图:


  • onSurfaceChanged 回调,触发时刻:Surface 大小变化触发重新布局之后触发。ArkTS 侧 onSurfaceChanged 的时序如下图:


  • onSurfaceDestroyed 回调,触发时刻:XComponent 组件被销毁时触发,与一般 ArkUI 的组件销毁时机一致。ArkTS 侧 onSurfaceDestroyed 的时序图:


Native XComponent 生命周期时序图

对于复杂的交互逻辑需跨语言开发,追求极致渲染性能或业务需求自主控制 Surface 的创建和销毁的,建议在 Native 侧使用 OH_ArkUI_SurfaceHolder 管理 Surface 生命周期。其生命周期触发时机如下:


  • OnSurfaceCreated 回调,触发时刻:当 XComponent 创建完成且创建好 Surface 后,满足以下任一条件时触发。

  • 组件上树且 autoInitialize = true。

  • 调用 OH_ArkUI_XComponent_Initialize。Native 侧 OnSurfaceCreated 的时序如下图:


  • OnSurfaceChanged 回调,触发时刻:OnSurfaceCreated 回调成功触发且 Surface 大小变化触发重新布局之后触发。Native 侧 OnSurfaceChanged 的时序如下图:


  • OnSurfaceDestroyed 回调,触发时刻:组件下树且 autoInitialize=true 或者调用 OH_ArkUI_XComponent_Finalize 后触发。Native 侧 OnSurfaceDestroyed 的时序图:



三、与 Android 自定义渲染组件深度对比

HarmonyOS XComponent 的设计思路与 Android 的SurfaceView/TextureView相似,但在跨层协作、生命周期管理、灵活性上有显著优化。以下从核心维度对比:


核心优势总结

  1. 跨层协作更高效:XComponent 通过SurfaceId/NodeHandle实现 ArkTS 与 Native 的直接通信,无需像 Android 那样通过 JNI 传递复杂对象;

  2. 生命周期更可控:提供双端生命周期管理方式,回调触发时机明确,减少资源泄漏风险;

  3. 开发范式更灵活:5 种范式覆盖从简单 UI 开发到极致性能需求的全场景,而 Android 仅支持单一创建方式;

  4. 事件支持更丰富:内置高级手势识别,无需像 Android 那样自定义手势检测器;

  5. 渲染模式更灵活:双渲染模式可按需切换,而 Android 需在 SurfaceView 和 TextureView 之间二选一。

四、XComponent 五大开发范式全解析

开发范式是标准化的流程模板,XComponent 基于 "创建方式 + 生命周期管理方式" 的组合,提供 5 种开发范式,覆盖不同技术栈需求:


五、XComponent 两大应用场景实战

XComponent 提供两种核心开发场景,分别适用于不同的技术栈需求。以下基于 HarmonyOS 6,以“绘制可点击变色的五角星”为例,拆解实战步骤。

5.1 场景 1:Native XComponent(C++主导渲染)

核心特点

  • 需配置libraryname(动态库名称)、id(唯一标识);

  • Native 层注册生命周期与事件回调,直接操作NativeWindow

  • 适合需要高效调用 C++图形库、硬件加速的场景(如人脸识别模型推理)。

开发步骤(关键代码+解释)

步骤 1:ArkTS 侧定义 XComponent
// 声明Native侧接口export default interface XComponentContext {  drawPattern(): void; // 绘制五角星  getStatus(): { hasDraw: boolean; hasChangeColor: boolean }; // 获取渲染状态}
@Entry@Componentstruct NativeXComponentDemo { private xComponentContext: XComponentContext | undefined = undefined; // 配置XComponent属性:id唯一、类型SURFACE、绑定动态库nativerender private xComponentAttrs: XComponentAttrs = { id: 'starRenderId', // 必须唯一 type: XComponentType.SURFACE, libraryname: 'nativerender' // 与Native层模块名一致 };
build() { Column() { XComponent(this.xComponentAttrs) .focusable(true) // 支持键盘事件 .onLoad((context) => { // 初始化Native层上下文 this.xComponentContext = context as XComponentContext; // 调用Native层绘制方法 this.xComponentContext?.drawPattern(); }) .onDestroy(() => { console.log("XComponent销毁,释放资源"); }) .width('80%') .height(300);
Button("切换颜色") .onClick(() => { const status = this.xComponentContext?.getStatus(); if (status) status.hasChangeColor = true; }) } .width('100%') .height('100%') .justifyContent(FlexAlign.Center); }}
复制代码
步骤 2:Native 层 Node-API 注册
// napi_init.cpp:将C++方法暴露给ArkTS侧#include <napi/native_api.h>#include "plugin_manager.h"
EXTERN_C_STARTstatic napi_value Init(napi_env env, napi_value exports) { // 暴露getContext接口,用于获取XComponent实例 napi_property_descriptor desc[] = { {"getContext", nullptr, PluginManager::GetContext, nullptr, nullptr, nullptr, napi_default, nullptr} }; napi_define_properties(env, exports, sizeof(desc)/sizeof(desc[0]), desc); // 导出绘制相关方法(drawPattern、getStatus) PluginManager::GetInstance()->Export(env, exports); return exports;}EXTERN_C_END
// 注册模块,模块名需与ArkTS侧libraryname一致static napi_module nativerenderModule = { .nm_version = 1, .nm_register_func = Init, .nm_modname = "nativerender", // 关键:与libraryname匹配 .nm_priv = nullptr, .reserved = {0}};
// 自动注册模块extern "C" __attribute__((constructor)) void RegisterModule(void) { napi_module_register(&nativerenderModule);}
复制代码
步骤 3:事件回调与渲染实现

核心是通过OH_NativeXComponent_RegisterCallback注册生命周期与触摸/按键事件,利用 EGL/GLES 绘制图形:


// plugin_render.cpp:渲染逻辑实现void PluginRender::RegisterCallback(OH_NativeXComponent* nativeXComponent) {  // 注册surface创建、改变、销毁回调  renderCallback_.OnSurfaceCreated = OnSurfaceCreatedCB;  renderCallback_.OnSurfaceChanged = OnSurfaceChangedCB;  renderCallback_.OnSurfaceDestroyed = OnSurfaceDestroyedCB;  // 注册触摸事件回调(用于点击变色)  renderCallback_.DispatchTouchEvent = DispatchTouchEventCB;  OH_NativeXComponent_RegisterCallback(nativeXComponent, &renderCallback_);}
// surface创建时初始化EGL环境void OnSurfaceCreatedCB(OH_NativeXComponent* component, void* window) { std::string id = GetXComponentId(component); // 获取唯一ID auto render = PluginRender::GetInstance(id); uint64_t width, height; OH_NativeXComponent_GetXComponentSize(component, window, &width, &height); // 初始化EGL上下文,准备绘制 render->eglCore_->EglContextInit(window, width, height); render->eglCore_->Background(); // 绘制背景}
// 触摸事件触发颜色切换void DispatchTouchEventCB(OH_NativeXComponent* component, void* window) { OH_NativeXComponent_TouchEvent touchEvent; OH_NativeXComponent_GetTouchEvent(component, window, &touchEvent); if (touchEvent.type == OH_NATIVEXCOMPONENT_UP) { // 手指抬起时 std::string id = GetXComponentId(component); auto render = PluginRender::GetInstance(id); render->eglCore_->ChangeColor(); // 切换五角星颜色 }}
复制代码
步骤 4:CMakeLists 配置(编译动态库)
cmake_minimum_required(VERSION 3.4.1)project(XComponentDemo)
# 头文件目录include_directories( ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_SOURCE_DIR}/include)
# 编译动态库nativerenderadd_library(nativerender SHARED render/egl_core.cpp render/plugin_render.cpp manager/plugin_manager.cpp napi_init.cpp)
# 链接依赖库(EGL、GLES、日志等)target_link_libraries(nativerender PUBLIC EGL GLESv3 hilog_ndk.z ace_ndk.z ace_napi.z uv)
复制代码
运行效果



图 4:Native XComponent 运行效果(左:初始状态;右:点击后变色)

5.2 场景 2:ArkTS XComponent(ArkTS 主导渲染)

核心特点

  • 无需配置libraryname,通过SurfaceId实现跨层通信;

  • ArkTS 侧获取SurfaceId并传递给 Native 层,生命周期与事件回调均在 ArkTS 侧触发;

  • 适合 ArkTS 为主、Native 为辅的混合开发场景,配置更简洁。

关键差异点

核心代码示例(ArkTS 侧)

// 重写XComponentController,监听Surface生命周期class MyXComponentController extends XComponentController {  // Surface创建时传递SurfaceId到Native层  onSurfaceCreated(surfaceId: string): void {    console.log(`Surface创建:${surfaceId}`);    nativeRender.SetSurfaceId(BigInt(surfaceId)); // 传递给Native  }
// Surface尺寸改变时更新 onSurfaceChanged(surfaceId: string, rect: SurfaceRect): void { nativeRender.ChangeSurface(BigInt(surfaceId), rect.surfaceWidth, rect.surfaceHeight); }
// Surface销毁时释放资源 onSurfaceDestroyed(surfaceId: string): void { nativeRender.DestroySurface(BigInt(surfaceId)); }}
@Entry@Componentstruct ArkTSXComponentDemo { private xComponentController = new MyXComponentController();
build() { Column() { XComponent({ type: XComponentType.SURFACE, controller: this.xComponentController }) .width('80%') .height(300);
Button("绘制五角星") .onClick(() => { const surfaceId = this.xComponentController.getXComponentSurfaceId(); nativeRender.DrawPattern(BigInt(surfaceId)); // 调用Native绘制 }); } .width('100%') .height('100%') .justifyContent(FlexAlign.Center); }}
复制代码

六、注意事项与避坑指南

  1. id/SurfaceId 唯一性:多个 XComponent 共存时,需保证id(Native 场景)或SurfaceId+随机数(ArkTS 场景)唯一,否则会导致资源缓存冲突;

  2. 资源释放必须及时onDestroyOnSurfaceDestroyed回调中,需释放NativeWindow、EGL 上下文、动态库实例,避免野指针崩溃;

  3. 禁止跨线程访问接口:文档明确说明 XComponent 的 NDK 接口不支持跨线程调用,需在同一线程处理渲染与事件;

  4. typeNode 组件特殊处理:若使用typeNode创建 XComponent,需先通过OH_NativeWindow_NativeWindowHandleOpt设置缓冲区尺寸,否则绘制失败。

七、总结与后续规划

7.1 核心回顾

XComponent 作为 HarmonyOS 复杂渲染的核心组件,通过NativeWindow与 EGL/GLES 的结合,实现了高效、灵活的自定义绘制能力。本文重点讲解了:


  • XComponent 的核心原理与两种渲染类型;

  • Native XComponent 与 ArkTS XComponent 的开发流程、差异对比;

  • 实战中需注意的资源管理、唯一性约束等关键问题。

7.2 系列博客预告

本系列的目标是实现“自定义人脸识别模型”,后续将逐步推进:


  • 第 2 篇:基于 XComponent 实现相机预览流捕获与实时渲染;

  • 第 3 篇:集成轻量级人脸识别 AI 模型(如 MTCNN),实现人脸检测;

  • 第 4 篇:优化渲染性能,实现人脸框实时叠加与模型推理加速。


通过本系列,你将掌握 HarmonyOS 中复杂渲染+AI 模型整合的完整流程,为开发高性能视觉类应用提供技术支撑。如果在实战中遇到问题,欢迎在评论区交流~

发布于: 2026-04-03阅读数: 124
用户头像

轻口味

关注

🏆2021年InfoQ写作平台-签约作者 🏆 2017-10-17 加入

Android、音视频、AI相关领域从业者。 欢迎加我微信wodekouwei拉您进InfoQ音视频沟通群 邮箱:qingkouwei@gmail.com

评论

发布
暂无评论