鸿蒙ArkTS实战开发-Native XComponent组件的使用

介绍

本篇Codelab主要介绍如何使用XComponent组件调用NAPI来创建EGL/GLES环境,实现在主页面绘制一个正方形,并可以改变正方形的颜色。本篇CodeLab使用Native C++模板创建。

如图所示,点击绘制矩形按钮,XComponent组件绘制区域中渲染出一个正方形,点击绘制区域,正方形显示另一种颜色,点击绘制矩形按钮正方形还原至初始绘制的颜色。

相关概念

  • EGL(Embedded Graphic Library):EGL 是Khronos渲染API (如OpenGL ES 或 OpenVG) 与底层原生窗口系统之间的接口。
  • XComponent:可用于EGL/OpenGLES和媒体数据写入,并显示在XComponent组件。

环境搭建

软件要求

  • DevEco Studio版本:DevEco Studio 3.1 Release。
  • OpenHarmony SDK版本:API version 9。

硬件要求

  • 开发板类型:润和RK3568开发板。
  • OpenHarmony系统:3.2 Release。

环境搭建

完成本篇Codelab我们首先要完成开发环境的搭建,本示例以RK3568开发板为例,参照以下步骤进行:

  1. 获取OpenHarmony系统版本:标准系统解决方案(二进制)。以3.2 Release版本为例:

2.搭建烧录环境。

    1. 完成DevEco Device Tool的安装
    2. 完成RK3568开发板的烧录

3.搭建开发环境。

    1. 开始前请参考工具准备,完成DevEco Studio的安装和开发环境配置。
    2. 开发环境配置完成后,请参考使用工程向导创建工程(模板选择“Empty Ability”)。
    3. 工程创建完成后,选择使用真机进行调测。

代码结构解读

代码目录结构图

本篇Codelab只对核心代码进行讲解。

使用Native C++模板创建项目会自动生成cpp文件夹、types文件夹、CMakeList.txt文件,开发者可以根据实际情况自行添加修改其他文件及文件夹。

├──entry/src/main
│  ├──cpp                           // C++代码区
│  │  ├──CMakeLists.txt             // CMake配置文件
│  │  ├──napi_init.cpp              // C++源代码
│  │  ├──common
│  │  │  └──common.h                // 常量定义文件
│  │  ├──manager                    // 生命周期管理模块
│  │  │  ├──plugin_manager.cpp
│  │  │  └──plugin_manager.h
│  │  ├──render                     // 渲染模块
│  │  │  ├──egl_core.cpp
│  │  │  ├──egl_core.h
│  │  │  ├──plugin_render.cpp
│  │  │  └──plugin_render.h
│  │  └──types                      // 接口存放文件夹
│  │     └──libhello
│  │        ├──index.d.ts           // 接口文件
│  │        └──oh-package.json5     // 接口注册配置文件
│  └──ets                           // 代码区
│     ├──common
│     │  └──CommonConstants.ets     // 常量定义文件
│     ├──entryability
│     │  └──EntryAbility.ts         // 程序入口类
│     └──pages                      // 页面文件
│        └──Index.ets               // 主界面
└──entry/src/main/resources         // 资源文件目录

应用架构

应用架构如图所示。其中,C++侧代码用于实现业务逻辑,ArkTS侧代码用于展示前端界面以及调用相关方法。

  • C++侧:实现图形渲染方法并进行接口函数封装,管理应用和页面的生命周期。
  • ArkTS侧:实现前端界面,调用C++侧的图形渲染方法,并在应用和页面的生命周期调用相关C++侧方法。
  • CMake编译工具链:跨平台的编译工具,将C++侧代码编译成so文件提供给ArkTS侧。

界面设计

主界面由标题、绘制区域、按钮组成。Index.ets文件完成界面实现,使用Column及Row容器组件进行布局。

// Index.ets
@Entry
@Component
struct Index {...build() {Column() {Row() {...}.height($r('app.float.title_height'))Column() {XComponent({...})}.height(CommonConstants.XCOMPONENT_HEIGHT)Row() {Button($r('app.string.button_text')).fontSize($r('app.float.button_font_size')).fontWeight(CommonConstants.FONT_WEIGHT)}.width(CommonConstants.FULL_PARENT)}.width(CommonConstants.FULL_PARENT).height(CommonConstants.FULL_PARENT)}
}

ArkTS侧方法调用

