HarmonyOS鸿蒙Next中之前定义了一个全局GlobalDialog,但是重复的弹窗一直关闭不了

HarmonyOS鸿蒙Next中之前定义了一个全局GlobalDialog,但是重复的弹窗一直关闭不了 项目中自定义了一全局弹窗,用来显示登录状态验证,但是在多个页面切换的时候,没有登录的情况,会重复弹窗,代码也加了弹窗加载重复验证队列,问题就是重复后显示的最后一个弹窗关闭不了,前面的都能关闭

cke_4530.jpeg

关闭代码如下:

/**
   * 关闭当前弹窗
   * @param context UI上下文
   */
  static async close(context: UIContext) {
    if (!GlobalDialog.isDialogOpen || !GlobalDialog.contentNode) {
      // 重置状态确保队列能正常处理
      GlobalDialog.isDialogOpen = false;
      return;
    }
    
    try {
      const ctx = getContext()
      const win = await window.getLastWindow(ctx)

      win.setWindowLayoutFullScreen(true).then(() => {
        console.info('Succeeded in setting the window layout to full-screen mode.');
        win.setWindowSystemBarProperties({
          navigationBarColor: '#00428E',
          isStatusBarLightIcon: true,
          isNavigationBarLightIcon: true,
          navigationBarContentColor: '#ffffff',
          statusBarContentColor: '#ffffff',
          statusBarColor: '#00428E'
        })
        const promptAction = context.getPromptAction();
        promptAction.closeCustomDialog(GlobalDialog.contentNode);
        GlobalDialog.contentNode = null
        GlobalDialog.isDialogOpen = false
        
        // 尝试处理队列中的下一个弹窗
        if (GlobalDialog.dialogQueue.length > 0) {
          // 给一点延迟确保当前弹窗完全关闭
          setTimeout(() => {
            const ctx = getContext();
    
          }, 100);
        }
      }).catch((err: BusinessError) => {
        console.error('Failed to set the window layout to full-screen mode. Cause:' + JSON.stringify(err));
        // 出错也要重置状态
        GlobalDialog.isDialogOpen = false;
      });
    } catch (error) {
      console.error('关闭弹窗失败:', error);
      // 出错也要重置状态
      GlobalDialog.isDialogOpen = false;
    }
  }
}

更多关于HarmonyOS鸿蒙Next中之前定义了一个全局GlobalDialog,但是重复的弹窗一直关闭不了的实战教程也可以访问 https://www.itying.com/category-93-b0.html

3 回复

1、问题出在

if (!GlobalDialog.isDialogOpen || !GlobalDialog.contentNode) {
      // 重置状态确保队列能正常处理
      GlobalDialog.isDialogOpen = false;
      return;
    }

2、替换成下面的代码试试:

/**
   * 关闭当前弹窗
   * @param context UI上下文
   */
  static async close(context: UIContext) {
    // 修复:总是尝试关闭弹窗,不管状态如何
    try {
      const ctx = getContext()
      const win = await window.getLastWindow(ctx)

      win.setWindowLayoutFullScreen(true).then(() => {
        console.info('Succeeded in setting the window layout to full-screen mode.');
        win.setWindowSystemBarProperties({
          navigationBarColor: '#00428E',
          isStatusBarLightIcon: true,
          isNavigationBarLightIcon: true,
          navigationBarContentColor: '#ffffff',
          statusBarContentColor: '#ffffff',
          statusBarColor: '#00428E'
        })
        const promptAction = context.getPromptAction();
        // 总是尝试关闭弹窗,即使contentNode为null
        if (GlobalDialog.contentNode) {
          promptAction.closeCustomDialog(GlobalDialog.contentNode);
          GlobalDialog.contentNode = null;
        }
        // 确保状态被重置
        GlobalDialog.isDialogOpen = false;
        
        // 尝试处理队列中的下一个弹窗
        if (GlobalDialog.dialogQueue.length > 0) {
          // 给一点延迟确保当前弹窗完全关闭
          setTimeout(() => {
            // 获取当前上下文
            const ctx = getContext();
            // 注意:这里可能需要传递正确的上下文
          }, 100);
        }
      }).catch((err: BusinessError) => {
        console.error('Failed to set the window layout to full-screen mode. Cause:' + JSON.stringify(err));
        // 出错也要重置状态
        GlobalDialog.isDialogOpen = false;
        // 出错时也清除contentNode引用
        GlobalDialog.contentNode = null;
      });
    } catch (error) {
      console.error('关闭弹窗失败:', error);
      // 出错也要重置状态
      GlobalDialog.isDialogOpen = false;
      // 出错时也清除contentNode引用
      GlobalDialog.contentNode = null;
    }
  }
}

