HarmonyOS APP多窗口:从创建管理到窗口间通信,玩转多窗口开发
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,生命周期靠回调,资源管理要勤销毁,这三板斧记牢了,多窗口开发就不慌。
- 点赞
- 收藏
- 关注作者
评论(0)