3.1 Release 弹窗弹出失效问题分析报告
1 关键字
CustomDialogController;Dialog;
2 问题描述
问题现象:在Launcher桌面中,拖动应用图标后,长按应用图标,点击卸载菜单弹出确认按钮,并没有弹出卸载对话框。
api版本:3.1 Release
硬件环境:rk3568
3 问题原因
3.1 正常机制
TS调用 CustomDialogController 中的 open 接口,能正常的弹出 Dialog 对话框。
3.2 异常机制
Launcher桌面的对话框在弹起后,由于某种异常操作,触发的对话框关闭的操作,调用了 close 方法,CustomDialogController 调用 close 接口后,通过 NAPI 机制调用 js_custom_dialog_controller.cpp 文件中的 JsCloseDialog 方法,JsCloseDialog 方法内部调用 CloseDialog 方法,方法内部将 pending 设置为 true。CloseDialog 方法内部执行 executor.PostTask,task 内部 进入if (!lastStack || !dialogComponent) 分支内部,方法 return, 导致 pending 状态没有复原。
void JSCustomDialogController::CloseDialog()
{
......
pending_ = true;
auto task = [lastStack, dialogComponent = dialogComponent_, this]() {
if (!lastStack || !dialogComponent) {
return;
}
......
};
......
}
在后续的操作中,点击卸载菜单弹出确认按钮,CustomDialogController 调用 open 接口,通过 NAPI 机制调用 js_custom_dialog_controller.cpp 文件中的 JsOpenDialog 方法,JsOpenDialog 方法内部调用 ShowDialog 方法,ShowDialog 方法在202行 判断 pending 状态,pending 为 true,执行逻辑进入 ShowDialog 内部,ShowDialog 方法 内部 return ,导致后续弹出 Dialog 对话框的代码没有调用。
void JSCustomDialogController::ShowDialog()
{
LOGI("JSCustomDialogController(ShowDialog)");
......
if (pending_) {
dialogOperation_.emplace_back(DialogOperation::DIALOG_OPEN);
return;
}
......
}
4 解决方案
应用层解决方案
每次弹出对话框时,重新构造 CustomDialogController 对象。(治标不治本,不建议)
let customDialogController = null
function showDialog() {
customDialogController = new CustomDialogController();
customDialogController.open()
}
Native解决方案
在 ShowDialog 和 CloseDialog 方法中,分别添加方法异常后的 pending 状态复原处理。
-
ShowDialog
void JSCustomDialogController::ShowDialog()
{
LOGI("JSCustomDialogController(ShowDialog)");
......
auto stack = context->GetLastStack();
auto result = false;
if (stack) {
LOGE("JSCustomDialogController(PostTask) stack");
result = executor->PostTask(task, TaskExecutor::TaskType::UI);
} else {
LOGE("JSCustomDialogController(PostDelayedTask) !!!!stack");
result = executor->PostDelayedTask(task, TaskExecutor::TaskType::UI, DELAY_TIME_FOR_STACK);
}
if (!result) {
LOGW("JSCustomDialogController(ShowDialog) fail to post task, reset pending status");
pending_ = false;
}
}
-
CloseDialog
void JSCustomDialogController::CloseDialog()
{
LOGI("JSCustomDialogController(CloseDialog)");
......
pending_ = true;
auto task = [lastStack, dialogComponent = dialogComponent_, this]() {
if (!lastStack || !dialogComponent) {
LOGI("JSCustomDialogController(CloseDialog) stack or dialog is null.");
this->NotifyDialogOperation(DialogOperation::DIALOG_CLOSE);
return;
}
......
};
auto result = executor->PostTask(task, TaskExecutor::TaskType::UI);
if (!result) {
LOGW("JSCustomDialogController(CloseDialog) fail to post task, reset pending status");
pending_ = false;
}
}
源文件路径: foundation\ace\ace_engine\frameworks\bridge\declarative_frontend\jsview\dialog\js_custom_dialog_controller.cpp
5 定位过程
在点击打开卸载弹窗后,弹窗没有弹起,查看系统日志。
# C:\Users\xxx>hdc_std shell # hilog | grep JSCustomDialogController 03900/Ace: [js_custom_dialog_controller.cpp(ShowDialog)-(1000000)] JSCustomDialogController(ShowDialog)
通过日志发现,'ShowDialog' 已经打印,说明 js_custom_dialog_controller.cpp 内部调用了 ShowDialog 方法,但却没有打开弹窗的日志,结合代码,猜测应该是执行到 ShowDialog 方法内部的某一个 return 的分支。为没有添加日志的 return 分支添加log日志,再次查看系统日志。ShowDialog 方法执行到 pending 为true的分支。
# C:\Users\xxx>hdc_std shell # hilog | grep JSCustomDialogController 03900/Ace: [js_custom_dialog_controller.cpp(ShowDialog)-(1000000)] JSCustomDialogController(ShowDialog) 03900/Ace: [js_custom_dialog_controller.cpp(ShowDialog)-(1000000)] JSCustomDialogController(ShowDialog) current state is pending.
查看 ShowDialog 和 CloseDialog 方法内部代码,在设置pending 为 true 后,存在部分return 但是没有复原pending 状态的异常分支,在异常分支添加log 后,再次复现问题,添加的log 正常打印,问题得到定位。
6 知识分享
CustomDialogController 是 Openharmony ArkUi 中提供的全局基础弹窗,可以基于该自定义弹窗来实现提示消息弹窗、确认弹窗、输入弹窗的UI组件封装。
更多推荐


所有评论(0)