第77篇 | HarmonyOS DLP Anti Peep:防窥状态监听与遮罩效果
第77篇 | HarmonyOS DLP Anti Peep:防窥状态监听与遮罩效果
第 77 篇讲 DLP Anti Peep。相册和保险箱属于高隐私内容,除了进入前认证,还要考虑用户正在浏览照片时是否被旁人窥屏。HarmonyOS 的 DLP Anti Peep 能力可以监听系统防窥状态,并在检测到风险时设置遮罩层。
双镜记忆相机把防窥能力放在相册详情页里:进入详情时启动防窥监听,页面隐藏时停止;检测到 HIDE 状态时更新遮罩状态,并调用 setAntiPeepMaskLayer。这一篇会把系统开关、事件监听、遮罩设置和 UI 覆盖层一起讲清楚。
本篇目标
- 理解 DLP Anti Peep 在相册详情页中的启动和停止时机。
- 掌握
dlpAntiPeep.on、getDlpAntiPeepInfo和setAntiPeepMaskLayer的协作。 - 理解防窥状态如何同步到页面提示和遮罩层。
- 学会处理权限缺失、设备不支持和系统开关未开启。
对应源码位置
superImage/entry/src/main/ets/pages/Index.etssuperImage/entry/src/main/module.json5
防窥不是弹窗,而是浏览过程保护
相册详情页里显示的是用户刚拍下的真实记忆,可能包含地点、人物和前后镜头组合。防窥能力的目标不是阻止用户打开照片,而是在系统判断有窥屏风险时,把当前窗口临时遮挡。
运行层面,用户仍然可以正常进入相册详情。只有系统防窥状态触发后,页面才显示“防偷窥保护中”这样的覆盖层,并配合系统遮罩保护当前窗口。这样体验不会过度打扰,但风险出现时能及时收住内容。

相册详情页承载 DLP Anti Peep 防窥状态提示
事件回调只把状态交给统一处理函数
galleryAntiPeepCallback 很短,只把系统回调状态交给 applyGalleryAntiPeepStatus。这种写法让事件入口保持轻量,真正的状态判断、UI 文案和遮罩设置集中在一个函数里。
项目还用 galleryAntiPeepSubscribed 记录是否已经订阅。防窥能力和近场分享一样属于系统监听类能力,必须避免重复订阅和漏清理。这个布尔状态就是后续 start/stop 闭环的基础。

galleryAntiPeepCallback 将系统状态交给统一处理函数
private readonly galleryAntiPeepCallback = (status: dlpAntiPeep.DlpAntiPeepStatus): void => {
void this.applyGalleryAntiPeepStatus(status, true);
};
private galleryAntiPeepSubscribed: boolean = false;
启动监听前先检查系统开关
startGalleryAntiPeepProtection 会先重置 ready、active 和状态文案,然后调用 isDlpAntiPeepSwitchOn。如果系统防窥未开启,就直接展示状态并返回,不继续订阅事件。
系统开关开启后,函数设置 ready 状态,注册 dlpAntiPeep 事件,并立即读取当前状态。立即读取很重要:如果进入详情页时系统已经处于 HIDE 状态,页面不能等下一次事件才遮挡。

startGalleryAntiPeepProtection 检查系统开关并订阅防窥事件
private async startGalleryAntiPeepProtection(): Promise<void> {
this.galleryAntiPeepReady = false;
this.galleryAntiPeepActive = false;
this.galleryAntiPeepStatusText = '';
try {
const enabled = await dlpAntiPeep.isDlpAntiPeepSwitchOn();
if (!enabled) {
this.galleryAntiPeepStatusText = '系统防窥未开启';
return;
}
this.galleryAntiPeepReady = true;
this.galleryAntiPeepStatusText = '防窥保护已开启';
if (!this.galleryAntiPeepSubscribed) {
dlpAntiPeep.on('dlpAntiPeep', this.galleryAntiPeepCallback);
this.galleryAntiPeepSubscribed = true;
}
const currentStatus = dlpAntiPeep.getDlpAntiPeepInfo();
await this.applyGalleryAntiPeepStatus(currentStatus, false);
} catch (error) {
const err = error as BusinessError;
this.galleryAntiPeepReady = false;
this.galleryAntiPeepActive = false;
this.galleryAntiPeepStatusText = this.getGalleryAntiPeepErrorText(err.code ?? -1);
console.warn(`Gallery anti-peep unavailable: ${err.code ?? -1} ${err.message ?? ''}`);
}
}
停止监听要恢复页面状态
stopGalleryAntiPeepProtection 在页面隐藏或退出详情时调用。它先尝试 off 事件,再把 subscribed、active、ready 和状态文案全部清空。即使 off 失败,也会输出 warning 并继续恢复本地状态。
这一步能避免一个常见问题:用户离开照片详情后,防窥状态仍然影响其他页面。系统能力监听如果没有生命周期边界,会让后续页面出现难以排查的状态残留。