更多关于HarmonyOS鸿蒙Next中之前定义了一个全局GlobalDialog,但是重复的弹窗一直关闭不了的实战系列教程也可以访问 https://www.itying.com/category-93-b0.html


在HarmonyOS Next中,GlobalDialog重复弹窗无法关闭通常是由于生命周期管理问题导致。请检查是否在每次显示弹窗前未正确销毁前一个实例,或存在多个全局实例同时被引用。确保使用单例模式管理GlobalDialog,并在关闭时调用dismiss()方法并置空引用。同时,检查ArkTS/ETS代码中是否有条件判断逻辑错误,导致弹窗被重复触发。

问题出在您的 close 方法逻辑上。当多个页面快速切换并触发弹窗时,队列中的弹窗实例(contentNode)和全局状态 isDialogOpen 在异步操作中发生了竞争条件。

核心问题分析:

  1. 状态管理竞争close 方法开始时检查 isDialogOpencontentNode,如果为假则直接返回并重置 isDialogOpen。但在后续的异步 setWindowLayoutFullScreen 操作中,才真正关闭弹窗并清空 contentNode。如果在此期间,另一个弹窗请求被加入队列并开始执行,它可能会看到 isDialogOpen 已被重置为 false,从而创建新的弹窗实例,覆盖了即将被关闭的 contentNode。导致 promptAction.closeCustomDialog(GlobalDialog.contentNode) 关闭的是错误(或已失效)的节点。

  2. 队列处理逻辑不完整setTimeout 中的队列处理代码是空的,没有实际调用显示下一个弹窗的方法。

解决方案:

修改您的 close 方法,确保状态变更和实际关闭操作的原子性,并完善队列处理。

static async close(context: UIContext) {
    // 1. 立即锁定并获取当前要关闭的节点
    const nodeToClose = GlobalDialog.contentNode;
    if (!nodeToClose) {
        GlobalDialog.isDialogOpen = false;
        return;
    }

    // 2. 立即清空全局状态,防止新的弹窗创建干扰
    GlobalDialog.contentNode = null;
    GlobalDialog.isDialogOpen = false;

    try {
        const ctx = getContext()
        const win = await window.getLastWindow(ctx)
        await win.setWindowLayoutFullScreen(true);
        console.info('Succeeded in setting the window layout to full-screen mode.');
        // ... (您的setWindowSystemBarProperties代码可以保留)

        const promptAction = context.getPromptAction();
        // 3. 使用之前保存的节点引用来关闭
        await promptAction.closeCustomDialog(nodeToClose);

        // 4. 处理队列中的下一个弹窗
        if (GlobalDialog.dialogQueue.length > 0) {
            // 短暂延迟确保UI更新完成
            setTimeout(() => {
                // 这里应该调用您显示弹窗的方法,例如 GlobalDialog.showNextInQueue(context)
                // 假设您有一个这样的方法,它从 dialogQueue 中取出下一个任务并显示
                // GlobalDialog.showNextInQueue(context);
            }, 50); // 延迟可以更短
        }
    } catch (error) {
        console.error('关闭弹窗失败:', error);
        // 错误发生时状态已重置,无需重复操作
    }
}

关键修改点:

  • 原子性操作:在尝试任何异步操作之前,先保存当前的 contentNode 到局部变量 nodeToClose,并立即清空全局的 contentNodeisDialogOpen。这确保了即使有新的弹窗请求,也不会干扰当前正在进行的关闭流程。
  • 使用局部引用:使用局部变量 nodeToClose 来执行关闭操作,避免在异步过程中因全局变量被修改而关闭错误的对象。
  • 完善队列处理:在 setTimeout 中,需要调用实际处理队列中下一个弹窗显示请求的方法。

额外建议: 确保您的弹窗显示方法(如 show)也有类似的队列管理和状态检查逻辑,防止同时创建多个弹窗实例。通常,在显示新弹窗前,如果 isDialogOpentrue,则应将请求参数放入 dialogQueue 等待,而不是直接创建新弹窗。

回到顶部