Hello World 应用开发入门指南
本文档以 helloworldpage 为例,带你在 HiDiTing 社区开发板上快速跑通第一个原生 UI 应用,并了解如何基于它构建自己的应用。
应用框架背景知识
MVP + SlicePage 调用流程
用户点击应用图标
│
▼
Ability Manager Service(应用生命周期管理)
│
▼
NativeAbility(全局调度)
│
▼
AbilitySliceProxy<View, Presenter> ← REGIST_MENU 注册(绑定 View 和 Presenter)
├──► View(应用视图)
└──► Presenter(业务逻辑调度)
│ 通过 SlicePageFactory 创建 Hello World 应用页面
▼
SlicePage(Hello World 应用页面) ← REGIST_SLICE_PAGE 注册
│ 通过 OnStart 启动页面
▼
Hello World 页面显示
- View:负责 Hello World 应用页面展示
- Presenter:处理页面业务逻辑,将数据变化反映到 View 层
- SlicePage:实现页面的生命周期管理
页面生命周期
CMake 收集机制和新增文件
nativeapp/CMakeLists.txt :通过 CMakeLists 语法将源码文件添加到编译中
.cpp文件放在nativeui/<myapp>/下,此目录下源文件会通过路径添加到编译列表。- 新增头文件路径在CMake里的 PRIVATE_HEADER 变量:
如果新增
.cpp后没有被编译进去,先确认文件路径正确,再执行fbb build --clean diting-community,排除 CMake 缓存导致的生成文件未更新问题。
快速跑通 Hello World
前置条件
- 使用
pack_diting_community构建目标,并确认helloworldpage已参与固件构建。 - 开发板至少连接一块 SDK 已适配的 QSPI 屏或 MIPI 屏,并启用与屏幕接口、分辨率和时序相匹配的显示驱动。QSPI 与 MIPI 二选一即可,不要求同时连接。
- 没有连接显示屏时仍可完成编译并查看启动日志,但无法通过界面确认 Hello World 的显示效果。
- 屏幕接线和上电检查参见社区开发板使用指南的“屏幕连接”章节。
编译
三种开发环境均可完成构建,本文使用推荐的一站式 CLI。完成环境配置后执行:
烧录与运行
使用一站式 CLI 烧写固件并打开 UART2 串口监视器。以下为 Windows USB DFU 示例;将 COM3 替换为实际日志串口,其他平台和串口烧写参数参见一站式 CLI 开发环境使用指南。
fbb flash -f "$env:FBB_SDK_DIR\output\3322\fwpkg\diting-community.fwpkg" --chip 3322 -d --timeout 180
fbb monitor --port COM3 --baud 750000
烧写完成后,双击 Power Key 进入应用列表,找到并点击 HelloWorld。
预期结果
烧录后逐项验收:
-
fbb build --clean打包通过 - 设备应用列表中出现 HelloWorld
- 点击 HelloWorld,页面显示 Hello World 文字
- 串口日志能看到
HelloWorldPage OnStart