ArkTS侧方法调用步骤如下:

  • 使用import语句导入编译生成的动态链接库文件。
  • 增加XComponent组件,设置XComponent组件的唯一标识id,指定XComponent组件类型及需要链接的动态库名称。
  • 组件链接动态库加载完成后回调onLoad()方法,指定XComponent组件的上下文环境,上下文环境包含来自C++挂载的方法。
  • 新增Button组件,绑定由NAPI注册的drawRectangle()方法,实现绘制正方形的功能。
// Index.ets
// 导入动态链接库
import nativerender from 'libnativerender.so';@Entry
@Component
struct Index {// XComponent实例对象的contextprivate xComponentContext: Record<string, () => void> = {};...build() {...// 增加XComponent组件XComponent({id: CommonConstants.XCOMPONENT_ID,type: CommonConstants.XCOMPONENT_TYPE,libraryname: CommonConstants.XCOMPONENT_LIBRARY_NAME}).onLoad((xComponentContext?: object | Record<string, () => void>) => {if (xComponentContext) {this.xComponentContext = xComponentContext as Record<string, () => void>;}})...// 增加Button组件Button($r('app.string.button_text')).onClick(() => {if (this.xComponentContext) {this.xComponentContext.drawRectangle();}})...}
}

C++侧功能实现

渲染功能实现

  • 进行环境的初始化,包括初始化可用的EGLDisplay、确定可用的surface配置、创建渲染区域surface、创建并关联上下文等。
// egl_core.cpp
bool EGLCore::EglContextInit(void *window, int width, int height)
{OH_LOG_Print(LOG_APP, LOG_INFO, LOG_PRINT_DOMAIN, "EGLCore", "EglContextInit execute");if ((nullptr == window) || (0 >= width) || (0 >= height)) {OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "EGLCore", "EglContextInit: param error");return false;}m_width = width;m_height = height;if (0 < m_width) {// 计算绘制矩形宽度百分比m_widthPercent = FIFTY_PERCENT * m_height / m_width;}m_eglWindow = static_cast<EGLNativeWindowType>(window);// 初始化displaym_eglDisplay = eglGetDisplay(EGL_DEFAULT_DISPLAY);if (EGL_NO_DISPLAY == m_eglDisplay) {OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "EGLCore", "eglGetDisplay: unable to get EGL display");return false;}EGLint majorVersion;EGLint minorVersion;if (!eglInitialize(m_eglDisplay, &majorVersion, &minorVersion)) {OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "EGLCore","eglInitialize: unable to get initialize EGL display");return false;}// 选择配置const EGLint maxConfigSize = 1;EGLint numConfigs;if (!eglChooseConfig(m_eglDisplay, ATTRIB_LIST, &m_eglConfig, maxConfigSize, &numConfigs)) {OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "EGLCore", "eglChooseConfig: unable to choose configs");return false;}// 创建环境return CreateEnvironment();
}bool EGLCore::CreateEnvironment()
{// 创建surfaceif (nullptr == m_eglWindow) {OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "EGLCore", "m_eglWindow is null");return false;}m_eglSurface = eglCreateWindowSurface(m_eglDisplay, m_eglConfig, m_eglWindow, NULL);if (nullptr == m_eglSurface) {OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "EGLCore","eglCreateWindowSurface: unable to create surface");return false;}// 创建contextm_eglContext = eglCreateContext(m_eglDisplay, m_eglConfig, EGL_NO_CONTEXT, CONTEXT_ATTRIBS);if (!eglMakeCurrent(m_eglDisplay, m_eglSurface, m_eglSurface, m_eglContext)) {OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "EGLCore", "eglMakeCurrent failed");return false;}// 创建programm_program = CreateProgram(VERTEX_SHADER, FRAGMENT_SHADER);if (PROGRAM_ERROR == m_program) {OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "EGLCore", "CreateProgram: unable to create program");return false;}return true;
}
  • 使用EGL渲染正方形。实现一个渲染函数,通过调用EGL相关API进行绘制。绘制颜色使用小数表示,需要将十六进制颜色转换成十进制数值除以255计算。例如#182431转换十进制分别为24/36/49。
