HarmonyOS APP多窗口:从创建管理到窗口间通信,玩转多窗口开发

举报
Jack20 发表于 2026/06/20 14:55:46 2026/06/20
【摘要】 HarmonyOS APP多窗口:从创建管理到窗口间通信,玩转多窗口开发📌 核心要点:多窗口不是简单的"开几个窗口",而是窗口创建、生命周期管理、窗口间通信、性能优化的系统工程一、背景与动机你有没有这样的体验?在电脑上同时开着浏览器查资料、VS Code 写代码、微信聊天——三个窗口各司其职,效率拉满。这就是多窗口的魅力。在移动端,多窗口的需求同样强烈。想象一下:你正在看视频教程,同时需要...

HarmonyOS APP多窗口:从创建管理到窗口间通信,玩转多窗口开发

📌 核心要点:多窗口不是简单的"开几个窗口",而是窗口创建、生命周期管理、窗口间通信、性能优化的系统工程


一、背景与动机

你有没有这样的体验?在电脑上同时开着浏览器查资料、VS Code 写代码、微信聊天——三个窗口各司其职,效率拉满。这就是多窗口的魅力。

在移动端,多窗口的需求同样强烈。想象一下:你正在看视频教程,同时需要在笔记应用里记录要点;或者你在购物时,想同时对比两个商品页面。单窗口来回切换?太痛苦了。多窗口才是正解。

HarmonyOS 提供了完善的多窗口能力,支持应用内创建多个子窗口、窗口间数据通信、独立的生命周期管理。但多窗口开发也不是"多开几个窗口"那么简单——窗口怎么创建?生命周期怎么管?窗口间怎么通信?性能怎么优化?这些问题都需要系统性地理解。

这篇文章,咱们就从零开始,把多窗口开发的方方面面都讲透。


二、核心原理

2.1 多窗口架构总览

关键概念解读:

主窗口(MainWindow):应用启动时自动创建的窗口,与 Ability 一一对应

子窗口(SubWindow):应用内通过代码创建的额外窗口,可以有多个

窗口管理器(WMS):系统级服务,负责所有窗口的创建、销毁、调度

2.2 多窗口生命周期

多窗口的生命周期和单窗口有显著差异,核心在于:每个窗口都有独立的生命周期,但共享同一个 Ability 生命周期

2.3 窗口间通信机制对比

通信方式

适用场景

数据类型

延迟

复杂度

Emitter

事件通知、简单数据传递

基本类型

AppStorage

状态同步、全局共享

@Observed对象

DataShare

大量数据、持久化共享

结构化数据

LocalStorage

同Ability内窗口共享

任意类型


三、代码实战

3.1 子窗口的创建与管理

这是一个完整的子窗口管理示例,包括创建、显示、隐藏、销毁全流程:

// MultiWindowAbility.ets
import { UIAbility, AbilityConstant, WindowStage } from '@kit.AbilityKit';
import { window } from '@kit.ArkUI';
import { BusinessError } from '@kit.BasicServicesKit';