上图用于说明预期界面和文字位置。实机验收时以所连接屏幕的显示效果与 UART2 日志为准。
文件结构与代码走读
文件结构
Hello World 的文件分布在两个目录下,不在同一处:
src/application/wearable/nativeapp/
├── nativeui/
│ └── helloworldpage/
│ ├── HelloWorldPresenter.cpp # 注册到应用列表
│ └── HelloWorldPage.cpp # 页面应用实现
└── nativeui/include/
└── helloworldpage/
├── HelloWorldView.h # View 接口
├── HelloWorldPresenter.h # Presenter 声明
└── HelloWorldPage.h # Page 声明
.cpp 和 .h 分开存放是整个 nativeui 层的统一惯例,新建应用时需要遵循同样的目录结构。
各文件职责
| 文件 | 功能 | 关键内容 |
|---|---|---|
| HelloWorldView.h | 定义页面应用接口 | 继承 View<HelloWorldPresenter>,可按需声明纯虚接口 |
| HelloWorldPresenter.h / HelloWorldPresenter.cpp | 业务逻辑 + 注册入口 | .cpp 里用 REGIST_MENU 注册到应用列表 |
| HelloWorldPage.h / HelloWorldPage.cpp | Hello World 页面逻辑实现 | .cpp 里用 REGIST_SLICE_PAGE 注册页面,OnStart 启动页面 |
关键类说明
| 类名 | 说明 |
|---|---|
HelloWorldPage |
管理UI控件和页面生命周期 |
HelloWorldView |
定义视图交互接口 |
HelloWorldPresenter |
处理业务逻辑和数据 |
HelloWorldPresenter.cpp —— 注册到应用列表
namespace OHOS {
#ifdef SDK_DITING_COMMUNITY
REGIST_MENU(VIEW_HELLOWORLD, HelloWorldView, HelloWorldPresenter, EMPTY_ICON, EMPTY_ICON, "HelloWorld");
// ↑ 唯一应用 ID ↑ View 类 ↑ Presenter 类 ↑ 圆形图标 ↑ 六边形图标 ↑ 应用名称
#endif
}
SDK_DITING_COMMUNITY 确定是被定义的,在程序启动时(全局静态对象初始化阶段)自动执行,无需手动调用。
VIEW_HELLOWORLD 定义在 nativelauncher/include/AppViewIDs.h 的枚举中,是该应用的全局唯一 ID。
EMPTY_ICON/EMPTY_IMAGE是应用资源:
EMPTY_IMAGE路径为user/res/EMPTY.binEMPTY_ICON路径为user/res/EMPTY_ICON.bin开发者请导入实际需要的资源(参见注意事项)。
HelloWorldPage.cpp —— 核心逻辑实现
注册页面
static constexpr uint16_t HELLOWORLD_PAGE = 1;
#ifdef SDK_DITING_COMMUNITY
REGIST_SLICE_PAGE(VIEW_HELLOWORLD, HELLOWORLD_PAGE, HelloWorldPage, true);
// ↑ 所属应用 ID ↑ 页面 ID ↑ 页面类 ↑ 是否为入口页
#endif
一个应用可以注册多个页面,isMainPage = true 用于设置主页面。
OnStart: 启动应用
void HelloWorldPage::OnStart(void *data)
{
// step1. 预加载图片资源(Hello World 用空资源占位)
bool ret = ImageCacheManager::GetInstance().LoadAllInMultiRes(EMPTY_IMAGE);
if (ret == false) {
WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "LoadEmptyImage fail");
return;
}
// step2. 创建根容器,设置容器大小(400×400)
container_ = new UIViewGroup();
if (container_ == nullptr) { return; } // new 可能返回 nullptr 的方式做防御检查
container_->SetPosition(0, 0, HORIZONTAL_RESOLUTION, VERTICAL_RESOLUTION);
container_->SetDraggable(true); // 允许手势拖动(支持向右滑退出应用)
container_->SetTouchable(true);
// step3. 创建文本标签,设置文本布局
helloWorldLabel_ = new UILabel();
if (helloWorldLabel_ == nullptr) { return; }
// x=121, y=161, w=193, h=40 → 在 400×400 屏幕上大致水平居中、略高于中央
helloWorldLabel_->SetPosition(121, 161, 193, 40);
helloWorldLabel_->SetAlign(TEXT_ALIGNMENT_CENTER, TEXT_ALIGNMENT_CENTER);
helloWorldLabel_->SetFont(DEFAULT_VECTOR_FONT_FILENAME, 30);
helloWorldLabel_->SetText("Hello World");
// step4. 组装视图树,交给图形框架渲染
container_->Add(helloWorldLabel_);
AddViewToPageContainer(container_); // 必须调用,否则页面为空
}
析构:释放所有资源
HelloWorldPage::~HelloWorldPage()
{
if (container_ != nullptr) {
container_->RemoveAll(); // 先断开子节点引用,再 delete 容器
delete container_;
container_ = nullptr;
}
if (helloWorldLabel_ != nullptr) {
delete helloWorldLabel_;
helloWorldLabel_ = nullptr;
}
// Load 和 Unload 必须成对,否则造成资源泄漏
ImageCacheManager::GetInstance().UnloadAllInMultiRes(EMPTY_IMAGE);
}
基于 Hello World 开发自己的应用
改动清单
新建一个 Demo(以 MyApp 为例)通常只需要以下改动(正式应用可能还需额外的资源文件、多语言支持或服务依赖):
- 新建
nativeui/myapp/MyAppPresenter.cpp - 新建
nativeui/myapp/MyAppPage.cpp - 新建
nativeui/include/myapp/MyAppView.h - 新建
nativeui/include/myapp/MyAppPresenter.h - 新建
nativeui/include/myapp/MyAppPage.h - 在
AppViewIDs.h中新增VIEW_MYAPP - 在
MyAppPresenter.cpp中调用REGIST_MENU - 在
MyAppPage.cpp中调用REGIST_SLICE_PAGE
CMakeLists.txt(见 CMake 收集机制和新增文件)。
名称修改表
如果开发者想基于 Hello World 开发自己的应用,请将 Hello World 的所有命名按如下规则修改:
| Hello World | MyApp | 位置 |
|---|---|---|
VIEW_HELLOWORLD |
VIEW_MYAPP |
AppViewIDs.h 枚举、两个注册宏 |
HelloWorldView |
MyAppView |
REGIST_MENU 参数、头文件类名 |
HelloWorldPresenter |
MyAppPresenter |
REGIST_MENU 参数、头文件类名、SlicePage<> 模板参数 |
HelloWorldPage |
MyAppPage |
REGIST_SLICE_PAGE 参数、头文件类名 |
HELLOWORLD_PAGE |
MYAPP_MAIN_PAGE |
REGIST_SLICE_PAGE 参数 |
"HelloWorld" |
"MyApp" |
REGIST_MENU 应用名称参数 |
helloworldpage/ |
myapp/ |
目录名、#include 路径 |
关键代码片段
AppViewIDs.h —— 分配唯一 ID
typedef enum : uint16_t {
...
VIEW_HELLOWORLD,
VIEW_MYAPP, // 添加在 VIEW_MAX_INTER_ARRY_APP 之前
...
VIEW_MAX_INTER_ARRY_APP = 0x7FFF,
} AppViewId;
MyAppPresenter.cpp —— 注册到应用列表
#include "NativeRegisterManager.h"
#include "myapp/MyAppView.h"
#include "myapp/MyAppPresenter.h"
namespace OHOS {
#ifdef SDK_DITING_COMMUNITY
REGIST_MENU(VIEW_MYAPP, MyAppView, MyAppPresenter, EMPTY_ICON, EMPTY_ICON, "MyApp");
#endif
}
MyAppPage.cpp 关键片段
以下为可直接参考的完整
OnStart页面启动,在此基础上替换文字或添加控件。
static constexpr uint16_t MYAPP_MAIN_PAGE = 1;
#ifdef SDK_DITING_COMMUNITY
REGIST_SLICE_PAGE(VIEW_MYAPP, MYAPP_MAIN_PAGE, MyAppPage, true);
#endif
void MyAppPage::OnStart(void *data)
{
bool ret = ImageCacheManager::GetInstance().LoadAllInMultiRes(EMPTY_IMAGE);
if (ret == false) {
WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "LoadEmptyImage fail");
return;
}
container_ = new UIViewGroup();
if (container_ == nullptr) { return; }
container_->SetPosition(0, 0, HORIZONTAL_RESOLUTION, VERTICAL_RESOLUTION);
container_->SetDraggable(true);
container_->SetTouchable(true);
label_ = new UILabel();
if (label_ == nullptr) { return; }
label_->SetPosition(100, 175, 200, 50);
label_->SetAlign(TEXT_ALIGNMENT_CENTER, TEXT_ALIGNMENT_CENTER);
label_->SetFont(DEFAULT_VECTOR_FONT_FILENAME, 28);
label_->SetText("My First App"); // 替换为你的内容
container_->Add(label_);
AddViewToPageContainer(container_); // 必须调用,否则页面为空
}
析构函数参照 HelloWorldPage::~HelloWorldPage() 原样实现,将 helloWorldLabel_ 替换为 label_ 即可。
验收标准
新 App 完成后,同样逐项验收(首次验证用完整构建;后续只改 Native 页面可用组件构建):
-
fbb build --clean通过 - 应用列表出现新应用名称
- 点击后页面显示正常
- 串口日志能看到
MyAppPage OnStart
注意事项
不要做什么:
- 不要修改
interim_binary/下的预编译库,这些是闭源组件。 - 不要复用已有的
VIEW_XXXID,每个应用必须有唯一 ID,重复会导致运行时冲突。 - 不要提交编译产物(
output/目录)到代码仓库。 - 普通 UI 示例不要修改 config.py,无需改动编译目标配置。
- 不要修改与自己应用无关的模块,保持 diff 最小化。
资源说明:
EMPTY_ICON 和 EMPTY_IMAGE 是开发者的应用资源:
- EMPTY_IMAGE(user/res/EMPTY.bin):页面加载的图片资源包。
- EMPTY_ICON(user/res/EMPTY_ICON.bin):应用列表图标,正式应用需替换为实际图标资源,否则应用列表显示空白图标。
常见错误
| 错误现象 | 原因 | 解决方法 |
|---|---|---|
VIEW_MYAPP undeclared identifier |
未在 AppViewIDs.h 中添加枚举项 |
在 VIEW_MAX_INTER_ARRY_APP 前添加 VIEW_MYAPP |
fatal error: myapp/MyAppPage.h: No such file |
include 路径写错,或头文件放错目录 | 头文件必须在 nativeui/include/myapp/ 下,不是 nativeui/myapp/ |
undefined reference to 'OHOS::MyAppPage::...' |
.cpp 文件没放在 nativeui/ 下,未被 GLOB_RECURSE 收集 |
确认文件路径为 nativeui/myapp/MyAppPage.cpp |
| 应用列表不出现新应用 | REGIST_MENU 的类名与实际类名不一致,或宏在错误的 #ifdef 分支内 |
核对宏参数中的 View/Presenter 类名,确认在 #ifdef SDK_DITING_COMMUNITY 内 |
| 点进去页面空白 | AddViewToPageContainer(container_) 未被调用,或 new 失败后提前 return |
检查 OnStart 中的 nullptr 判断和最后一行的 AddViewToPageContainer |
namespace OHOS |
类定义未在 OHOS 命名空间,实现文件未包裹 |
.cpp 文件的实现,代码需包在 namespace OHOS { } 中 |