// common.h
/*** 绘制矩形颜色 #7E8FFB*/
const GLfloat DRAW_COLOR[] = { 126.0f / 255, 143.0f / 255, 251.0f / 255, 1.0f };/*** 绘制背景颜色 #182431*/
const GLfloat BACKGROUND_COLOR[] = { 24.0f / 255, 36.0f / 255, 49.0f / 255, 1.0f };/*** 绘制背景顶点*/
const GLfloat BACKGROUND_RECTANGLE_VERTICES[] = {-1.0f, 1.0f,1.0f, 1.0f,1.0f, -1.0f,-1.0f, -1.0f
};// egl_core.cpp
void EGLCore::Draw()
{m_flag = false;OH_LOG_Print(LOG_APP, LOG_INFO, LOG_PRINT_DOMAIN, "EGLCore", "Draw");// 绘制准备工作GLint position = PrepareDraw();if (POSITION_ERROR == position) {OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "EGLCore", "Draw get position failed");return;}// 绘制背景if (!ExecuteDraw(position, BACKGROUND_COLOR, BACKGROUND_RECTANGLE_VERTICES,sizeof(BACKGROUND_RECTANGLE_VERTICES))) {OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "EGLCore", "Draw execute draw background failed");return;}// 确定绘制矩形的顶点,使用绘制区域的百分比表示const GLfloat rectangleVertices[] = {-m_widthPercent, FIFTY_PERCENT,m_widthPercent, FIFTY_PERCENT,m_widthPercent, -FIFTY_PERCENT,-m_widthPercent, -FIFTY_PERCENT};// 绘制矩形if (!ExecuteDraw(position, DRAW_COLOR, rectangleVertices, sizeof(rectangleVertices))) {OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "EGLCore", "Draw execute draw rectangle failed");return;}// 绘制后操作if (!FinishDraw()) {OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "EGLCore", "Draw FinishDraw failed");return;}// 标记已绘制m_flag = true;
}// 绘制前准备,获取position,创建成功时position值从0开始
GLint EGLCore::PrepareDraw()
{if ((nullptr == m_eglDisplay) || (nullptr == m_eglSurface) || (nullptr == m_eglContext) ||(!eglMakeCurrent(m_eglDisplay, m_eglSurface, m_eglSurface, m_eglContext))) {OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "EGLCore", "PrepareDraw: param error");return POSITION_ERROR;}// 下列方法无返回值glViewport(DEFAULT_X_POSITION, DEFAULT_X_POSITION, m_width, m_height);glClearColor(GL_RED_DEFAULT, GL_GREEN_DEFAULT, GL_BLUE_DEFAULT, GL_ALPHA_DEFAULT);glClear(GL_COLOR_BUFFER_BIT);glUseProgram(m_program);return glGetAttribLocation(m_program, POSITION_NAME);
}// 依据传入参数在指定区域绘制指定颜色
bool EGLCore::ExecuteDraw(GLint position, const GLfloat *color, const GLfloat rectangleVertices[],unsigned long vertSize)
{if ((0 > position) || (nullptr == color) || (RECTANGLE_VERTICES_SIZE != vertSize / sizeof(rectangleVertices[0]))) {OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "EGLCore", "ExecuteDraw: param error");return false;}// 下列方法无返回值glVertexAttribPointer(position, POINTER_SIZE, GL_FLOAT, GL_FALSE, 0, rectangleVertices);glEnableVertexAttribArray(position);glVertexAttrib4fv(1, color);glDrawArrays(GL_TRIANGLE_FAN, 0, TRIANGLE_FAN_SIZE);glDisableVertexAttribArray(position);return true;
}// 结束绘制操作
bool EGLCore::FinishDraw()
{// 强制刷新缓冲glFlush();glFinish();// 交换前后缓存return eglSwapBuffers(m_eglDisplay, m_eglSurface);
}
  • 改变正方形的颜色。重新绘制一个大小相同、颜色不同的正方形,与原正方形地址交换,实现改变颜色的功能。
// common.h
/*** 改变后绘制的颜色 #92D6CC*/
const GLfloat CHANGE_COLOR[] = { 146.0f / 255, 214.0f / 255, 204.0f / 255, 1.0f };// egl_core.cpp
void EGLCore::ChangeColor()
{// 界面未绘制矩形时退出if (!m_flag) {return;}// 绘制准备工作GLint position = PrepareDraw();if (POSITION_ERROR == position) {OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "EGLCore", "ChangeColor get position failed");return;}// 绘制背景if (!ExecuteDraw(position, BACKGROUND_COLOR, BACKGROUND_RECTANGLE_VERTICES,sizeof(BACKGROUND_RECTANGLE_VERTICES))) {OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "EGLCore", "ChangeColor execute draw background failed");return;}// 确定绘制矩形的顶点,使用绘制区域的百分比表示const GLfloat rectangleVertices[] = {-m_widthPercent, FIFTY_PERCENT,m_widthPercent, FIFTY_PERCENT,m_widthPercent, -FIFTY_PERCENT,-m_widthPercent, -FIFTY_PERCENT};// 使用新的颜色绘制矩形if (!ExecuteDraw(position, CHANGE_COLOR, rectangleVertices, sizeof(rectangleVertices))) {OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "EGLCore", "ChangeColor execute draw rectangle failed");return;}// 结束绘制if (!FinishDraw()) {OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "EGLCore", "ChangeColor FinishDraw failed");}
}

