Flutter三方库适配OpenHarmony【secure_application】— 错误处理与异常边界
欢迎加入开源鸿蒙跨平台社区:https://openharmonycrossplatform.csdn.net即使出错也不能崩溃。用户打开银行App查看余额,如果因为隐私保护模块的一个异常导致整个App闪退,那比不做保护还糟糕。所以 secure_application 在 OpenHarmony 端采用了大量的防御性编程——每个可能出错的地方都包了 try-catch。这篇把所有的错误场景和处理
前言
欢迎加入开源鸿蒙跨平台社区:https://openharmonycrossplatform.csdn.net
隐私保护插件有一个特殊要求:即使出错也不能崩溃。用户打开银行App查看余额,如果因为隐私保护模块的一个异常导致整个App闪退,那比不做保护还糟糕。所以 secure_application 在 OpenHarmony 端采用了大量的防御性编程——每个可能出错的地方都包了 try-catch。
这篇把所有的错误场景和处理策略都梳理一遍。
一、BusinessError 错误类型
1.1 什么是 BusinessError
import { BusinessError } from '@kit.BasicServicesKit';
BusinessError 是 OpenHarmony 系统 API 返回的标准错误类型,包含错误码和错误信息:
interface BusinessError {
code: number; // 错误码
message: string; // 错误描述
}
1.2 Window API 相关错误码
| 错误码 | 含义 | 触发场景 |
|---|---|---|
| 1300001 | 重复操作 | 重复调用某些窗口方法 |
| 1300002 | 窗口状态异常 | 窗口已销毁或不可用 |
| 1300003 | 系统服务异常 | 窗口管理服务不可用 |
| 1300004 | 权限不足 | 缺少 PRIVACY_WINDOW 权限 |
| 1300005 | 参数无效 | 传入了无效的参数 |
1.3 错误日志格式
Log.e(TAG, "Failed to set window privacy mode: " + JSON.stringify(err));
// 输出示例:
// SecureApplicationPlugin: Failed to set window privacy mode: {"code":1300002,"message":"Window state abnormal"}
📌 用 JSON.stringify 序列化错误对象,可以完整输出错误码和错误信息,方便排查。
二、Window 获取失败的处理
2.1 失败场景
| 场景 | 原因 | 频率 |
|---|---|---|
| 插件初始化太早 | 窗口还没创建 | 偶尔 |
| Context 无效 | 应用正在退出 | 罕见 |
| 系统资源不足 | 窗口管理服务异常 | 极罕见 |
2.2 处理策略:延迟重试
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);
}
2.3 降级策略
getMainWindow() 失败
│
├── 记录错误日志
├── mainWindow 保持 null
│
└── 后续调用 secure() 时
│
├── mainWindow == null → 重新尝试获取
│ │
│ ├── 成功 → 设置隐私模式 ✅
│ └── 失败 → 再次记录日志,静默失败
│
└── mainWindow != null → 正常设置
💡 静默失败的合理性:即使原生端的隐私模式设置失败,Dart 层的 SecureGate 模糊遮罩仍然有效。用户切回 App 时看到的是模糊画面,只是截屏防护没生效。
三、setWindowPrivacyMode 调用失败
3.1 失败处理
private applyPrivacyMode(win: window.Window, isPrivacy: boolean): void {
try {
win.setWindowPrivacyMode(isPrivacy).then(() => {
Log.i(TAG, "Window privacy mode set to: " + isPrivacy);
}).catch((err: BusinessError) => {
Log.e(TAG, "Failed to set window privacy mode: " + JSON.stringify(err));
});
} catch (err) {
Log.e(TAG, "Exception applying privacy mode: " + JSON.stringify(err));
}
}
3.2 可能的失败原因
| 原因 | 错误码 | 处理 |
|---|---|---|
| 窗口已销毁 | 1300002 | 重新获取窗口 |
| 权限不足 | 1300004 | 提示用户声明权限 |
| 系统服务异常 | 1300003 | 延迟重试 |
3.3 失败后的影响
| 影响范围 | 是否受影响 | 说明 |
|---|---|---|
| 截屏防护 | ✅ 失效 | 用户可以正常截屏 |
| 应用切换器 | ✅ 失效 | 切换器中能看到内容 |
| 模糊遮罩 | ❌ 不受影响 | Flutter 层的遮罩正常工作 |
| 认证流程 | ❌ 不受影响 | onNeedUnlock 正常触发 |
| 锁定/解锁 | ❌ 不受影响 | 状态管理正常工作 |
四、生命周期回调注册失败
4.1 失败处理
private registerLifecycleCallback(): void {
if (this.context == null) {
return;
}
try {
const applicationContext = this.context.getApplicationContext();
// ... 注册回调 ...
Log.i(TAG, "Lifecycle callback registered");
} catch (err) {
Log.e(TAG, "Failed to register lifecycle callback: " + JSON.stringify(err));
}
}
4.2 失败后的影响
| 影响 | 说明 |
|---|---|
| 应用进入后台时不会触发锁定 | 但窗口事件监听可能仍然有效 |
| 窗口事件作为备份 | WINDOW_INACTIVE 仍然能检测到切换 |
| Flutter 层的 WidgetsBindingObserver | 仍然能检测到 AppLifecycleState 变化 |
📌 三重保障:即使生命周期回调注册失败,还有窗口事件监听和 Flutter 层的 WidgetsBindingObserver 作为备份。三层中只要有一层工作,锁定就能触发。
五、try-catch 包裹策略
5.1 所有 try-catch 的位置
// 1. 获取主窗口
private getMainWindow(): void {
try { ... } catch (err) { Log.e(TAG, ...); }
}
// 2. 设置隐私模式
private setPrivacyMode(isPrivacy: boolean): void {
try { ... } catch (err) { Log.e(TAG, ...); }
}
// 3. 应用隐私模式
private applyPrivacyMode(win, isPrivacy): void {
try { ... } catch (err) { Log.e(TAG, ...); }
}
// 4. 注册窗口事件
private registerWindowEventCallback(win): void {
try { ... } catch (err) { Log.e(TAG, ...); }
}
// 5. 注销窗口事件
private unregisterWindowEventCallback(): void {
try { ... } catch (err) { Log.e(TAG, ...); }
}
// 6. 注册生命周期回调
private registerLifecycleCallback(): void {
try { ... } catch (err) { Log.e(TAG, ...); }
}
// 7. 注销生命周期回调
private unregisterLifecycleCallback(): void {
try { ... } catch (err) { Log.e(TAG, ...); }
}
5.2 统计
| 统计项 | 数量 |
|---|---|
| try-catch 块 | 7个 |
| Promise.catch | 4个 |
| 空值检查 | 8个 |
| 总防御点 | 19个 |
234行代码中有19个防御点,平均每12行就有一个。这个密度对于安全类插件来说是合理的。
5.3 防御性编程原则
- 每个系统 API 调用都包 try-catch
- 每个 Promise 都有 .catch
- 每个可能为 null 的引用都做空值检查
- 错误不上抛,只记录日志
- 错误不影响后续操作
六、onMethodCall 中的错误处理
6.1 opacity 参数解析
case "opacity":
try {
const newOpacity: number = call.argument("opacity");
if (newOpacity != null && newOpacity != undefined) {
this.opacity = newOpacity;
}
} catch (e) {
Log.e(TAG, "Failed to parse opacity argument");
}
result.success(true); // 即使解析失败也返回成功
break;
6.2 secure 方法的可选参数
case "secure":
this.secured = true;
try {
const secureOpacity: number = call.argument("opacity");
if (secureOpacity != null && secureOpacity != undefined) {
this.opacity = secureOpacity;
}
} catch (e) {
// 静默忽略,使用默认 opacity
}
this.setPrivacyMode(true);
result.success(true);
break;
6.3 为什么参数解析失败不影响主逻辑
secure() 调用
│
├── this.secured = true ← 必须成功
├── 解析 opacity ← 可选,失败不影响
├── this.setPrivacyMode(true) ← 必须执行
└── result.success(true) ← 必须返回
opacity 是可选参数,解析失败时使用默认值(0.2),不影响核心的隐私模式设置。
七、错误恢复策略
7.1 自动恢复
| 错误 | 恢复方式 | 时机 |
|---|---|---|
| 窗口获取失败 | 下次 secure() 时重试 | 自动 |
| 隐私模式设置失败 | 下次 secure() 时重试 | 自动 |
| 窗口事件注册失败 | 依赖生命周期回调 | 自动降级 |
7.2 手动恢复
// Dart 层:如果怀疑保护没生效,可以重新调用
controller.open(); // 先关闭
controller.secure(); // 再开启
7.3 错误监控建议
// 可以添加错误计数器
private errorCount: number = 0;
private handleError(operation: string, err: any): void {
this.errorCount++;
Log.e(TAG, `[${this.errorCount}] ${operation}: ${JSON.stringify(err)}`);
// 如果错误过多,通知 Dart 层
if (this.errorCount > 5 && this.channel != null) {
this.channel.invokeMethod("error", {
"message": "Privacy mode may not be working",
"count": this.errorCount
});
}
}
💡 当前实现没有错误计数和上报机制,但在生产环境中建议添加。这样 Dart 层可以在隐私模式不可用时给用户一个提示。
总结
本文全面分析了 secure_application 的错误处理策略:
- BusinessError:OpenHarmony 标准错误类型,包含错误码和描述
- 19个防御点:7个 try-catch + 4个 Promise.catch + 8个空值检查
- 静默失败:错误不上抛,不影响 App 正常运行
- 三重保障:窗口事件 + 生命周期回调 + Flutter WidgetsBindingObserver
- 自动恢复:窗口获取失败时下次调用自动重试
下一篇我们讲调试与问题排查——如何用日志和工具定位问题。
如果这篇文章对你有帮助,欢迎点赞👍、收藏⭐、关注🔔,你的支持是我持续创作的动力!
相关资源:
- BusinessError 文档
- OpenHarmony 错误码列表
- ArkTS 异常处理
- secure_application OpenHarmony 源码
- Flutter 错误处理最佳实践
- 防御性编程
- Promise 错误处理
- 开源鸿蒙跨平台社区

更多推荐



所有评论(0)