在HarmonyOS NEXT开发中全局弹窗demo?

在HarmonyOS NEXT开发中全局弹窗demo?手机号互踢需求。在A手机登录手机号之后,在B手机登录相同的手机号时,需要在A手机上弹出对话框,告知用户手机号已在别的设备登录,此设备已退出登录。需要A手机在应用的任何页面都可以弹窗此对话框。

阅读 781
avatarAI BotBETA

以下是在HarmonyOS NEXT中实现全局互踢弹窗的解决方案:

// 1. 创建全局弹窗管理器
class GlobalDialogManager {
  private static instance: GlobalDialogManager;
  private currentContext: common.UIAbilityContext | null = null;

  static getInstance() {
    if (!GlobalDialogManager.instance) {
      GlobalDialogManager.instance = new GlobalDialogManager();
    }
    return GlobalDialogManager.instance;
  }

  // 注册Ability上下文
  registerContext(context: common.UIAbilityContext) {
    this.currentContext = context;
  }

  // 显示全局弹窗
  showKickOutDialog() {
    if (!this.currentContext) return;

    AlertDialog.show({
      title: '账号异地登录',
      message: '您的账号已在其他设备登录,当前设备已退出登录',
      confirm: {
        value: '确定',
        action: () => {
          // 执行退出登录操作
          this.handleLogout();
        }
      },
      cancel: () => {} // 可选取消按钮
    });
  }

  private handleLogout() {
    // 清理登录状态等操作
    // ...
    // 跳转到登录页面
    this.currentContext?.startAbility({
      bundleName: 'com.example.app',
      abilityName: 'LoginAbility'
    });
  }
}

// 2. 在EntryAbility中初始化
export default class EntryAbility extends UIAbility {
  onCreate(want: Want, launchParam: AbilityConstant.LaunchParam) {
    // 注册全局上下文
    GlobalDialogManager.getInstance().registerContext(this.context);
    
    // 订阅全局事件(示例使用Emitter)
    emitter.on("ACCOUNT_KICKED_OUT", () => {
      GlobalDialogManager.getInstance().showKickOutDialog();
    });
  }
}

// 3. 在任意页面触发弹窗(示例:当收到推送时)
function onPushReceive(data: string) {
  if (data.includes('account_kicked')) {
    emitter.emit("ACCOUNT_KICKED_OUT");
  }
}

// 4. 在页面中使用(示例:HomePage.ets)
@Entry
@Component
struct HomePage {
  build() {
    Column() {
      // 页面内容...
    }
  }
}

关键实现点:

  1. 单例模式管理全局上下文
  2. 通过UIAbility注册全局上下文
  3. 使用Emitter实现跨页面事件通信
  4. AlertDialog的全局调用
  5. 登录状态清理和页面跳转处理

注意事项:

  • 需要在前台显示弹窗时,可结合@Watch监听状态变化
  • 弹窗内容需遵循HarmonyOS设计规范
  • 需要处理多线程安全问题
  • 在manifest.json中申请必要的权限:

    "abilities": [
    {
      "name": "EntryAbility",
      "startWindowIcon": "$media:icon",
      "startWindowBackground": "$color:white",
      "visible": true
    }
    ]

补充说明:实际开发中需要结合账号体系实现以下功能:

  1. 登录状态持久化存储
  2. WebSocket/Push消息监听
  3. 心跳机制保持连接
  4. 服务端踢人接口实现
  5. Token失效处理机制
1 个回答

可以参考以下demo:

index.ets 
import { GlobalContext } from './GlobalContext'; 
import { testPromptDialog } from './HttpUtil'; 
import { promptAction } from '@kit.ArkUI'; 
 
@Entry 
@Component 
struct Index { 
  aboutToAppear(): void { 
    GlobalContext.getContext().setObject('UIContext', this) 
  } 
  build() { 
    Row() { 
      Column() { 
        Button("promptAction弹窗") 
          .onClick(() => { 
            testPromptDialog() 
          }) 
      } 
      .width('100%') 
    } 
    .height('100%') 
  } 
} 
GlobalContext.ets 
 
export class GlobalContext { 
  private constructor() { 
  } 
 
  private static instance: GlobalContext; 
  private _objects = new Map<string, Object>(); 
  public static getContext(): GlobalContext { 
    if (!GlobalContext.instance) { 
      GlobalContext.instance = new GlobalContext(); 
    } 
    return GlobalContext.instance; 
  } 
  getObject(value: string): Object | undefined { 
    return this._objects.get(value); 
  } 
  setObject(key: string, objectClass: Object): void { 
    this._objects.set(key, objectClass); 
  } 
} 
HttpUtil.ets 
 
 
 
let customDialogId: number = 0 
@Builder 
export function customDialogBuilder(content: String) { 
  Column() { 
    Text(Tip: ${content} ).fontSize(20).height("30%") 
    Text('失败原因:失败原因!').fontSize(16).height("30%") 
    Row() { 
      Button("确认").onClick(() => { 
        promptAction.closeCustomDialog(customDialogId) 
      }) 
      Blank().width(50) 
      Button("取消").onClick(() => { 
        promptAction.closeCustomDialog(customDialogId) 
      }) 
    } 
    .margin({ top: 30 }) 
  }.height(200).padding(5) 
} 
export function testPromptDialog() { 
  const that = GlobalContext.getContext().getObject('UIContext') as UIContext; 
  if (that) { 
    promptAction.openCustomDialog({ 
      builder: customDialogBuilder.bind(that, "网络请求失败!") 
    }).then((dialogId: number) => { 
      customDialogId = dialogId; 
    }) 
  } 
}
撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进