使用NAPI将C++方法传递给ArkTS

  • 创建接口函数NapiDrawRectangle(),封装对应C++渲染方法。根据XComponent组件信息,获取对应的渲染模块render,调用绘制矩形的方法。

注册为ArkTS侧接口的方法,固定为napi_value (*napi_callback)(napi_env env, napi_callback_info info)类型,不可更改。napi_value为NAPI定义的指针,无返回值时返回 nullptr。

// plugin_render.cpp
// NAPI注册方法固定参数及返回值类型,无返回值时返回nullptr
napi_value PluginRender::NapiDrawRectangle(napi_env env, napi_callback_info info)
{OH_LOG_Print(LOG_APP, LOG_INFO, LOG_PRINT_DOMAIN, "PluginRender", "NapiDrawRectangle");if ((nullptr == env) || (nullptr == info)) {OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "PluginRender", "NapiDrawRectangle: env or info is null");return nullptr;}// 获取环境变量参数napi_value thisArg;if (napi_ok != napi_get_cb_info(env, info, nullptr, nullptr, &thisArg, nullptr)) {OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "PluginRender", "NapiDrawRectangle: napi_get_cb_info fail");return nullptr;}// 获取环境变量中XComponent实例napi_value exportInstance;if (napi_ok != napi_get_named_property(env, thisArg, OH_NATIVE_XCOMPONENT_OBJ, &exportInstance)) {OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "PluginRender","NapiDrawRectangle: napi_get_named_property fail");return nullptr;}OH_NativeXComponent *nativeXComponent = nullptr;if (napi_ok != napi_unwrap(env, exportInstance, reinterpret_cast<void **>(&nativeXComponent))) {OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "PluginRender", "NapiDrawRectangle: napi_unwrap fail");return nullptr;}// 获取XComponent实例的idchar idStr[OH_XCOMPONENT_ID_LEN_MAX + 1] = { '\0' };uint64_t idSize = OH_XCOMPONENT_ID_LEN_MAX + 1;if (OH_NATIVEXCOMPONENT_RESULT_SUCCESS != OH_NativeXComponent_GetXComponentId(nativeXComponent, idStr, &idSize)) {OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "PluginRender","NapiDrawRectangle: Unable to get XComponent id");return nullptr;}std::string id(idStr);PluginRender *render = PluginRender::GetInstance(id);if (render) {// 调用绘制矩形的方法render->m_eglCore->Draw();OH_LOG_Print(LOG_APP, LOG_INFO, LOG_PRINT_DOMAIN, "PluginRender", "render->m_eglCore->Draw() executed");}return nullptr;
}
  • 使用NAPI中的napi_define_properties方法,将接口函数NapiDrawRectangle()注册为ArkTS侧接口drawRectangle(),在ArkTS侧调用drawRectangle()方法,完成正方形的绘制。
// plugin_render.cpp
void PluginRender::Export(napi_env env, napi_value exports)
{if ((nullptr == env) || (nullptr == exports)) {OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "PluginRender", "Export: env or exports is null");return;}// 将接口函数注册为ArkTS侧接口drawRectanglenapi_property_descriptor desc[] = {{ "drawRectangle", nullptr, PluginRender::NapiDrawRectangle, nullptr, nullptr, nullptr, napi_default, nullptr }};if (napi_ok != napi_define_properties(env, exports, sizeof(desc) / sizeof(desc[0]), desc)) {OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "PluginRender", "Export: napi_define_properties failed");}
}

使用NAPI实现触摸事件回调函数

通过将C++方法封装至触摸事件回调函数的方式,实现触摸绘制区域时改变正方形的颜色的功能。

  • 创建一个新函数DispatchTouchEventCB(),将改变颜色的C++方法ChangeColor()封装于其中。
  • 将新函数绑定为组件触摸事件的回调函数,组件绘制区域产生触摸事件时触发,改变正方形展示的颜色。
