UIKit 图形服务与调试
屏幕旋转
中间buffer方案
原理为将内容绘制中间buffer上,再将中间buffer旋转到fb并送显fb。修改方式如下:
-
打开文件“/src/middleware/services/gui/hal/common/include/graphic_hardware_config_wearable.h”将宏ROTATE_METHOD的值修改为0。
须知: 在宏ROTATE_METHOD的值为0时,宏HW_ROTATE_ANGLE的值不能修改为0,否则将无内容显示。
-
将宏HW_ROTATE_ANGLE修改为需要旋转的角度(1:顺时针90度,2:顺时针180度,3:顺时针270度)。
预旋转方案
原理为将绘制的内容加上旋转变换绘制到fb上,送显fb。修改方式如下:
- 打开文件“/src/middleware/services/gui/hal/common/include/graphic_hardware_config_wearable.h”将宏ROTATE_METHOD的值修改为1。
- 将宏HW_ROTATE_ANGLE修改为需要旋转的角度(0不旋转,1表示顺时针90度,2顺时针180度,3顺时针270度)。
DPU旋转方案
原理为直接绘制到DPU上,再由DPU旋转送显fb。修改方式如下:
- 打开文件“/src/middleware/services/gui/hal/common/include/graphic_hardware_config_wearable.h”将宏ROTATE_METHOD的值修改为2。
- 将宏HW_ROTATE_ANGLE修改为需要旋转的角度(0:不旋转,1:顺时针90度,2:顺时针180度,3:顺时针270度)。
Graphic Service
整体方案