export default class MultiWindowAbility extends UIAbility {
// 主窗口引用
private mainWindow: window.Window | null = null;
// 子窗口引用集合
private subWindows: Map<string, window.Window> = new Map();

onWindowStageCreate(windowStage: WindowStage): void {
console.info('[MultiWindow] onWindowStageCreate');

// 获取主窗口
windowStage.getMainWindow().then((win: window.Window) => {
this.mainWindow = win;
win.setWindowLayoutFullScreen(true);
});

// 加载主页面
windowStage.loadContent('pages/Index');
}

/**
* 创建子窗口
* @param name 窗口名称(唯一标识)
* @param rect 窗口位置和大小
* @param contentPath 页面路径
*/
createSubWindow(
name: string,
rect: window.Rect,
contentPath: string
): void {
// 检查是否已存在同名子窗口
if (this.subWindows.has(name)) {
console.warn(`[MultiWindow] 子窗口 ${name} 已存在`);
this.showSubWindow(name);
return;
}

// 创建子窗口
window.create(this.context, name, window.WindowType.TYPE_APP)
.then((subWin: window.Window) => {
// 设置子窗口属性
subWin.resize(rect.width, rect.height);
subWin.moveWindowTo(rect.left, rect.top);

// 加载子窗口内容
subWin.setUIContent(contentPath, () => {
console.info(`[MultiWindow] 子窗口 ${name} 内容加载完成`);
});

// 显示子窗口
subWin.showWindow();

// 保存引用
this.subWindows.set(name, subWin);
console.info(`[MultiWindow] 子窗口 ${name} 创建成功`);
})
.catch((err: BusinessError) => {
console.error(`[MultiWindow] 创建子窗口失败: ${err.message}`);
});
}

/**
* 显示子窗口
*/
showSubWindow(name: string): void {
const subWin = this.subWindows.get(name);
if (subWin) {
subWin.showWindow();
console.info(`[MultiWindow] 子窗口 ${name} 已显示`);
}
}

/**
* 隐藏子窗口
*/
hideSubWindow(name: string): void {
const subWin = this.subWindows.get(name);
if (subWin) {
subWin.hideWindow();
console.info(`[MultiWindow] 子窗口 ${name} 已隐藏`);
}
}

/**
* 销毁子窗口
*/
destroySubWindow(name: string): void {
const subWin = this.subWindows.get(name);
if (subWin) {
subWin.destroyWindow().then(() => {
this.subWindows.delete(name);
console.info(`[MultiWindow] 子窗口 ${name} 已销毁`);
}).catch((err: BusinessError) => {
console.error(`[MultiWindow] 销毁子窗口失败: ${err.message}`);
});
}
}

onWindowStageDestroy(): void {
// 销毁所有子窗口
this.subWindows.forEach((subWin, name) => {
subWin.destroyWindow().catch(() => {});
console.info(`[MultiWindow] 子窗口 ${name} 已随主窗口销毁`);
});
this.subWindows.clear();
this.mainWindow = null;
}
}

3.2 窗口间通信——Emitter 事件总线

Emitter 是窗口间通信最轻量的方式,适合事件通知和简单数据传递:

// WindowCommunication.ets
import { emitter } from '@kit.BasicServicesKit';
import { window } from '@kit.ArkUI';
import { common } from '@kit.AbilityKit';

// 事件ID定义(全局常量,确保各窗口使用相同的ID)
const EVENT_ID_VIDEO_CONTROL = 10001;
const EVENT_ID_NOTE_UPDATE = 10002;
const EVENT_ID_PLAYLIST_SYNC = 10003;

// ===== 主窗口页面 =====
@Entry
@Component
struct MainWindowPage {
@State videoTitle: string = 'HarmonyOS 开发教程';
@State isPlaying: boolean = false;

aboutToAppear(): void {
// 监听来自子窗口的控制事件
emitter.on({ eventId: EVENT_ID_VIDEO_CONTROL }, (eventData) => {
const action = eventData.data?.action as string;
console.info(`[MainWindow] 收到控制事件: ${action}`);
if (action === 'play') {
this.isPlaying = true;
} else if (action === 'pause') {
this.isPlaying = false;
}
});
}

aboutToDisappear(): void {
// 注销监听
emitter.off(EVENT_ID_VIDEO_CONTROL);
}

build() {
Column({ space: 16 }) {
Text('主窗口 - 视频播放')
.fontSize(22)
.fontWeight(FontWeight.Bold)

Text(this.videoTitle)
.fontSize(16)
.fontColor('#aaa')

Row({ space: 20 }) {
Button(this.isPlaying ? '暂停' : '播放')
.onClick(() => {
this.isPlaying = !this.isPlaying;
// 通知子窗口状态变化
emitter.emit({
eventId: EVENT_ID_PLAYLIST_SYNC,
}, {
data: { isPlaying: this.isPlaying }
});
})
}

Button('打开笔记子窗口')
.width('80%')
.backgroundColor('#4CAF50')
.onClick(() => this.openNoteWindow())
}
.width('100%')
.height('100%')
.backgroundColor('#0d0d1a')
.foregroundColor('#fff')
.justifyContent(FlexAlign.Center)
}

/**
* 打开笔记子窗口
*/
private openNoteWindow(): void {
const context = getContext(this) as common.UIAbilityContext;
window.create(context, 'noteWindow', window.WindowType.TYPE_APP)
.then((subWin: window.Window) => {
subWin.resize(400, 600);
subWin.moveWindowTo(50, 200);
subWin.setUIContent('pages/NoteSubWindow');
subWin.showWindow();
});
}
}