// plugin_render.cpp
void DispatchTouchEventCB(OH_NativeXComponent *component, void *window)
{OH_LOG_Print(LOG_APP, LOG_INFO, LOG_PRINT_DOMAIN, "Callback", "DispatchTouchEventCB");if ((nullptr == component) || (nullptr == window)) {OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "Callback","DispatchTouchEventCB: component or window is null");return;}char idStr[OH_XCOMPONENT_ID_LEN_MAX + 1] = { '\0' };uint64_t idSize = OH_XCOMPONENT_ID_LEN_MAX + 1;if (OH_NATIVEXCOMPONENT_RESULT_SUCCESS != OH_NativeXComponent_GetXComponentId(component, idStr, &idSize)) {OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "Callback","DispatchTouchEventCB: Unable to get XComponent id");return;}std::string id(idStr);PluginRender *render = PluginRender::GetInstance(id);if (nullptr != render) {// 封装改变颜色的函数render->m_eglCore->ChangeColor();}
}PluginRender::PluginRender(std::string &id)
{this->m_id = id;this->m_eglCore = new EGLCore();auto renderCallback = &PluginRender::m_callback;renderCallback->OnSurfaceCreated = OnSurfaceCreatedCB;renderCallback->OnSurfaceChanged = OnSurfaceChangedCB;renderCallback->OnSurfaceDestroyed = OnSurfaceDestroyedCB;// 设置触摸事件的回调函数,在触摸事件触发时调用NAPI接口函数,从而调用原C++方法renderCallback->DispatchTouchEvent = DispatchTouchEventCB;
}

释放相关资源

释放申请资源的步骤如下:

  • EGLCore类下创建Release()方法,释放初始化环境时申请的资源,包含窗口display、渲染区域surface、环境上下文context等。
  • PluginRender类添加Release()方法,释放EGLCore实例及PluginRender实例。
  • 创建一个新方法OnSurfaceDestroyedCB(),将PluginRender类内释放资源的方法Release()封装于其中。
  • 将新方法绑定为组件销毁事件的回调方法,组件销毁时触发,释放相关资源。
// egl_core.cpp
void EGLCore::Release()
{if ((nullptr == m_eglDisplay) || (nullptr == m_eglSurface) || (!eglDestroySurface(m_eglDisplay, m_eglSurface))) {OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "EGLCore", "Release eglDestroySurface failed");}if ((nullptr == m_eglDisplay) || (nullptr == m_eglContext) || (!eglDestroyContext(m_eglDisplay, m_eglContext))) {OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "EGLCore", "Release eglDestroyContext failed");}if ((nullptr == m_eglDisplay) || (!eglTerminate(m_eglDisplay))) {OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "EGLCore", "Release eglTerminate failed");}
}// plugin_render.cpp
void PluginRender::Release(std::string &id)
{PluginRender *render = PluginRender::GetInstance(id);if (nullptr != render) {render->m_eglCore->Release();delete render->m_eglCore;render->m_eglCore = nullptr;delete render;render = nullptr;m_instance.erase(m_instance.find(id));}
}void OnSurfaceDestroyedCB(OH_NativeXComponent *component, void *window)
{OH_LOG_Print(LOG_APP, LOG_INFO, LOG_PRINT_DOMAIN, "Callback", "OnSurfaceDestroyedCB");if ((nullptr == component) || (nullptr == window)) {OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "Callback","OnSurfaceDestroyedCB: component or window is null");return;}char idStr[OH_XCOMPONENT_ID_LEN_MAX + 1] = { '\0' };uint64_t idSize = OH_XCOMPONENT_ID_LEN_MAX + 1;if (OH_NATIVEXCOMPONENT_RESULT_SUCCESS != OH_NativeXComponent_GetXComponentId(component, idStr, &idSize)) {OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "Callback","OnSurfaceDestroyedCB: Unable to get XComponent id");return;}// 释放申请的资源std::string id(idStr);PluginRender::Release(id);
}PluginRender::PluginRender(std::string &id)
{this->m_id = id;this->m_eglCore = new EGLCore();OH_NativeXComponent_Callback *renderCallback = &PluginRender::m_callback;renderCallback->OnSurfaceCreated = OnSurfaceCreatedCB;renderCallback->OnSurfaceChanged = OnSurfaceChangedCB;// 设置组件销毁事件的回调函数,组件销毁时触发相关操作,释放申请的资源renderCallback->OnSurfaceDestroyed = OnSurfaceDestroyedCB;renderCallback->DispatchTouchEvent = DispatchTouchEventCB;
}

