Flutter三方库适配OpenHarmony【secure_application】— Window 管理与 getLastWindow API
欢迎加入开源鸿蒙跨平台社区:https://openharmonycrossplatform.csdn.netsecure_application 在 OpenHarmony 上的核心能力——截屏防护和应用切换器内容隐藏——都依赖于Window 对象。你得先拿到窗口引用,才能调用。但 OpenHarmony 的窗口获取是异步的,这带来了一些需要处理的边界情况。今天我们把 Window 管理的完整逻
前言
欢迎加入开源鸿蒙跨平台社区:https://openharmonycrossplatform.csdn.net
secure_application 在 OpenHarmony 上的核心能力——截屏防护和应用切换器内容隐藏——都依赖于 Window 对象。你得先拿到窗口引用,才能调用 setWindowPrivacyMode。但 OpenHarmony 的窗口获取是异步的,这带来了一些需要处理的边界情况。
今天我们把 Window 管理的完整逻辑讲清楚。
一、@kit.ArkUI 中的 window 模块
1.1 模块导入
import { window } from '@kit.ArkUI';
@kit.ArkUI 是 OpenHarmony 的 UI 框架 Kit,其中的 window 模块提供了窗口管理能力。
1.2 window 模块的核心 API
| API | 类型 | 说明 |
|---|---|---|
window.getLastWindow(context) |
异步 | 获取当前应用的最后一个窗口 |
Window.setWindowPrivacyMode(isPrivacy) |
异步 | 设置窗口隐私模式 |
Window.on('windowEvent', callback) |
同步 | 注册窗口事件监听 |
Window.off('windowEvent') |
同步 | 注销窗口事件监听 |
1.3 Window 对象的能力
interface Window {
setWindowPrivacyMode(isPrivacy: boolean): Promise<void>;
on(type: 'windowEvent', callback: (event: WindowEventType) => void): void;
off(type: 'windowEvent', callback?: Function): void;
// ... 其他方法
}
对于 secure_application,我们只用到了上面三个方法。
二、getLastWindow 异步获取主窗口
2.1 基本用法
window.getLastWindow(this.context).then((win: window.Window) => {
this.mainWindow = win;
Log.i(TAG, "Got main window successfully");
}).catch((err: BusinessError) => {
Log.e(TAG, "Failed to get main window: " + JSON.stringify(err));
});
2.2 为什么是异步的
Android 获取窗口是同步的:
// Android:同步获取
val window = activity.window // 立即返回
OpenHarmony 获取窗口是异步的:
// OpenHarmony:异步获取
window.getLastWindow(context).then((win) => { ... })
| 平台 | 获取方式 | 原因 |
|---|---|---|
| Android | 同步 | Window 是 Activity 的直接属性 |
| OpenHarmony | 异步 | Window 由窗口管理服务管理,需要 IPC 查询 |
2.3 context 参数
window.getLastWindow(this.context)
这里的 context 是 common.Context 类型,通过 FlutterPluginBinding.getApplicationContext() 获取。它告诉窗口管理服务"我要获取哪个应用的窗口"。
2.4 "最后一个窗口"的含义
getLastWindow 返回的是当前应用最后创建的窗口,通常就是主窗口。在 Flutter 应用中,一般只有一个窗口,所以 getLastWindow 返回的就是 Flutter 渲染内容的那个窗口。
📌 注意:如果应用创建了多个窗口(比如悬浮窗),
getLastWindow可能返回的不是主窗口。但 Flutter 应用通常不会创建额外窗口,所以这个问题不大。
三、完整的窗口获取实现
3.1 getMainWindow 方法
private getMainWindow(): void {
if (this.context == null) {
return;
}
try {
window.getLastWindow(this.context).then((win: window.Window) => {
this.mainWindow = win;
Log.i(TAG, "Got main window successfully");
this.registerWindowEventCallback(win);
}).catch((err: BusinessError) => {
Log.e(TAG, "Failed to get main window: " + JSON.stringify(err));
});
} catch (err) {
Log.e(TAG, "Exception getting main window: " + JSON.stringify(err));
}
}
3.2 逐行分析
| 行 | 操作 | 说明 |
|---|---|---|
| 空值检查 | if (this.context == null) |
没有上下文就无法获取窗口 |
| try-catch | 外层 try-catch | 捕获 getLastWindow 调用本身的异常 |
| then | 成功回调 | 保存窗口引用,注册事件监听 |
| catch | 失败回调 | 记录错误日志 |
3.3 双重错误处理
try {
window.getLastWindow(this.context).then((win) => {
// 成功
}).catch((err: BusinessError) => {
// Promise 拒绝(异步错误)
Log.e(TAG, "Failed to get main window: " + JSON.stringify(err));
});
} catch (err) {
// 同步异常(比如 context 无效)
Log.e(TAG, "Exception getting main window: " + JSON.stringify(err));
}
为什么需要两层错误处理?
| 错误类型 | 捕获方式 | 示例 |
|---|---|---|
| 同步异常 | try-catch | context 类型错误、API 不存在 |
| 异步错误 | Promise.catch | 窗口不存在、权限不足 |
💡 实际经验:在开发过程中,两种错误都遇到过。同步异常通常是 SDK 版本不匹配导致的,异步错误通常是窗口还没创建好就去获取。
四、窗口获取失败的容错处理
4.1 失败场景
| 场景 | 原因 | 频率 |
|---|---|---|
| 插件初始化太早 | 窗口还没创建完成 | 偶尔 |
| context 无效 | 应用正在退出 | 罕见 |
| 系统资源不足 | 窗口管理服务异常 | 极罕见 |
4.2 延迟重试策略
在 setPrivacyMode 方法中实现了重试逻辑:
private setPrivacyMode(isPrivacy: boolean): void {
if (this.mainWindow == null) {
// 窗口还没获取到,重新尝试获取
if (this.context != null) {
try {
window.getLastWindow(this.context).then((win: window.Window) => {
this.mainWindow = win;
this.applyPrivacyMode(win, isPrivacy);
}).catch((err: BusinessError) => {
Log.e(TAG, "Failed to get window for privacy mode: " + JSON.stringify(err));
});
} catch (err) {
Log.e(TAG, "Exception setting privacy mode: " + JSON.stringify(err));
}
}
return;
}
this.applyPrivacyMode(this.mainWindow, isPrivacy);
}
4.3 重试逻辑的设计
Dart 调用 secure()
│
├── mainWindow != null → 直接设置隐私模式 ✅
│
└── mainWindow == null → 重新获取窗口
│
├── 获取成功 → 设置隐私模式 ✅
└── 获取失败 → 记录日志,静默失败
这种"懒获取"策略的好处是:即使 onAttachedToEngine 时窗口还没准备好,后续调用 secure() 时还有机会获取到。
4.4 静默失败 vs 抛出异常
| 策略 | 优点 | 缺点 | 适用场景 |
|---|---|---|---|
| 静默失败 | 不影响 App 正常运行 | 可能不知道保护没生效 | secure_application 选择的方式 |
| 抛出异常 | 开发者能立即发现问题 | 可能导致 App 崩溃 | 安全关键场景 |
secure_application 选择了静默失败,因为隐私保护是一个"增强功能",即使失败了也不应该影响 App 的正常使用。
五、Window 对象的生命周期
5.1 有效期
onAttachedToEngine → getMainWindow() → mainWindow 有效
│
│ ... 插件正常工作 ...
│
onDetachedFromEngine → mainWindow = null → mainWindow 无效
5.2 窗口引用失效的场景
| 场景 | mainWindow 状态 | 处理 |
|---|---|---|
| 正常运行 | 有效 | 正常使用 |
| 热重载 | 可能失效 | onDetachedFromEngine 置空,重新获取 |
| 应用退出 | 失效 | onDetachedFromEngine 置空 |
| 配置变更 | 可能失效 | 需要重新获取 |
5.3 安全使用窗口引用
// 每次使用前检查
if (this.mainWindow != null) {
this.mainWindow.setWindowPrivacyMode(true);
}
// 或者用可选链
this.mainWindow?.setWindowPrivacyMode(true);
六、与 Android Activity.window 的对比
6.1 获取方式对比
// Android:同步,通过 Activity 获取
val window = activity.window
window.addFlags(LayoutParams.FLAG_SECURE)
// OpenHarmony:异步,通过 Context 获取
window.getLastWindow(this.context).then((win) => {
win.setWindowPrivacyMode(true);
});
6.2 操作方式对比
| 操作 | Android | OpenHarmony |
|---|---|---|
| 获取窗口 | activity.window(同步) |
getLastWindow(context)(异步) |
| 开启保护 | addFlags(FLAG_SECURE) |
setWindowPrivacyMode(true) |
| 关闭保护 | clearFlags(FLAG_SECURE) |
setWindowPrivacyMode(false) |
| 返回值 | void | Promise |
6.3 异步带来的复杂性
Android 的同步 API 用起来很简单:
// Android:一行搞定
activity.window.addFlags(LayoutParams.FLAG_SECURE)
OpenHarmony 的异步 API 需要更多的错误处理:
// OpenHarmony:需要处理异步和错误
try {
await this.mainWindow?.setWindowPrivacyMode(true);
} catch (err) {
Log.e(TAG, "Failed: " + JSON.stringify(err));
}
📌 虽然异步 API 用起来复杂一些,但它的设计更合理——窗口操作涉及系统服务的 IPC 调用,本质上就是异步的。Android 把它包装成同步 API 反而隐藏了潜在的性能问题。
七、窗口管理的最佳实践
7.1 获取时机
| 时机 | 推荐度 | 原因 |
|---|---|---|
| onAttachedToEngine | ✅ 推荐 | 尽早获取,后续调用不需要等待 |
| 首次 secure() 调用时 | ⚠️ 可选 | 懒加载,但首次调用会有延迟 |
| 每次 secure() 调用时 | ❌ 不推荐 | 频繁获取浪费资源 |
7.2 缓存策略
// 推荐:获取一次,缓存使用
private mainWindow: window.Window | null = null;
private getMainWindow(): void {
window.getLastWindow(this.context).then((win) => {
this.mainWindow = win; // 缓存
});
}
private setPrivacyMode(isPrivacy: boolean): void {
if (this.mainWindow != null) {
this.applyPrivacyMode(this.mainWindow, isPrivacy); // 使用缓存
}
}
7.3 释放时机
onDetachedFromEngine(binding: FlutterPluginBinding): void {
// 先注销事件,再释放引用
this.unregisterWindowEventCallback();
this.mainWindow = null; // 释放窗口引用
}
先注销事件回调,再释放引用。顺序不能反——如果先释放引用,注销事件时就找不到窗口对象了。
总结
本文详细讲解了 secure_application 的 Window 管理:
- getLastWindow:异步获取主窗口,需要 ApplicationContext
- 双重错误处理:try-catch 捕获同步异常,Promise.catch 捕获异步错误
- 懒获取策略:初始化时获取失败,后续调用时重试
- 静默失败:窗口获取失败不影响 App 正常运行
- 缓存使用:获取一次缓存起来,避免频繁获取
下一篇我们讲 setWindowPrivacyMode——这是 OpenHarmony 隐私保护的核心 API。
如果这篇文章对你有帮助,欢迎点赞👍、收藏⭐、关注🔔,你的支持是我持续创作的动力!
相关资源:
- OpenHarmony Window API
- window.getLastWindow 文档
- @kit.ArkUI 模块
- BusinessError 错误类型
- secure_application OpenHarmony 源码
- Android Window 类
- Promise 异步编程
- 开源鸿蒙跨平台社区

OpenHarmony 窗口管理架构
更多推荐
所有评论(0)