Graphic service总体上分为两部分:
- 初始化graphic。
- 创建两个任务线程,分别完成主任务执行,及vblank(显示器扫描线完成最后一行后,需要重返左上角的过程,也叫VBI(Vertical Blank Interval)、AOD(Always On Display,常亮显示)、Screen等硬件信息监测。
通过InitGraphicService()调用,完成对gralloc、display HAL、input、TaskManager等的初始化,准备graphic执行环境。同时会启动两个线程。
- main task:只做一件事,执行event queue中的任务,若队列空则睡眠等待信号量。
- vsync thread:监测screen、AOD信息,若是灭屏或低功耗模式,图形将睡眠等待信号量;否则获取vblank信号,并以此为周期将TaskHandler推送到main task执行。
所有外部模块对图形的调用,均需通过PostGraphicEvent()接口推送到event queue,等待vblank周期执行。
对外接口
表 1 graphic service对外API
| 函数 | 说明 |
|---|---|
| void PostGraphicEvent(const OHOS::GraphicEvent& event) | 向图形service任务队列推送任务,任务将在后续的Vsync周期被执行。 注意:仅推送涉及UI属性变更或刷新的任务,切勿推送无关且耗时的处理,这会阻塞UI,导致显示卡顿。 |
| void NotifyScreenOn() | 设置screen flag为1(on)。 |
| void NotifyScreenOff() | 设置screen flag为0(off)。 |
说明: 若是C语言代码,通过graphic_service_wrapper中对应的接口调用。
表情包支持
方案介绍
不同于文字使用矢量字库绘制,表情包使用图片进行绘制,支持自定义表情包,表情包自定义流程如下:
- 制作表情包压缩数据(请参考“图片资源打包”)。
- 在头文件ui_emoji_table.cpp中定义unicode码点和表情包图片的映射关系。
- 导入表情包压缩数据并使用。
制作表情包数据库和表情包映射表
制作表情包数据库和表情包映射表流程如下:
-
将使用的表情包用Debugkits以package的方式进行压缩,并将名字保存为emoji.bin。

-
将表情包与码点的映射关系放到头文件“/src/middleware/services/gui/uikit/proprietary/src/ui/frameworks/common/ui_emoji_table.cpp”中去,同时通过变量“EMOJI_PATH”配置emoji.bin文件的位置。

-
将emoji.bin放入配置的路径中去。
绘制原理
普通的码点(unicode)首先通过查找字库,获取矢量数据进行绘制。对于找不到的码点会先在自定义的表情包数据库中寻找,如果找到,会提取表情包的图片数据,并进行绘制;如果在表情包映射表中也找不到,将会在配置的多语言字库中查找码点数据。
DFX
内存占用
-
dumpmem
打印UIKit占用内存,包含以下内存:
-
current malloc/new memory:
通过UIKit UIMalloc或new分配的内存块数量。
-
current gralloc allocate memory:
通过UIKit GrallocEngine分配的内存,包含图片、矢量、视频Buffer等内存。
注意,如果当前dumpmem数值在慢慢变大,一般说明出现内存泄漏,请参考“内存泄漏定位”;如果当前dumpmem数值在慢慢减小,说明出现内存重复释放,需要自行定位。
-
-
dumpimg
打印UIKit中图片资源缓存信息,以及相关控件所持有的图片资源信息。
说明: 在串口工具中发送指令,指令格式为:‘AT+GUI=UIKIT_DFX,’+具体命令,后续不再赘述。
帧率
-
printfps
- -h:打印帧率说明。
- duration n:n秒,持续打印帧率n秒[n次]。
仅输入printfps,则只打印一次帧率。
-
showfps
- -h:显示帧率说明。
- -p:指定显示起始位置。参数格式:x,y。
仅输入showfps,则默认显示在屏幕中间。
其坐标位置仅在第一次输入showfps时有效,后续再调用均无效。
-
hidefps
隐藏帧率显示。
帧率DFX示例如下:
/* 打印帧率 */
AT+GUI=UIKIT_DFX,printfps,10
/* 显示帧率 */
AT+GUI=UIKIT_DFX,showfps,-p,100,100
/* 隐藏帧率 */
AT+GUI=UIKIT_DFX,hidefps
截图
-
dump_view_id
串口输入此命令,将当前RootView下所有设置过viewId的子节点的viewId和坐标信息输出在串口中:[viewId]: x, y, w, h。
-
screencap
- -h:截图指南,无参。
- -v:控件截图,截取指定控件的数据。参数格式:viewId。
- -c:区域截图,截取指定区域的数据。参数格式:x,y,w,h。参数非负,且不能超过屏幕大小。
- -f:输出文件,截取数据保存至该输出文件。
说明:
- -f需配置为最后一个选项。如果-f前无其他选项,则判定为全屏截图,是从FB捞取的图片数据。
- -v是重新绘制后截取的图片数据。-c是从FB中捞取的图片数据。
- FB捞取的图片数据的压缩模式是COMPRESS_MODE_HFBC_2X,重绘截取的图片数据的压缩模式是COMPRESS_MODE_HFBC_3X。
- 截取的压缩图片需要解压才能查看,解压缩流程请参考《DebugKits工具使用指南》中的“图形压缩对比”章节。
- 视频截图仅能通过-v,需要先打印控件树获取视频控件ID,再配置ID通过-v截取视频图片数据。
- 文件保存的数据格式为RGB888。
- 文件像素宽度为16倍数对齐,即真实宽度为:

-
串口回显:
screencap result: 1.(表示执行成功)。
screencap result: 0.(表示执行失败)。
截图DFX示例如下:
/* 区域截图 */
AT+GUI=UIKIT_DFX,screencap,-c,0 0 100 100,-f,/bin/vs/sd0p0/rect.bin
/* 组件截图 */
dump_view_id
AT+GUI=UIKIT_DFX,screencap,-v,mainclickactivity,-f,/bin/vs/sd0p0/view.bin
/* 全屏截图 */
AT+GUI=UIKIT_DFX,screencap,-f,/bin/vs/sd0p0/screen.bin
模拟事件
- inject_rotate n
- 参数n:旋转刻度(有符号整形类型)。
-
示例:
AT+GUI=UIKIT_DFX,inject_rotate,10
- inject_key keyId state
- 参数keyId:键值(字符串类型),合法值[power,func]。
- 参数state:按键状态(无符号整形类型),合法值[0, 1, 2]。0代表松开,1代表按下,2代表长按。
-
示例:
AT+GUI=UIKIT_DFX,inject_key,power,1
- inject_click x y
- 参数x:X坐标(无符号整形类型)。
- 参数y:Y坐标(无符号整形类型)。
-
示例:
AT+GUI=UIKIT_DFX,inject_click,100,200
- inject_drag startX startY endX endY duration
- 参数startX:起始X坐标(无符号整形类型)。
- 参数startY:起始Y坐标(无符号整形类型)。
- 参数endX:结束X坐标(无符号整形类型)。
- 参数endY:结束Y坐标(无符号整形类型)。
- 参数duration:拖动时长ms(无符号整形类型)。
-
示例:
AT+GUI=UIKIT_DFX,inject_drag,200,400,200,100,30
- inject_lp x y
- 参数x:X坐标(无符号整形类型)。
- 参数y:Y坐标(无符号整形类型)。
-
示例:
AT+GUI=UIKIT_DFX,inject_lp,100,30
时延统计
-
showtime param
参数param:要统计时延的类型,可取值point、tp、render、close。
- point:打印UI一秒内尝试查询tp点的次数及相关信息。
- tp:实时打印tp报点。
- render:打印绘制耗时。
- close:关闭时延统计。
-
示例:
AT+GUI=UIKIT_DFX,showtime,render
-
showtime point
- showtime tp
-
showtime render count flush
- 含义:渲染过程中每一帧软硬件耗时、task耗时和event耗时。
-
示例
-
示例1:
AT+GUI=UIKIT_DFX,showtime,render,30,0
30:打印接下来30帧软硬件绘制指令。
0:第一帧不强制刷新。
-
示例2:
AT+GUI=UIKIT_DFX,showtime,render,30,1
30:打印接下来30帧软硬件绘制指令。
1:第一帧强制刷新。
-
打印结果(截取部分):
Total: E 11, T 50, F 10 E 0: 0, 81 E 1: 13, 16330 T 0: 17, 422 T 1: 7, 36 T 2: 6, 103 T 3: 5, 17 T 4: 5, 15699 isNativeRunning 1, freq 60 Cmd count: 9 time: 15037, {0 0 465 465} 0. hw_rect: 575, 187, {0, 0, 465, 465} 1. hw_blit: 766, 252, 1 2. hw_path: 1187, 857, 11 3. hw_blit: 1171, 239, 1 4. hw_blit: 716, 422, 2 5. hw_blit: 1063, 771, 6 6. hw_wait: 29, 95 7. hw_flush: 48, 7122, {0, 0, 465, 465}, s 8. hw_submit: 47, 65, 0表 1 showtime render打印结果说明
格式
含义
备注
Total: E 11, T 50, F 10
- E 11:本次图形线程记录共11个event;
- T 50:本次图形线程记录共50个task;
- F 10:本次图形线程成功送显10帧。
图形线程执行流程:while循环处理event队列,event分为内部/外部两种类型,内部event进一步拆分为多个task(最后三个task固定为输入/动效/渲染送显)。
E 0: 0, 81
- E 0:第0号event;
- 0:距离上个event结束的间隔时间(首个event为0μs);
- 81:本event耗时81μs。
该event无task,属于外部event。
E 1: 13, 16330
T 0: 17, 422
T 1: 7, 36
T 2: 6, 103
T 3: 5, 17
T 4: 5, 15699
isNativeRunning 1, freq 60
Cmd count: 9 time: 15037, {0 0 465 465}
0. hw_rect: 575, 187, {0, 0, 465, 465}
...
- E 1: 13, 16330
13:距离上个event结束13μs后开始;
16330:本event总耗时16330μs。
- T 0: 17, 422
T 0:属于event 1的第0号task;
17:距离上个task开始间隔17μs(首个task表示event开始到task开始的间隔);
422:本task耗时422μs。
- T 4: 5, 15699
5:距离上个task间隔5μs;
15699:送显task总耗时15699μs。送显task可进一步拆解为指令级耗时(见下面内容)。
-
T 4: 5, 15699
isNativeRunning 1, freq 60
Cmd count: 9 time: 15037, {0 0 465 465}
0. hw_rect: 575, 187, {0, 0, 465, 465}
1. hw_blit: 766, 252, 1
2. hw_path: 1187, 857, 11
3. hw_blit: 1171, 239, 1
4. hw_blit: 716, 422, 2
5. hw_blit: 1063, 771, 6
6. hw_wait: 29, 95
7. hw_flush: 48, 7122, {0, 0, 465, 465}, s
8. hw_submit: 47, 65, 0
- isNativeRunning 1, freq 60”:
isNativeRunning 1:表示本次送显是在图形线程送显,如果为0表示在非图形线程送显(例如JS线程);
freq 60:当前送显最高帧率为60(实际帧率小于等于60)。
- Cmd count: 9 time: 15037, {0 0 465 465}:
Cmd count: 9:本次送显包含9条指令;
time: 15037:送显统计开始到结束总共耗时15037μs;
{0 0 465 465}:送显区域,格式{left, top, right, down}。
- 0. hw_rect: 575, 187, {0, 0, 465, 465}:
0. hw_rect:绘制指令hw_rect的序号为0,其中hw_rect表示绘制一个矩形,详细解释见下面内容。
575:上一次指令结束到本次指令开始之间的间隔为575μs,此处为第一次绘制,表示绘制统计开始到第一次指令之间的时间间隔;
187:本次指令提交耗时为187μs;
{0, 0, 465, 465}:矩形绘制区域,格式{left, top, right, down}。
指令格式规范:
<序号>. <sw/hw>_<指令类型>:<间隔时间>,<执行耗时>,<附加信息>[, <属性标记>]
字段说明:
- sw/hw前缀
- sw:软件实现的指令(如 sw_rect表示软件绘制矩形);
- hw:硬件加速的指令(如hw_rect表示硬件绘制矩形)。
- 附加信息(根据指令类型变化):
- 默认携带绘制区域(格式:{left, top, right, bottom});
- 特殊指令携带:
hw_blit:图片搬移次数(整数);
hw_path:矢量路径数量(整数);
hw_flush:送显缓冲区类型(s=SRAM/p=PSRAM);
hw_submit:提交方式(0:异步;1:同步)。
- 属性标记(可选):
- fill:填充区域;
- stroke:描边区域;
- both:同时包含填充和描边;
- s/p:标识使用的显存类型。
示例说明:"3. hw_rect: 100,200,{0,0,480,800},fill"
表示:第3条指令,硬件绘制矩形,距上一条指令间隔100μs,执行耗时200μs,绘制区域为(0,0)-(480,800),包含填充属性。
sw_tran
软件绘制图片变换,携带信息为区域。
-
sw_blit
软件图片搬移,携带信息为区域。
-
hw_blit
硬件图片搬移,携带信息为搬移图片数量。
-
hw_path
硬件矢量绘制,携带信息为矢量路径数量。
由于一次绘制中,矢量绘制的次数会很多,因此将矢量绘制的信息压缩。例如,“hw_path: 1187, 857, 11”,表示总间隔时间为1187μs,硬件总提交指令耗时为857μs,总共11条指令。
普遍场景下,文字、地图上的路径都是由矢量绘制的。
hw_clip
硬件矢量路径截图,携带信息为区域。
-
sw_bezier
软件贝塞尔曲线,不携带信息。
-
sw_rect
软件矩形绘制,携带信息为区域。
-
hw_rect
硬件矩形绘制,携带信息为区域,此外有可能额外包含路径的属性:fill、stroke、both。
-
hw_blur
硬件高斯模糊绘制,携带信息为区域。
-
sw_letter
软件绘制文字,携带信息为文字区域。
-
sw_arc
软件绘制圆弧,携带信息为区域。
-
sw_line
软件绘制直线,携带信息为区域。
-
hw_submit
硬件指令提交,携带信息为异步和同步,0:异步提交;1:同步提交。
-
hw_flush
硬件送显,携带信息为送显区域,显存类型包括sram和psram,s:sram;p:psram。
-
hw_wait
硬件等待上一次提交指令绘制完成,无携带信息。
-
hw_arc_rect
硬件圆角矩形绘制,携带信息为区域,此外有可能额外包含路径的属性:fill、stroke、both。
-
hw_line
硬件绘制直线,携带信息为区域,此外有可能额外包含路径的属性:fill、stroke、both。
-
hw_arc
硬件绘制圆弧,携带信息为区域,此外有可能额外包含路径的属性:fill、stroke、both。
-
hw_bezier
硬件贝塞尔曲线,携带信息为区域,此外有可能额外包含路径的属性:fill、stroke、both。
-
hw_ellipse
硬件绘制椭圆,携带信息为区域,此外有可能额外包含路径的属性:fill、stroke、both。
-
hw_light
硬件绘制光照动效,携带信息为区域。
-
hw_3d_cylinder
硬件绘制圆柱动效,携带信息为区域。
-
hw_3d_mesh
硬件绘制方格动效,携带信息为区域。
-
hw_3d_sphere
硬件绘制球体效果,携带信息为区域。
-
hw_rain
硬件绘制雨滴效果,携带信息为区域。
-
控件树打印
-
dump_root_view
以json格式打印当前整个控件树,打印信息会输出到屏幕上,同时会将信息保存到“/user/res/view_tree.json”。
-
示例:
AT+GUI=UIKIT_DFX,dump_root_view
-
内存泄漏定位
-
leak_panic cnt size
该条指令需要结合代码分析,在存在内存泄漏的代码区间(例如某个类的构造和析构之间)两端分别调用如下代码:
MemCheck::GetInstance()->EnableLeakCheck(true); // 检测开始 // some code MemCheck::GetInstance()->EnableLeakCheck(false); // 检测结束结果会输出这个区间内没有释放的内存,包含信息为“申请次序+大小”,多次运行这段代码,内存泄漏会在某次固定存在,在下一次运行这段代码前发出指令“AT+GUI=UIKIT_DFX,leak_panic,cnt,size”。其中cnt表示第几次,size表示大小,该指令会在第cnt次申请内存时主动崩溃,并输出调用栈信息,通过调用栈可以定位内存泄漏位置。
-
示例:
AT+GUI=UIKIT_DFX,leak_panic,16,24
-
Native状态打印
-
dumpstate
打印Native UI的运行状态。打印的信息包含:
- NativeRunning:应用运行状态,用于区分Native和JS的状态。1代表Native正在运行中,0代表JS正在运行中。
- IsScreenOn:屏幕量灭屏状态。1代表处于亮屏状态,0代表处于灭屏状态。
- GraphicTaskTriggeredCount:已触发的Native任务个数。
- LowPower:UI低功耗相关信息。
-
示例:
AT+GUI=UIKIT_DFX,dumpstate
术语表
A |
||
AOD |
Always On Display |
常亮显示 |
D |
||
DFX |
Design for eXcellence |
产品非功能性属性设计 |
E |
||
eMMC |
Embedded Multimedia Controller |
嵌入式多媒体控制器 |
G |
||
GUI |
Graphical User Interface |
图形用户界面 |
H |
||
HAL |
Hardware Abstraction Layer |
硬件抽象层 |
HSV |
Hue, Saturation, Value |
色度、彩度、亮度 |
I |
||
ICU |
||
IoT |
Internet of Things |
物联网 |
L |
||
LTR |
Left To Right |
从左向右 |
O |
||
OS |
Operating System |
操作系统 |
P |
||
POI |
||
PSRAM |
Pseudostatic Random Access Memory |
假静态随机存储器 |
R |
||
RTL |
Right To Left |
从右向左 |
S |
||
SVG |
Scalable Vector Graphics |
可缩放矢量图形 |
U |
||
UI |
User Interface |
用户界面 |
V |
||
VBI |
Vertical Blanking Interval |
垂直消隐区 |
X |
||
XML |
Extensible Markup Language |
可扩展标记语言 |
注意事项
- UIKit 的组件树具有所有权关系。页面创建的容器、标签、图片和资源应在页面退出或析构时按创建的反向顺序释放;图片资源必须配对调用加载和卸载接口。
- 使用
UIViewGroup::Add()后,子视图由容器参与显示和事件分发;仅创建组件而未调用AddViewToPageContainer()不会显示页面内容。 - 动态修改文本、位置、可见性或样式后,按控件行为调用
Invalidate()或对应组件刷新接口,避免依赖下一帧偶然重绘。 - 新建应用必须使用未占用的
VIEW_*标识和页 ID。重复注册会造成菜单入口异常、页面跳转错乱或链接阶段的重复符号错误。 HelloWorld的菜单和页面注册受SDK_DITING_COMMUNITY条件保护。移植到其他目标时,应先确认对应目标启用了 UIKit、OpenHarmony Native 框架和所需应用组件。- 组件详细说明中的资源、表盘、旋转、Graphic Service 和 DFX 特性可能依赖特定硬件、资源包或产品配置;使用前先核对各章节的前置条件和 SDK 源码中的编译条件。
常见编译错误
| 现象 | 原因与处理 |
|---|---|
找不到 components/ui_label.h、SlicePage.h |
将页面放入现有 /src/application/wearable/nativeapp/nativeui 与 nativeui/include 结构,复用 nativelauncher 现有的 UIKit 头文件配置;不要在外部组件中猜测私有头文件路径。 |
| 固件中没有新增菜单 | 检查 REGIST_MENU 是否参与编译、VIEW_* 是否已加入 AppViewIDs.h,以及是否被 SDK_DITING_COMMUNITY 或其他条件编译屏蔽。 |
| 进入页面后黑屏或没有控件 | 检查根容器位置和尺寸、container->Add()、AddViewToPageContainer()、字体与图片资源加载是否全部成功。 |
| 页面切换后内存增长 | 检查容器是否 RemoveAll()、通过 new 创建的控件是否释放、LoadAllInMultiRes() 是否有对应的 UnloadAllInMultiRes()。可参考本文 DFX 章节中的内存泄漏定位方法。 |
| 修改页面源码后未生效 | nativelauncher 通过递归规则收集 nativeui/*.cpp;新增文件或编译配置变化后,按 CLI 指南重新配置并构建,避免复用过期构建目录。 |