注册与编译

在napi_init.cpp文件中,Init方法注册上文实现的接口函数,从而将封装的C++方法传递出来,供ArkTS侧调用。编写接口的描述信息,根据实际需要可以修改对应参数。__attribute__((constructor))修饰的方法由系统自动调用,使用NAPI接口napi_module_register()传入模块描述信息进行模块注册。Native C++模板创建项目会自动生成此结构代码,开发者可根据实际情况修改其中内容。

// napi_init.cpp
static napi_value Init(napi_env env, napi_value exports)
{OH_LOG_Print(LOG_APP, LOG_INFO, LOG_PRINT_DOMAIN, "Init", "Init begins");if ((nullptr == env) || (nullptr == exports)) {OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "Init", "env or exports is null");return nullptr;}napi_property_descriptor desc[] = {{ "getContext", nullptr, PluginManager::GetContext, nullptr, nullptr, nullptr, napi_default, nullptr }};// 将接口函数注册为ArkTS侧接口getContext()if (napi_ok != napi_define_properties(env, exports, sizeof(desc) / sizeof(desc[0]), desc)) {OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "Init", "napi_define_properties failed");return nullptr;}// 方法内检查环境变量是否包含XComponent组件实例,若实例存在注册绘制相关接口PluginManager::GetInstance()->Export(env, exports);return exports;
}static napi_module nativerenderModule = {.nm_version = 1,.nm_flags = 0,.nm_filename = nullptr,.nm_register_func = Init,        // 入口函数.nm_modname = "nativerender",    // 模块名称.nm_priv = ((void *)0),.reserved = { 0 }
};extern "C" __attribute__((constructor)) void RegisterModule(void)
{napi_module_register(&nativerenderModule);
}

使用CMake工具链将C++源代码编译成动态链接库文件。本篇Codelab中会链接两次动态库,第一次为import语句,第二次为XComponent组件链接动态库。

# CMakeList.txt
# 声明使用 CMAKE 的最小版本号
cmake_minimum_required(VERSION 3.4.1)# 配置项目信息
project(XComponent)# set命令,格式为set(key value),表示设置key的值为value
set(NATIVERENDER_ROOT_PATH ${CMAKE_CURRENT_SOURCE_DIR})# 设置头文件的搜索目录
include_directories(${NATIVERENDER_ROOT_PATH}${NATIVERENDER_ROOT_PATH}/include
)# 添加名为nativerender的库,库文件名为libnativerender.so,添加cpp文件
add_library(nativerender SHAREDrender/egl_core.cpprender/plugin_render.cppmanager/plugin_manager.cppnapi_init.cpp
)
...
# 添加构建需要链接的库
target_link_libraries(nativerender PUBLIC ${EGL-lib} ${GLES-lib} ${hilog-lib} ${libace-lib} ${libnapi-lib} ${libuv-lib} libc++.a)

总结

您已经完成了本次Codelab的学习,并了解到以下知识点:

  1. 使用XComponent组件调用NAPI创建EGL/GLES环境。
  2. 使用OpenGL ES进行开发渲染。
  3. 使用回调函数响应触摸事件。

为了帮助大家更深入有效的学习到鸿蒙开发知识点,小编特意给大家准备了一份全套最新版的HarmonyOS NEXT学习资源,获取完整版方式请点击→HarmonyOS教学视频

HarmonyOS教学视频

鸿蒙语法ArkTS、TypeScript、ArkUI等…视频教程

鸿蒙生态应用开发白皮书V2.0PDF:

获取完整版白皮书方式请点击→《鸿蒙生态应用开发白皮书V2.0PDF

在这里插入图片描述

鸿蒙 (Harmony OS)开发学习手册

一、入门必看

  1. 应用开发导读(ArkTS)
  2. .……

在这里插入图片描述


二、HarmonyOS 概念

  1. 系统定义
  2. 技术架构
  3. 技术特性
  4. 系统安全

在这里插入图片描述

三、如何快速入门?《鸿蒙基础入门学习指南》

  1. 基本概念
  2. 构建第一个ArkTS应用
  3. .……

在这里插入图片描述


四、开发基础知识

  1. 应用基础知识
  2. 配置文件
  3. 应用数据管理
  4. 应用安全管理
  5. 应用隐私保护
  6. 三方应用调用管控机制
  7. 资源分类与访问
  8. 学习ArkTS语言
  9. .……

