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 自绘制核心流程

开发者

通过NativeWindow申请Buffer

绘制内容(EGL/GLES)

提交Buffer至图形队列

XComponent持有surface接收Buffer

surface整合进应用UI

用户看到最终渲染效果

图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后,满足以下任一条件时触发。

    1. 组件上树且autoInitialize = true。
    2. 调用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相似,但在跨层协作、生命周期管理、灵活性上有显著优化。以下从核心维度对比:

对比维度 HarmonyOS XComponent Android SurfaceView Android TextureView
核心渲染载体 Surface(通过 NativeWindow 操作) Surface SurfaceTexture
渲染模式 双模式:SURFACE(独立图层)、TEXTURE(UI 合成) 独立图层(SurfaceFlinger 直接渲染) UI 合成(与 View 树同图层)
创建方式 3 种:ArkTS 声明式、ArkTS 自定义节点、NDK XML 布局 / 代码创建 XML 布局 / 代码创建
生命周期管理 2 种:XComponentController(ArkTS 侧)、OH_ArkUI_SurfaceHolder(Native 侧) SurfaceHolder 回调(surfaceCreated/surfaceDestroyed) SurfaceTextureListener 回调
跨层通信 ArkTS↔Native 通过 Node-API 接口契约,支持直接传递 SurfaceId/NodeHandle Java↔Native 通过 JNI,需手动传递 Surface 对象 需通过 SurfaceTexture 跨层传递,流程繁琐
事件支持 基础事件(触摸 / 键盘 / 鼠标)+ 高级手势(长按 / 拖拽) 仅基础触摸事件,高级手势需自定义 支持 View 树事件传递,但合成有延迟
性能表现 SURFACE 模式无 UI 合成开销,TEXTURE 模式合成效率优化 独立图层无合成开销,性能最优 需 GPU 合成,高帧率场景有性能损耗
灵活性 支持 5 种开发范式,适配不同技术栈 仅支持 Java 层开发,Native 扩展需 JNI 支持 Java 层开发,Native 扩展复杂
资源释放 回调明确,支持自动释放 + 手动释放双重保障 依赖 SurfaceHolder 回调,易遗漏释放导致内存泄漏 需监听 TextureView 销毁,释放逻辑复杂

核心优势总结

  1. 跨层协作更高效:XComponent 通过SurfaceId/NodeHandle实现 ArkTS 与 Native 的直接通信,无需像 Android 那样通过 JNI 传递复杂对象;
  2. 生命周期更可控:提供双端生命周期管理方式,回调触发时机明确,减少资源泄漏风险;
  3. 开发范式更灵活:5 种范式覆盖从简单 UI 开发到极致性能需求的全场景,而 Android 仅支持单一创建方式;
  4. 事件支持更丰富:内置高级手势识别,无需像 Android 那样自定义手势检测器;
  5. 渲染模式更灵活:双渲染模式可按需切换,而 Android 需在 SurfaceView 和 TextureView 之间二选一。

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

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

范式类型 创建方式 生命周期管理方式 核心适用场景
范式 1 ArkTS 声明式 UI XComponentController 通用 UI 开发、相机预览 / 视频播放(ArkTS 为主)
范式 2 ArkTS 声明式 UI OH_ArkUI_SurfaceHolder 复杂交互、跨层性能敏感场景(Native 主导渲染)
范式 3 ArkTS 自定义组件节点 XComponentController 自定义复杂组件、动态布局场景
范式 4 ArkTS 自定义组件节点 OH_ArkUI_SurfaceHolder 复杂组件 + 极致渲染性能需求
范式 5 NDK 接口 OH_ArkUI_SurfaceHolder 纯 Native 开发、底层硬件操作场景

五、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
@Component
struct 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_START
static 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
)

# 编译动态库nativerender
add_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
)
运行效果

在这里插入图片描述
在这里插入图片描述

![[HarmonyOS 6 自定义人脸识别模型1:XComponent入门-7.png]]

图4:Native XComponent运行效果

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

核心特点
  • 无需配置libraryname,通过SurfaceId实现跨层通信;
  • ArkTS侧获取SurfaceId并传递给Native层,生命周期与事件回调均在ArkTS侧触发;
  • 适合ArkTS为主、Native为辅的混合开发场景,配置更简洁。
关键差异点
对比维度 Native XComponent ArkTS XComponent
跨层标识 依赖id+动态库名 依赖SurfaceId
回调触发 Native层注册回调 ArkTS侧通过Controller注册
初始化方式 Native层获取OH_NativeXComponent实例 Native层通过SurfaceId创建NativeWindow
核心代码示例(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
@Component
struct 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模型整合的完整流程,为开发高性能视觉类应用提供技术支撑。如果在实战中遇到问题,欢迎在评论区交流~

Logo

讨论HarmonyOS开发技术,专注于API与组件、DevEco Studio、测试、元服务和应用上架分发等。

更多推荐