// ===== 子窗口页面 =====
@Entry
@Component
struct NoteSubWindowPage {
@State noteContent: string = '';
@State isVideoPlaying: boolean = false;

aboutToAppear(): void {
// 监听播放列表同步事件
emitter.on({ eventId: EVENT_ID_PLAYLIST_SYNC }, (eventData) => {
this.isVideoPlaying = eventData.data?.isPlaying as boolean;
console.info(`[NoteWindow] 播放状态同步: ${this.isVideoPlaying}`);
});
}

aboutToDisappear(): void {
emitter.off(EVENT_ID_PLAYLIST_SYNC);
}

build() {
Column({ space: 12 }) {
Text('笔记子窗口')
.fontSize(18)
.fontWeight(FontWeight.Bold)

// 播放状态指示器
Row({ space: 8 }) {
Circle({ width: 8, height: 8 })
.fill(this.isVideoPlaying ? '#4CAF50' : '#666')
Text(this.isVideoPlaying ? '播放中' : '已暂停')
.fontSize(12)
.fontColor('#aaa')
}

// 笔记输入区
TextArea({ placeholder: '在这里记录笔记...' })
.width('90%')
.height(200)
.onChange((value: string) => {
this.noteContent = value;
// 通知主窗口笔记内容更新
emitter.emit({
eventId: EVENT_ID_NOTE_UPDATE,
}, {
data: { content: value }
});
})

// 控制主窗口视频播放
Row({ space: 12 }) {
Button('播放')
.onClick(() => {
emitter.emit({ eventId: EVENT_ID_VIDEO_CONTROL }, { data: { action: 'play' } });
})
Button('暂停')
.onClick(() => {
emitter.emit({ eventId: EVENT_ID_VIDEO_CONTROL }, { data: { action: 'pause' } });
})
}
}
.width('100%')
.height('100%')
.backgroundColor('#1a1a2e')
.foregroundColor('#fff')
.padding(16)
}
}

3.3 多窗口生命周期管理与性能优化

这个示例展示如何在多窗口场景下管理生命周期,并进行性能优化:

// MultiWindowLifecycle.ets
import { UIAbility, AbilityConstant, WindowStage } from '@kit.AbilityKit';
import { window } from '@kit.ArkUI';
import { BusinessError } from '@kit.BasicServicesKit';

// 窗口状态枚举
enum WindowState {
CREATED = 'created',
SHOWN = 'shown',
HIDDEN = 'hidden',
DESTROYED = 'destroyed'
}

// 窗口信息接口
interface SubWindowInfo {
window: window.Window;
state: WindowState;
lastActiveTime: number;
}