在这里插入图片描述


五、基于ArkTS 开发

  1. Ability开发
  2. UI开发
  3. 公共事件与通知
  4. 窗口管理
  5. 媒体
  6. 安全
  7. 7.网络与链接
  8. 电话服务
  9. 数据管理
  10. 后台任务(Background Task)管理
  11. 设备管理
  12. 设备使用信息统计
  13. DFX
  14. 国际化开发
  15. 折叠屏系列
  16. .……

在这里插入图片描述


更多了解更多鸿蒙开发的相关知识可以参考:《鸿蒙 (Harmony OS)开发学习手册

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/762928.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Sketch软件:重塑UI/UX设计流程的革命性工具

Sketch是一款在Mac操作系统上运行的矢量图形设计软件&#xff0c;其功能特色丰富多样&#xff0c;深受设计师们的喜爱。以下是Sketch软件的主要功能特色介绍&#xff1a; 专业矢量图形设计&#xff1a;Sketch为UI设计、移动应用设计和Web设计等领域提供了强大的支持。它支持线条…

了解OGC

什么是OGC OGC&#xff08;Open Geospatial Consortium&#xff09;是一个国际性的组织&#xff0c;专注于制定和发布开放的地理信息标准&#xff0c;以促进地理空间数据和服务的互操作性和共享。其目标是在全球范围内推动地理信息系统&#xff08;GIS&#xff09;和地理空间技…

串口控制LED灯

uart.c #include "uart4.h"void uart4_init() {//使能GPIOB GPIOG UART4外设时钟RCC->MP_AHB4ENSETR | (0x1<<1);//GPIOBRCC->MP_AHB4ENSETR | (0x1<<6);//GPIOGRCC->MP_APB1ENSETR | (0X1<<16);//UART4//设置PB2和PG11管脚复用//PB2GPI…

即时编译器在JVM调优战场的决胜策略

目录 一、方法内联 二、循环展开 三、分支预测 四、逃逸分析 4.1 栈上分配 4.2 标量替换 4.3 同步消除 五、冗余消除 JVM中的即时编译器&#xff08;如HotSpot的C1、C2编译器&#xff09;会对代码进行即时编译优化&#xff0c;即时编译优化&#xff08;Just-In-Time Compi…

【JavaScript 中的数据类型】

JavaScript 中的基本数据类型 基本数据类型 数字&#xff08;Number&#xff09;&#xff1a;用于表示数字&#xff0c;包括整数和浮点数。字符串&#xff08;String&#xff09;&#xff1a;用于表示文本数据。布尔值&#xff08;Boolean&#xff09;&#xff1a;用于表示逻…

数据结构奇妙旅程之线性表

线性表&#xff08;Linear List&#xff09;是数据结构中的一种基本类型&#xff0c;它代表了具有相同类型数据元素的一个有限序列。线性表中的数据元素之间是一对一的关系&#xff0c;即除了第一个元素外&#xff0c;每个元素有且仅有一个前驱元素&#xff1b;除了最后一个元素…

Hive常用函数 之 数值处理

Hive常用函数 之 数值处理 以下是Hive中常用的数值处理函数&#xff0c;可用于执行各种数学运算和数值转换操作。 1. ABS()&#xff1a;返回一个数的绝对值。 SELECT ABS(-10); -- 输出: 102. ROUND()&#xff1a;对一个数进行四舍五入。 SELECT ROUND(10.56); -- 输出: 113.…

探索ChatGPT时代下的下一代信息检索系统:机遇与挑战

1 Introduction 2022 年 11 月 30 日&#xff0c;OpenAI 推出了 ChatGPT&#xff0c;这是一款由先进的 GPT3.5 和更高版本的 GPT-4 生成语言模型提供支持的 AI 聊天机器人应用程序。该应用迅速吸引了全球超亿用户&#xff0c;创下了产品快速传播的新纪录。 它能够以对话的方式…

ElasticSearch 用法

首先讲下 ES的倒排序索引 创建倒排索引是对正向索引的一种特殊处理&#xff0c; 将每一个文档的数据利用算法分词&#xff0c;得到一个个词条 创建表&#xff0c;每行数据包括词条、词条所在文档id、位置等信息 因为词条唯一性&#xff0c;可以给词条创建索引&#xff0c;例如…

旅游小程序开发的费用及功能

