当前位置: 首页 > news >正文

鸿蒙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)开发学习手册

相关文章:

  • 机器学习 - 准备数据
  • 总结: HQL语句
  • 软考 系统架构设计师系列知识点之系统性能(1)
  • Hive自定义GenericUDF函数
  • 【AI生成文章】flutter ChangeNotifierProvider 实用场景举例
  • RIP,EIGRP,OSPF的区别
  • 医学预测变量筛选的几种方法(R语言版)
  • 2024年阿里云2核4G服务器价格30元、165元和199元1年
  • AI智能分析网关V4在养老院视频智能监控场景中的应用
  • File的学习2
  • ChatGPT无法登录,提示我们检测到可疑的登录行为?如何解决?
  • 南京大学AI考研,宣布改考408!
  • Visual Studio - 添加快捷键图标
  • 无服务器推理在大语言模型中的未来
  • 一次完整的 HTTP 请求所经历的步骤
  • [译]CSS 居中(Center)方法大合集
  • Android Studio:GIT提交项目到远程仓库
  • Angular2开发踩坑系列-生产环境编译
  • angular学习第一篇-----环境搭建
  • Babel配置的不完全指南
  • github指令
  • JavaScript/HTML5图表开发工具JavaScript Charts v3.19.6发布【附下载】
  • Just for fun——迅速写完快速排序
  • nodejs调试方法
  • React-flux杂记
  • Redis 中的布隆过滤器
  • sessionStorage和localStorage
  • 关于List、List?、ListObject的区别
  • 今年的LC3大会没了?
  • 开年巨制!千人千面回放技术让你“看到”Flutter用户侧问题
  • 力扣(LeetCode)965
  • 删除表内多余的重复数据
  • 什么软件可以剪辑音乐?
  • 使用Maven插件构建SpringBoot项目,生成Docker镜像push到DockerHub上
  • 腾讯视频格式如何转换成mp4 将下载的qlv文件转换成mp4的方法
  • 我从编程教室毕业
  • 走向全栈之MongoDB的使用
  • 如何用纯 CSS 创作一个货车 loader
  • ​猴子吃桃问题:每天都吃了前一天剩下的一半多一个。
  • #我与Java虚拟机的故事#连载16:打开Java世界大门的钥匙
  • (M)unity2D敌人的创建、人物属性设置,遇敌掉血
  • (Redis使用系列) Springboot 使用Redis+Session实现Session共享 ,简单的单点登录 五
  • (Redis使用系列) Springboot 使用redis的List数据结构实现简单的排队功能场景 九
  • (笔试题)分解质因式
  • (笔试题)合法字符串
  • (附源码)spring boot校园健康监测管理系统 毕业设计 151047
  • (附源码)springboot 校园学生兼职系统 毕业设计 742122
  • (附源码)springboot学生选课系统 毕业设计 612555
  • (附源码)ssm高校志愿者服务系统 毕业设计 011648
  • (未解决)jmeter报错之“请在微信客户端打开链接”
  • (转)Linux整合apache和tomcat构建Web服务器
  • .net core webapi 部署iis_一键部署VS插件:让.NET开发者更幸福
  • .NET Core工程编译事件$(TargetDir)变量为空引发的思考
  • .net MVC中使用angularJs刷新页面数据列表
  • .Net Winform开发笔记(一)