export default class MultiWindowLifecycleAbility extends UIAbility {
// 子窗口管理器
private subWindowMap: Map<string, SubWindowInfo> = new Map();
// 最大子窗口数量限制
private readonly MAX_SUB_WINDOWS = 3;

onWindowStageCreate(windowStage: WindowStage): void {
windowStage.getMainWindow().then((win: window.Window) => {
// 监听主窗口焦点变化
win.on('windowStatusChange', (status: window.WindowStatus) => {
if (status === window.WindowStatus.UNFOCUSED) {
// 主窗口失焦时,暂停非活跃子窗口的动画
this.pauseInactiveSubWindows();
} else {
// 主窗口获焦时,恢复活跃子窗口
this.resumeActiveSubWindows();
}
});
});

windowStage.loadContent('pages/Index');
}

/**
* 创建子窗口(带生命周期管理)
*/
createManagedSubWindow(name: string, contentPath: string): void {
// 性能优化:限制子窗口数量
if (this.subWindowMap.size >= this.MAX_SUB_WINDOWS) {
// 销毁最早不活跃的子窗口
this.destroyOldestInactiveWindow();
}

window.create(this.context, name, window.WindowType.TYPE_APP)
.then((subWin: window.Window) => {
const info: SubWindowInfo = {
window: subWin,
state: WindowState.CREATED,
lastActiveTime: Date.now()
};
this.subWindowMap.set(name, info);

// 注册子窗口生命周期回调
this.registerSubWindowCallbacks(name, subWin);

// 加载内容并显示
subWin.setUIContent(contentPath, () => {
subWin.showWindow();
info.state = WindowState.SHOWN;
info.lastActiveTime = Date.now();
});
});
}

/**
* 注册子窗口的生命周期回调
*/
private registerSubWindowCallbacks(name: string, subWin: window.Window): void {
// 监听子窗口焦点变化
subWin.on('windowStatusChange', (status: window.WindowStatus) => {
const info = this.subWindowMap.get(name);
if (!info) return;

if (status === window.WindowStatus.FOCUSED) {
info.lastActiveTime = Date.now();
console.info(`[Lifecycle] 子窗口 ${name} 获得焦点`);
} else {
console.info(`[Lifecycle] 子窗口 ${name} 失去焦点`);
}
});
}

/**
* 暂停非活跃子窗口(性能优化)
* 通知UI层暂停动画、降低刷新率
*/
private pauseInactiveSubWindows(): void {
this.subWindowMap.forEach((info, name) => {
if (info.state === WindowState.SHOWN) {
// 通过 AppStorage 通知子窗口暂停动画
AppStorage.setOrCreate(`pause_${name}`, true);
console.info(`[Lifecycle] 暂停子窗口 ${name} 的动画`);
}
});
}

/**
* 恢复活跃子窗口
*/
private resumeActiveSubWindows(): void {
this.subWindowMap.forEach((info, name) => {
if (info.state === WindowState.SHOWN) {
AppStorage.setOrCreate(`pause_${name}`, false);
console.info(`[Lifecycle] 恢复子窗口 ${name} 的动画`);
}
});
}

/**
* 销毁最早不活跃的子窗口
* 用于在子窗口数量达到上限时腾出空间
*/
private destroyOldestInactiveWindow(): void {
let oldestName = '';
let oldestTime = Infinity;

this.subWindowMap.forEach((info, name) => {
if (info.lastActiveTime < oldestTime) {
oldestTime = info.lastActiveTime;
oldestName = name;
}
});

if (oldestName) {
this.destroySubWindow(oldestName);
console.info(`[Lifecycle] 为腾出空间,销毁最不活跃的子窗口: ${oldestName}`);
}
}

/**
* 销毁指定子窗口
*/
private destroySubWindow(name: string): void {
const info = this.subWindowMap.get(name);
if (info) {
info.window.off('windowStatusChange');
info.window.destroyWindow();
info.state = WindowState.DESTROYED;
this.subWindowMap.delete(name);
}
}

onForeground(): void {
console.info('[Lifecycle] 应用回到前台,恢复所有子窗口');
this.resumeActiveSubWindows();
}

onBackground(): void {
console.info('[Lifecycle] 应用退到后台,暂停所有子窗口');
this.pauseInactiveSubWindows();
}

onWindowStageDestroy(): void {
// 销毁所有子窗口
this.subWindowMap.forEach((info, name) => {
try {
info.window.off('windowStatusChange');
info.window.destroyWindow();
} catch (e) {
// 忽略销毁时的错误
}
});
this.subWindowMap.clear();
}
}


四、踩坑与注意事项

4.1 子窗口类型选择

:用 TYPE_FLOAT 创建子窗口,结果在部分设备上无法显示。

HarmonyOS 的窗口类型有严格区分:

类型

说明

使用场景

TYPE_APP

应用子窗口

应用内多窗口

TYPE_FLOAT

悬浮窗

需要悬浮权限

TYPE_DIALOG

对话框窗口

模态对话框

建议:应用内的多窗口统一使用 TYPE_APP,悬浮窗使用 TYPE_FLOAT(需要额外权限)。

4.2 子窗口的 Z 序管理

:创建了多个子窗口,但窗口叠放顺序混乱,重要内容被遮挡。

子窗口的 Z 序(叠放顺序)默认按创建时间排列,后创建的在上层。如果需要调整:

// ❌ 错误:没有控制Z序,窗口可能被遮挡
subWin1.showWindow();
subWin2.showWindow(); // subWin2 在上层

// ✅ 正确:按需调整窗口显示顺序
subWin2.showWindow(); // 先显示下层的
subWin1.showWindow(); // 再显示上层的

4.3 窗口间通信的数据量

:通过 Emitter 传递大量数据(如图片 base64),导致通信卡顿。

Emitter 设计用于轻量级事件通知,不适合传递大数据。大数据传递的推荐方案:

// ❌ 错误:通过 Emitter 传递大数据
emitter.emit({ eventId: 100 }, { data: { imageBase64: '...' } });

// ✅ 正确:通过 AppStorage 传递数据引用
AppStorage.setOrCreate('sharedImageData', imageData);
// 子窗口通过 AppStorage.get 读取

4.4 子窗口内存泄漏

:反复创建子窗口但不销毁,内存持续增长。

// ❌ 错误:每次都创建新窗口,旧窗口未销毁
private openWindow(): void {
window.create(this.context, 'note', window.WindowType.TYPE_APP)
.then(subWin => {
subWin.setUIContent('pages/NotePage');
subWin.showWindow();
// 没有保存引用,也无法销毁!
});
}

// ✅ 正确:保存引用,关闭时销毁
private subWindow: window.Window | null = null;

private openWindow(): void {
if (this.subWindow) {
this.subWindow.showWindow(); // 复用已有窗口
return;
}
window.create(this.context, 'note', window.WindowType.TYPE_APP)
.then(subWin => {
this.subWindow = subWin;
subWin.setUIContent('pages/NotePage');
subWin.showWindow();
});
}

private closeWindow(): void {
this.subWindow?.destroyWindow();
this.subWindow = null;
}

4.5 多窗口下的资源竞争

:多个子窗口同时访问同一个资源(如相机、麦克风),导致冲突。

解决方案:

1. 使用 AppStorage 或 LocalStorage 做资源锁

2. 在窗口获焦时申请资源,失焦时释放

3. 使用排他性设计,同一时刻只有一个窗口可以使用敏感资源


五、HarmonyOS 6 适配

5.1 API 变化

特性

HarmonyOS 5.0

HarmonyOS 6.0

子窗口数量限制

无明确限制

建议不超过5个,系统可能限制

窗口间通信

Emitter/AppStorage

新增 WindowProxy 跨窗口调用

窗口动画

手动实现

新增窗口过渡动画 API

多窗口布局

手动计算位置

新增 WindowLayout 辅助类

子窗口拖拽

不支持

新增拖拽移动子窗口能力

5.2 迁移要点

1. 窗口数量限制:HarmonyOS 6 对子窗口数量有更严格的管控,建议应用内子窗口不超过 3-5 个

2. WindowProxy:新增的跨窗口调用机制,比 Emitter 更类型安全,适合复杂通信场景

3. 窗口过渡动画:创建和销毁子窗口时可以添加过渡动画,提升用户体验

4. WindowLayout:新增的布局辅助类,可以更方便地计算子窗口位置

5.3 兼容性写法

// 兼容 HarmonyOS 5.0 和 6.0 的子窗口创建
private async createSubWindowCompat(name: string, contentPath: string): Promise<void> {
const subWin = await window.create(this.context, name, window.WindowType.TYPE_APP);

// 基础属性设置(5.0+ 支持)
subWin.resize(400, 600);
subWin.moveWindowTo(50, 200);

// HarmonyOS 6.0 新增:窗口过渡动画
// if (deviceInfo.osFullName >= '6.0.0') {
// subWin.setTransitionAnimation(window.TransitionAnimation.SLIDE);
// }

await subWin.setUIContent(contentPath);
subWin.showWindow();
}


六、总结

知识点

核心内容

关键API

多窗口模式

主窗口+子窗口架构

window.create

窗口创建

创建、显示、隐藏、销毁

create, showWindow, hideWindow, destroyWindow

窗口管理

Z序、焦点、数量限制

on('windowStatusChange')

窗口间通信

Emitter/AppStorage/DataShare

emitter.emit/on, AppStorage

生命周期

独立焦点、共享Ability

onForeground/onBackground

性能优化

暂停非活跃窗口、限制数量

AppStorage 状态控制

资源管理

避免泄漏、资源竞争

destroyWindow, 引用管理

一句话总结:多窗口开发的核心是"管好三个关系"——窗口与窗口的通信关系、窗口与Ability的生命周期关系、窗口与系统的资源关系。通信用 Emitter,生命周期靠回调,资源管理要勤销毁,这三板斧记牢了,多窗口开发就不慌。

【声明】本内容来自华为云开发者社区博主,不代表华为云及华为云开发者社区的观点和立场。转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息,否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。