随着科技的发展和智能手机的普及&#xff0c;越来越多的行业开始利用小程序来进行线上服务。旅游业作为一个重要的服务业&#xff0c;也纷纷推出了自己的旅游小程序&#xff0c;以方便游客在线预订、查询景点信息等。那么&#xff0c;旅游小程序开发的费用是多少&#xff1f;功…

Google研究者们提出了VLOGGER模型

每周跟踪AI热点新闻动向和震撼发展 想要探索生成式人工智能的前沿进展吗&#xff1f;订阅我们的简报&#xff0c;深入解析最新的技术突破、实际应用案例和未来的趋势。与全球数同行一同&#xff0c;从行业内部的深度分析和实用指南中受益。不要错过这个机会&#xff0c;成为AI领…

【探索Linux】—— 强大的命令行工具 P.29(网络编程套接字 —— 简单的TCP网络程序模拟实现)

阅读导航 引言一、TCP协议二、TCP网络程序模拟实现1. 预备代码⭕ThreadPool.hpp&#xff08;线程池&#xff09;⭕makefile文件⭕打印日志文件⭕将当前进程转变为守护进程 2. TCP 服务器端实现&#xff08;TcpServer.hpp&#xff09;3. TCP 客户端实现&#xff08;main函数&…

babyos 学习记录

宏定义头文件 将一个宏定义取不同的数据到不同的数组中&#xff1b; 侵入式链表 struct list_head { struct list_head *next, *prev; }; // 添加&#xff08;list_add_tail/list_add&#xff09;、删除、查找 xx.h // 定义一个用于链表管理的结构体 typedef sturct{ xxx …

探索数据库的世界:分类、作用与特点

数据库是现代信息时代的基础设施之一&#xff0c;它是一种组织、存储、管理和检索数据的系统。数据库在各个领域都有着广泛的应用&#xff0c;如企业、政府、医疗、金融等&#xff0c;它可以帮助企业和组织高效地管理和分析数据&#xff0c;从而做出更好的决策。本文将介绍数据…

matlab矩形薄板小挠度弯曲有限元编程 |【Matlab源码+理论文本】|板单元| Kirchoff薄板 | 板壳单元

专栏导读 作者简介&#xff1a;工学博士&#xff0c;高级工程师&#xff0c;专注于工业软件算法研究本文已收录于专栏&#xff1a;《有限元编程从入门到精通》本专栏旨在提供 1.以案例的形式讲解各类有限元问题的程序实现&#xff0c;并提供所有案例完整源码&#xff1b;2.单元…

分库分表场景下多维查询解决方案(用户+商户)

在采用分库分表设计时&#xff0c;通过一个PartitionKey根据散列策略将数据分散到不同的库表中&#xff0c;从而有效降低海量数据下C端访问数据库的压力。这种方式可以缓解单一数据库的压力&#xff0c;提升了吞吐量&#xff0c;但同时也带来了新的问题。对于B端商户而言&#…

项目技术问题记录-【前端】处理低版本JQuery系UI框架的toggle事件失效的问题

原创作者&#xff1a;田超凡&#xff08;程序员田宝宝&#xff09; 版权所有&#xff0c;引用请注明原作者&#xff0c;严禁复制转载 $.fn.toggle function( fn, fn2 ) { var args arguments,guid fn.guid || $.guid,i0, toggle function( event ) { …

Layui实现删除及修改后停留在当前页

1、功能概述&#xff1f; 我们在使用layui框架的table显示数据的时候&#xff0c;会经常的使用分页技术&#xff0c;这个我们期望能够期望修改数据能停留在当前页&#xff0c;或者删除数据的时候也能够停留在当前页&#xff0c;这样的用户体验会更好一些&#xff0c;但往往事与…

硬核分享|如何将文字转成语音对视频进行配音或旁白解说

硬核分享|如何将文字转成语音对视频进行配音或旁白解说_哔哩哔哩_bilibili 文字转语音工具成为了一种便利而实用的技术应用&#xff0c;它能够将文字内容转化为声音&#xff0c;为我们提供全新的听觉体验。 不论是在阅读、学习、娱乐还是无障碍辅助等方面&#xff0c;文字转语…

Go语言学习--Gin框架之Hello World

Go语言学习–Gin框架之Hello World 文章目录 Go语言学习--Gin框架之Hello World1. 简介2. 下载gin框架3. 开始HelloWorld 1. 简介 Gin 是一个用 Go (Golang) 编写的 Web 框架。 它具有类似 martini 的 API&#xff0c;性能要好得多&#xff0c;多亏了 httprouter&#xff0c;速…