stopGalleryAntiPeepProtection 退出时清理订阅和 UI 状态
private stopGalleryAntiPeepProtection(): void {
if (this.galleryAntiPeepSubscribed) {
try {
dlpAntiPeep.off('dlpAntiPeep', this.galleryAntiPeepCallback);
} catch (error) {
const err = error as BusinessError;
console.warn(`Gallery anti-peep off failed: ${err.code ?? -1} ${err.message ?? ''}`);
}
this.galleryAntiPeepSubscribed = false;
}
this.galleryAntiPeepActive = false;
this.galleryAntiPeepReady = false;
this.galleryAntiPeepStatusText = '';
}
HIDE 状态触发窗口遮罩
applyGalleryAntiPeepStatus 判断系统状态是否为 HIDE。命中后把 galleryAntiPeepActive 设为 true,更新文案,再调用 showGalleryAntiPeepMaskLayer。遮罩函数拿到当前 windowId 后调用 setAntiPeepMaskLayer。
页面 UI 也会根据 galleryAntiPeepActive 展示覆盖层。这样系统窗口遮罩和 ArkUI 页面提示同时生效:系统负责保护内容,页面负责告诉用户当前照片已进入防窥保护状态。

检测到 HIDE 后设置页面状态并调用系统遮罩
private async applyGalleryAntiPeepStatus(
status: dlpAntiPeep.DlpAntiPeepStatus,
fromCallback: boolean
): Promise<void> {
this.galleryAntiPeepReady = true;
if (status === dlpAntiPeep.DlpAntiPeepStatus.HIDE) {
this.galleryAntiPeepActive = true;
this.galleryAntiPeepStatusText = '检测到窥屏,已保护';
await this.showGalleryAntiPeepMaskLayer(fromCallback);
return;
}
this.galleryAntiPeepActive = false;
this.galleryAntiPeepStatusText = '防窥保护已开启';
}
private async showGalleryAntiPeepMaskLayer(fromCallback: boolean): Promise<void> {
try {
const currentWindow = await window.getLastWindow(this.getAbilityContext());
const windowId = currentWindow.getWindowProperties().id;
await dlpAntiPeep.setAntiPeepMaskLayer(windowId);
} catch (error) {
const err = error as BusinessError;
if (!fromCallback && err.code === 1020600002) {
this.galleryAntiPeepStatusText = '系统防窥未开启';
}
console.warn(`Gallery anti-peep mask failed: ${err.code ?? -1} ${err.message ?? ''}`);
}
}
工程检查清单
module.json5中声明 DLP 防窥相关权限。- 进入详情页后检查系统防窥开关再订阅事件。
- 读取初始状态,不能只等待下一次回调。
- 页面隐藏时取消订阅并清空 active/ready 状态。
今日练习
- 关闭系统防窥开关后进入详情页,观察状态文案。
- 临时把状态模拟为 HIDE,确认覆盖层和遮罩函数都会触发。
- 从详情页返回相册,检查防窥状态是否被清空。
训练营里的每一篇都建议按同一个节奏复盘:先看页面行为,再回到源码定位状态和服务层,最后自己改一个很小的参数验证结果。这样写文章时不会停留在 API 名词,读者也能沿着真实工程把功能跑通。
更多推荐

所有评论(0)