动态应用¶
FAQ1 动态应用如何添加开机初始化¶
调用初始化注册宏¶
动态应用如果存在必须在gui_thread_entry运行前完成的初始化逻辑,可参照reader_bookshelf.c中reader_init的调用方式处理。该方式主要用于开机初始化,例如提前完成开机动画资源配置、图片解码器初始化等准备工作。
static void reader_init(void)
{
app_pm_anim_set_resource((anim_img_t *)pm_anim_fn, sizeof(pm_anim_fn) / sizeof(anim_img_t));
epbub_decoder_img_init();
LOG_I("%s", __func__);
}
/* It must be initialized before the gui_thread_entry runs. */
#ifdef BSP_USING_PC_SIMULATOR
INIT_PRE_APP_EXPORT(reader_init);
#else
DLMODULE_INIT_DEF(reader_init);
#endif
PC模拟器场景:使用
INIT_PRE_APP_EXPORT(reader_init),随主工程开机初始化流程执行。板级动态模块场景:使用
DLMODULE_INIT_DEF(reader_init),在动态模块加载初始化阶段执行。
FAQ2 动态应用如何判断是否会被编译装入¶
配置readme.ini依赖规则¶
Solution通过Butterfli工具编译动态应用,工具会根据当前板级工程HCPU的.config配置判断是否装入动态应用:
动态应用根目录存在
readme.ini时,Butterfli会读取其中定义的分辨率和依赖宏,与.config中的配置进行匹配,匹配成功才装入。动态应用根目录不存在
readme.ini时,默认认为该动态应用与当前配置兼容,直接装入。
readme.ini中常用配置项如下:
配置项 |
说明 |
|---|---|
|
定义支持的分辨率, |
|
通用依赖宏,板级和模拟器共享,例如 |
|
板级专用依赖宏,例如硬件相关宏 |
|
模拟器专用依赖宏,无依赖时设置 |
更完整说明可参考:动态应用指南
FAQ3 动态应用的数据如何保存¶
使用动态应用NVM接口¶
动态应用的数据应通过动态应用NVM接口读写,数据会与应用绑定;删除应用时,框架会自动清理关联数据。
size_t app_nvm_read(const char* key_name, const void* data, size_t length);
rt_err_t app_nvm_write(const char* key_name, const void* data, size_t length);
rt_err_t app_nvm_del(const char* key_name);
注意:
key_name必须和动态应用的ID保持一致,包括动态app、wf、aod,否则可能导致数据无法按预期读写或清理。
FAQ4 动态应用的注册ID、目录名和资源名有什么要求¶
保持ID和资源规则一致¶
动态应用需要保证注册ID、动态应用目录名、_MODULE_NAME_宏定义保持一致。动态应用在模拟器调试、内置编译以及文件系统分目录访问资源时都会依赖该名称。
#define _MODULE_NAME_ "dyn_plane"
#include "app_module.h"
APPLICATION_REGISTER(app_get_strid(key_plane, "Plane"), NULL, "dyn_plane", sizeof(plane_play_t))
常见规则如下:
动态应用注册ID必须和动态应用目录、
_MODULE_NAME_一致。动态
app、aod、wf缩略图名称必须固定为tn.png。动态应用多语言接口
app_get_str和app_get_str_from_id只会访问动态应用目录下resource/lang/multi_language_table.xlsx生成的词条。
FAQ5 动态应用无法显示如何排查¶
按日志和文件完整性排查¶
动态应用无法显示时,可按照以下步骤排查,原始说明可参考:动态应用指南-无法显示问题。
搜索日志中关键字
module,查看是否存在can't find ... in kernel symbol table的记录。若存在,说明有接口未在主代码中导出,需在
app_rtm_export.c中对应声明导出。例如日志提示动态应用找不到
custom_ring_play:can't find custom_ring_play in kernel symbol table则需要在主代码的
solution/framework/gui_fwk/dyn_fwk/export/app_rtm_export.c中导出该接口。若已有对应头文件,先包含头文件,再增加RTM_EXPORT:#include "ring.h" RTM_EXPORT(custom_ring_play);
如果没有合适的头文件,也可以先声明函数原型,再导出:
void custom_ring_play(const char *file_path); RTM_EXPORT(custom_ring_play);
修改后需要重新编译主工程,使该符号进入主代码的动态模块符号表。
若未找到
can't find相关日志,则需检查:应用文件是否完整,
installer目录是否包含xxx_tn.bin、xxx.so、xxx_res.so、xxx.desc。文件存放目录是否正确,例如应放在
root分区,却误放到/dyn分区等。
FAQ6 动态应用发生死机如何分析¶
加载动态应用符号表分析调用栈¶
动态应用发生死机时,由于动态加载代码的执行地址经过重定位,使用Trace32恢复现场时需要额外加载动态应用符号表,才能看到完整调用栈。原始说明可参考:动态应用指南-死机分析。
处理流程如下:
获取模块地址(两种方式)
可从串口日志中搜索
open module关键字,最后一个关键字后的地址就是模块地址。固件中也会保存最后打开的模块地址,例如
cur_app_module、cur_wf_module、cur_aod_module。
获取入口地址。
将模块地址转换为
struct rt_dlmodule *类型,读取其中的entry_addr:((struct rt_dlmodule *) cur_app_module)->entry_addr((struct rt_dlmodule *) cur_wf_module)->entry_addr((struct rt_dlmodule *) cur_aod_module)->entry_addr
如这里取出的地址是:
0x6036dc38
加载符号表。
Butterfli编译完成后,会在文件系统路径中生成
zip_output_symbolbak目录,其中包含xxx.so.nostrip符号表文件。使用Trace32命令按入口地址加载,例如:
D.Load sport.so.nostrip 0x6036dc38 /nocode /noclear。
加载符号表后,即可根据完整调用栈定位动态应用中触发异常的具体函数和代码位置。