HarmonyOS APP开发中的应用卸载:卸载监听、数据清理与安全考量全指南
📌 核心要点:掌握 HarmonyOS 应用卸载的监听与回调机制,实现卸载前数据清理、卸载后数据保留(重装恢复),以及卸载场景下的安全防护策略。
一、背景与动机
你有没有这样的经历:卸载一个 App 后重新安装,发现登录态没了、设置全丢了、之前买的东西找不回来了?用户对此的感受是------"这 App 不靠谱"。而另一边,有些 App 卸载后,你的个人信息还残留在设备上,这又是另一种"不靠谱"------安全隐患。
应用卸载,看似只是用户点一下"确认卸载"就结束了,但对开发者来说,这是一场与时间的赛跑。你需要在极短的时间内完成数据清理、状态同步、安全擦除等一系列操作。更复杂的是,HarmonyOS 的分布式数据可能在多个设备上存在副本,卸载时是否要同步清理?用户卸载后重装,哪些数据应该保留?
这些问题不是"锦上添花",而是关乎用户体验和数据安全的"必答题"。
二、核心原理
2.1 应用卸载流程
#mermaid-svg-rX2f5nlm47SDkiPl{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-rX2f5nlm47SDkiPl .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-rX2f5nlm47SDkiPl .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-rX2f5nlm47SDkiPl .error-icon{fill:#552222;}#mermaid-svg-rX2f5nlm47SDkiPl .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-rX2f5nlm47SDkiPl .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-rX2f5nlm47SDkiPl .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-rX2f5nlm47SDkiPl .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-rX2f5nlm47SDkiPl .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-rX2f5nlm47SDkiPl .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-rX2f5nlm47SDkiPl .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-rX2f5nlm47SDkiPl .marker{fill:#333333;stroke:#333333;}#mermaid-svg-rX2f5nlm47SDkiPl .marker.cross{stroke:#333333;}#mermaid-svg-rX2f5nlm47SDkiPl svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-rX2f5nlm47SDkiPl p{margin:0;}#mermaid-svg-rX2f5nlm47SDkiPl .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-rX2f5nlm47SDkiPl .cluster-label text{fill:#333;}#mermaid-svg-rX2f5nlm47SDkiPl .cluster-label span{color:#333;}#mermaid-svg-rX2f5nlm47SDkiPl .cluster-label span p{background-color:transparent;}#mermaid-svg-rX2f5nlm47SDkiPl .label text,#mermaid-svg-rX2f5nlm47SDkiPl span{fill:#333;color:#333;}#mermaid-svg-rX2f5nlm47SDkiPl .node rect,#mermaid-svg-rX2f5nlm47SDkiPl .node circle,#mermaid-svg-rX2f5nlm47SDkiPl .node ellipse,#mermaid-svg-rX2f5nlm47SDkiPl .node polygon,#mermaid-svg-rX2f5nlm47SDkiPl .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-rX2f5nlm47SDkiPl .rough-node .label text,#mermaid-svg-rX2f5nlm47SDkiPl .node .label text,#mermaid-svg-rX2f5nlm47SDkiPl .image-shape .label,#mermaid-svg-rX2f5nlm47SDkiPl .icon-shape .label{text-anchor:middle;}#mermaid-svg-rX2f5nlm47SDkiPl .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-rX2f5nlm47SDkiPl .rough-node .label,#mermaid-svg-rX2f5nlm47SDkiPl .node .label,#mermaid-svg-rX2f5nlm47SDkiPl .image-shape .label,#mermaid-svg-rX2f5nlm47SDkiPl .icon-shape .label{text-align:center;}#mermaid-svg-rX2f5nlm47SDkiPl .node.clickable{cursor:pointer;}#mermaid-svg-rX2f5nlm47SDkiPl .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-rX2f5nlm47SDkiPl .arrowheadPath{fill:#333333;}#mermaid-svg-rX2f5nlm47SDkiPl .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-rX2f5nlm47SDkiPl .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-rX2f5nlm47SDkiPl .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-rX2f5nlm47SDkiPl .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-rX2f5nlm47SDkiPl .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-rX2f5nlm47SDkiPl .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-rX2f5nlm47SDkiPl .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-rX2f5nlm47SDkiPl .cluster text{fill:#333;}#mermaid-svg-rX2f5nlm47SDkiPl .cluster span{color:#333;}#mermaid-svg-rX2f5nlm47SDkiPl div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-rX2f5nlm47SDkiPl .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-rX2f5nlm47SDkiPl rect.text{fill:none;stroke-width:0;}#mermaid-svg-rX2f5nlm47SDkiPl .icon-shape,#mermaid-svg-rX2f5nlm47SDkiPl .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-rX2f5nlm47SDkiPl .icon-shape p,#mermaid-svg-rX2f5nlm47SDkiPl .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-rX2f5nlm47SDkiPl .icon-shape .label rect,#mermaid-svg-rX2f5nlm47SDkiPl .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-rX2f5nlm47SDkiPl .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-rX2f5nlm47SDkiPl .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-rX2f5nlm47SDkiPl :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;}#mermaid-svg-rX2f5nlm47SDkiPl .primary>*{fill:#4CAF50!important;stroke:#388E3C!important;color:#fff!important;}#mermaid-svg-rX2f5nlm47SDkiPl .primary span{fill:#4CAF50!important;stroke:#388E3C!important;color:#fff!important;}#mermaid-svg-rX2f5nlm47SDkiPl .primary tspan{fill:#fff!important;}#mermaid-svg-rX2f5nlm47SDkiPl .warning>*{fill:#FF9800!important;stroke:#F57C00!important;color:#fff!important;}#mermaid-svg-rX2f5nlm47SDkiPl .warning span{fill:#FF9800!important;stroke:#F57C00!important;color:#fff!important;}#mermaid-svg-rX2f5nlm47SDkiPl .warning tspan{fill:#fff!important;}#mermaid-svg-rX2f5nlm47SDkiPl .error>*{fill:#F44336!important;stroke:#D32F2F!important;color:#fff!important;}#mermaid-svg-rX2f5nlm47SDkiPl .error span{fill:#F44336!important;stroke:#D32F2F!important;color:#fff!important;}#mermaid-svg-rX2f5nlm47SDkiPl .error tspan{fill:#fff!important;}#mermaid-svg-rX2f5nlm47SDkiPl .info>*{fill:#2196F3!important;stroke:#1976D2!important;color:#fff!important;}#mermaid-svg-rX2f5nlm47SDkiPl .info span{fill:#2196F3!important;stroke:#1976D2!important;color:#fff!important;}#mermaid-svg-rX2f5nlm47SDkiPl .info tspan{fill:#fff!important;}#mermaid-svg-rX2f5nlm47SDkiPl .purple>*{fill:#9C27B0!important;stroke:#7B1FA2!important;color:#fff!important;}#mermaid-svg-rX2f5nlm47SDkiPl .purple span{fill:#9C27B0!important;stroke:#7B1FA2!important;color:#fff!important;}#mermaid-svg-rX2f5nlm47SDkiPl .purple tspan{fill:#fff!important;} 取消
确认
保留
清除
时间窗口有限
用户点击卸载
系统弹出确认对话框
用户确认
卸载取消
系统发送卸载广播
触发 onDisconnect 回调
执行数据清理逻辑
停止应用进程
删除应用文件
是否保留数据
保留应用沙箱外的数据
清除所有应用数据
卸载完成
2.2 卸载数据清理范围
#mermaid-svg-jz76Pv0hdIeLsKXQ{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-jz76Pv0hdIeLsKXQ .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-jz76Pv0hdIeLsKXQ .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-jz76Pv0hdIeLsKXQ .error-icon{fill:#552222;}#mermaid-svg-jz76Pv0hdIeLsKXQ .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-jz76Pv0hdIeLsKXQ .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-jz76Pv0hdIeLsKXQ .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-jz76Pv0hdIeLsKXQ .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-jz76Pv0hdIeLsKXQ .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-jz76Pv0hdIeLsKXQ .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-jz76Pv0hdIeLsKXQ .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-jz76Pv0hdIeLsKXQ .marker{fill:#333333;stroke:#333333;}#mermaid-svg-jz76Pv0hdIeLsKXQ .marker.cross{stroke:#333333;}#mermaid-svg-jz76Pv0hdIeLsKXQ svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-jz76Pv0hdIeLsKXQ p{margin:0;}#mermaid-svg-jz76Pv0hdIeLsKXQ .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-jz76Pv0hdIeLsKXQ .cluster-label text{fill:#333;}#mermaid-svg-jz76Pv0hdIeLsKXQ .cluster-label span{color:#333;}#mermaid-svg-jz76Pv0hdIeLsKXQ .cluster-label span p{background-color:transparent;}#mermaid-svg-jz76Pv0hdIeLsKXQ .label text,#mermaid-svg-jz76Pv0hdIeLsKXQ span{fill:#333;color:#333;}#mermaid-svg-jz76Pv0hdIeLsKXQ .node rect,#mermaid-svg-jz76Pv0hdIeLsKXQ .node circle,#mermaid-svg-jz76Pv0hdIeLsKXQ .node ellipse,#mermaid-svg-jz76Pv0hdIeLsKXQ .node polygon,#mermaid-svg-jz76Pv0hdIeLsKXQ .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-jz76Pv0hdIeLsKXQ .rough-node .label text,#mermaid-svg-jz76Pv0hdIeLsKXQ .node .label text,#mermaid-svg-jz76Pv0hdIeLsKXQ .image-shape .label,#mermaid-svg-jz76Pv0hdIeLsKXQ .icon-shape .label{text-anchor:middle;}#mermaid-svg-jz76Pv0hdIeLsKXQ .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-jz76Pv0hdIeLsKXQ .rough-node .label,#mermaid-svg-jz76Pv0hdIeLsKXQ .node .label,#mermaid-svg-jz76Pv0hdIeLsKXQ .image-shape .label,#mermaid-svg-jz76Pv0hdIeLsKXQ .icon-shape .label{text-align:center;}#mermaid-svg-jz76Pv0hdIeLsKXQ .node.clickable{cursor:pointer;}#mermaid-svg-jz76Pv0hdIeLsKXQ .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-jz76Pv0hdIeLsKXQ .arrowheadPath{fill:#333333;}#mermaid-svg-jz76Pv0hdIeLsKXQ .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-jz76Pv0hdIeLsKXQ .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-jz76Pv0hdIeLsKXQ .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-jz76Pv0hdIeLsKXQ .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-jz76Pv0hdIeLsKXQ .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-jz76Pv0hdIeLsKXQ .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-jz76Pv0hdIeLsKXQ .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-jz76Pv0hdIeLsKXQ .cluster text{fill:#333;}#mermaid-svg-jz76Pv0hdIeLsKXQ .cluster span{color:#333;}#mermaid-svg-jz76Pv0hdIeLsKXQ div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-jz76Pv0hdIeLsKXQ .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-jz76Pv0hdIeLsKXQ rect.text{fill:none;stroke-width:0;}#mermaid-svg-jz76Pv0hdIeLsKXQ .icon-shape,#mermaid-svg-jz76Pv0hdIeLsKXQ .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-jz76Pv0hdIeLsKXQ .icon-shape p,#mermaid-svg-jz76Pv0hdIeLsKXQ .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-jz76Pv0hdIeLsKXQ .icon-shape .label rect,#mermaid-svg-jz76Pv0hdIeLsKXQ .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-jz76Pv0hdIeLsKXQ .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-jz76Pv0hdIeLsKXQ .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-jz76Pv0hdIeLsKXQ :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;}#mermaid-svg-jz76Pv0hdIeLsKXQ .primary>*{fill:#4CAF50!important;stroke:#388E3C!important;color:#fff!important;}#mermaid-svg-jz76Pv0hdIeLsKXQ .primary span{fill:#4CAF50!important;stroke:#388E3C!important;color:#fff!important;}#mermaid-svg-jz76Pv0hdIeLsKXQ .primary tspan{fill:#fff!important;}#mermaid-svg-jz76Pv0hdIeLsKXQ .warning>*{fill:#FF9800!important;stroke:#F57C00!important;color:#fff!important;}#mermaid-svg-jz76Pv0hdIeLsKXQ .warning span{fill:#FF9800!important;stroke:#F57C00!important;color:#fff!important;}#mermaid-svg-jz76Pv0hdIeLsKXQ .warning tspan{fill:#fff!important;}#mermaid-svg-jz76Pv0hdIeLsKXQ .error>*{fill:#F44336!important;stroke:#D32F2F!important;color:#fff!important;}#mermaid-svg-jz76Pv0hdIeLsKXQ .error span{fill:#F44336!important;stroke:#D32F2F!important;color:#fff!important;}#mermaid-svg-jz76Pv0hdIeLsKXQ .error tspan{fill:#fff!important;}#mermaid-svg-jz76Pv0hdIeLsKXQ .info>*{fill:#2196F3!important;stroke:#1976D2!important;color:#fff!important;}#mermaid-svg-jz76Pv0hdIeLsKXQ .info span{fill:#2196F3!important;stroke:#1976D2!important;color:#fff!important;}#mermaid-svg-jz76Pv0hdIeLsKXQ .info tspan{fill:#fff!important;}#mermaid-svg-jz76Pv0hdIeLsKXQ .purple>*{fill:#9C27B0!important;stroke:#7B1FA2!important;color:#fff!important;}#mermaid-svg-jz76Pv0hdIeLsKXQ .purple span{fill:#9C27B0!important;stroke:#7B1FA2!important;color:#fff!important;}#mermaid-svg-jz76Pv0hdIeLsKXQ .purple tspan{fill:#fff!important;} 需要主动清理的数据
后台代理
BackgroundTask
通知订阅
Notification
定时器
ReminderAgent
系统监听
SystemEvent
沙箱外数据 - 卸载时可能保留
分布式数据
distributedData/
公共目录文件
MediaLibrary/
云同步数据
CloudSync/
系统设置
Settings/
沙箱内数据 - 卸载时自动清除
应用文件目录
files/
缓存目录
cache/
临时目录
temp/
偏好设置
preferences/
数据库
database/
2.3 卸载与重装数据保留策略
| 数据类型 | 卸载时 | 重装后 | 保留方式 |
|---|---|---|---|
| 应用沙箱内文件 | ❌ 清除 | ❌ 不可恢复 | 系统自动清除 |
| 偏好设置 | ❌ 清除 | ❌ 不可恢复 | 系统自动清除 |
| 本地数据库 | ❌ 清除 | ❌ 不可恢复 | 系统自动清除 |
| 云端数据 | ✅ 保留 | ✅ 可恢复 | 服务器存储 |
| 分布式数据 | ⚠️ 取决于配置 | ⚠️ 取决于配置 | 跨设备同步 |
| 公共媒体文件 | ✅ 保留 | ✅ 可恢复 | 公共目录 |
| 应用账号信息 | ✅ 保留 | ✅ 可恢复 | 系统账号框架 |
核心原则:沙箱内的数据随卸载而清除,沙箱外的数据需要开发者主动管理。
三、代码实战
3.1 卸载监听与数据清理
typescript
import { UIAbility, AbilityConstant, Want } from '@kit.AbilityKit';
import { bundleManager } from '@kit.AbilityKit';
import { window } from '@kit.ArkUI';
import { hilog } from '@kit.PerformanceAnalysisKit';
import { reminderAgentManager } from '@kit.ReminderAgentKit';
import { notificationManager } from '@kit.NotificationKit';
import { backgroundTaskManager } from '@kit.BackgroundTasksKit';
const TAG = '[UninstallHandler]';
/**
* 卸载监听与数据清理 Ability
*
* 注意:HarmonyOS 中没有直接的"卸载回调"给被卸载的应用本身
* 但可以通过以下方式间接实现:
* 1. 使用 ExtensionAbility(如 ServiceExtension)监听其他应用的卸载
* 2. 使用 bundleManager 的监听接口
* 3. 在 onDisconnect 中做最后的清理
*/
export default class UninstallAwareAbility extends UIAbility {
// 注册的定时器ID列表
private reminderIds: number[] = [];
// 后台任务请求ID
private bgTaskId: number = -1;
// 通知订阅标签
private notificationSlot: string = 'default';
onCreate(want: Want, launchParam: AbilityConstant.LaunchParam): void {
hilog.info(0x0001, TAG, 'onCreate');
// 注册应用变更监听(监听其他应用的安装/卸载)
this.registerBundleStatusListener();
// 注册需要清理的系统资源
this.registerSystemResources();
}
onWindowStageCreate(windowStage: window.WindowStage): void {
windowStage.loadContent('pages/Index');
}
/**
* 注册应用包状态变更监听
* 可以监听到其他应用的安装、更新、卸载事件
*/
private registerBundleStatusListener() {
try {
const callback = (bundleStatus: bundleManager.BundleStatus) => {
const bundleName = bundleStatus.bundleName;
const status = bundleStatus.status;
// status 类型:
// ENABLED = 1 - 已启用
// DISABLED = 2 - 已禁用
// UPDATED = 3 - 已更新
// UNINSTALLED = 4 - 已卸载
if (status === bundleManager.BundleStatus.UNINSTALLED) {
hilog.info(0x0001, TAG, `应用已卸载: ${bundleName}`);
this.onOtherAppUninstalled(bundleName);
} else if (status === bundleManager.BundleStatus.UPDATED) {
hilog.info(0x0001, TAG, `应用已更新: ${bundleName}`);
this.onOtherAppUpdated(bundleName);
} else if (status === bundleManager.BundleStatus.ENABLED) {
hilog.info(0x0001, TAG, `应用已安装: ${bundleName}`);
this.onOtherAppInstalled(bundleName);
}
};
// 注册监听
bundleManager.on('bundleStatusChange', callback);
hilog.info(0x0001, TAG, '应用包状态监听已注册');
} catch (err) {
hilog.error(0x0001, TAG, `注册监听失败: ${JSON.stringify(err)}`);
}
}
/**
* 其他应用被卸载时的处理
*/
private onOtherAppUninstalled(bundleName: string) {
// 清理与该应用相关的数据
// 例如:清理与该应用的聊天记录缓存
hilog.info(0x0001, TAG, `清理与 ${bundleName} 相关的数据`);
// 如果该应用是你的服务的依赖方,可能需要:
// 1. 解除账号绑定
// 2. 清理共享数据
// 3. 取消关联的后台任务
}
/**
* 注册需要清理的系统资源
* 在应用被卸载前,这些资源需要被主动清理
*/
private registerSystemResources() {
// 注册后台提醒(如闹钟、定时提醒)
this.registerReminder();
// 注册通知渠道
this.registerNotificationSlot();
// 注册后台长时任务
this.registerBackgroundTask();
}
/**
* 注册后台提醒
*/
private async registerReminder() {
try {
const reminder: reminderAgentManager.ReminderRequestTimer = {
reminderType: reminderAgentManager.ReminderType.REMINDER_TYPE_TIMER,
triggerTimeInSeconds: 3600, // 1小时后触发
actionButton: [
{
title: '查看',
type: reminderAgentManager.ActionButtonType.ACTION_BUTTON_TYPE_CUSTOM
}
],
wantAgent: {
pkgName: 'com.example.app',
abilityName: 'MainAbility'
},
title: '提醒标题',
content: '提醒内容'
};
const reminderId = await reminderAgentManager.publishReminder(reminder);
this.reminderIds.push(reminderId);
hilog.info(0x0001, TAG, `注册提醒成功, ID: ${reminderId}`);
} catch (err) {
hilog.error(0x0001, TAG, `注册提醒失败: ${JSON.stringify(err)}`);
}
}
/**
* 注册通知渠道
*/
private async registerNotificationSlot() {
try {
const slot: notificationManager.NotificationSlot = {
type: notificationManager.SlotType.SOCIAL_COMMUNICATION,
level: notificationManager.SlotLevel.LEVEL_DEFAULT
};
await notificationManager.addSlot(slot);
hilog.info(0x0001, TAG, '通知渠道已注册');
} catch (err) {
hilog.error(0x0001, TAG, `注册通知渠道失败: ${JSON.stringify(err)}`);
}
}
/**
* 注册后台长时任务
*/
private async registerBackgroundTask() {
try {
const bgMode: backgroundTaskManager.BackgroundMode =
backgroundTaskManager.BackgroundMode.DATA_TRANSFER;
// 注意:实际使用需要申请 ohos.permission.KEEP_BACKGROUND_RUNNING 权限
hilog.info(0x0001, TAG, '后台任务已注册');
} catch (err) {
hilog.error(0x0001, TAG, `注册后台任务失败: ${JSON.stringify(err)}`);
}
}
/**
* 清理所有注册的系统资源
* 在 onDisconnect 或 onDestroy 中调用
*/
private async cleanupSystemResources() {
hilog.info(0x0001, TAG, '开始清理系统资源');
// 1. 取消所有后台提醒
for (const reminderId of this.reminderIds) {
try {
await reminderAgentManager.cancelReminder(reminderId);
hilog.info(0x0001, TAG, `取消提醒: ${reminderId}`);
} catch (err) {
hilog.error(0x0001, TAG, `取消提醒失败: ${reminderId}`);
}
}
// 2. 取消所有通知
try {
await notificationManager.cancelAll();
hilog.info(0x0001, TAG, '已取消所有通知');
} catch (err) {
hilog.error(0x0001, TAG, `取消通知失败: ${JSON.stringify(err)}`);
}
// 3. 取消后台长时任务
if (this.bgTaskId >= 0) {
try {
backgroundTaskManager.cancelSuspendDelay(this.bgTaskId);
hilog.info(0x0001, TAG, '已取消后台任务');
} catch (err) {
hilog.error(0x0001, TAG, `取消后台任务失败: ${JSON.stringify(err)}`);
}
}
// 4. 移除应用包状态监听
try {
bundleManager.off('bundleStatusChange');
hilog.info(0x0001, TAG, '已移除应用包状态监听');
} catch (err) {
hilog.error(0x0001, TAG, `移除监听失败: ${JSON.stringify(err)}`);
}
hilog.info(0x0001, TAG, '系统资源清理完成');
}
onDisconnect(): void {
hilog.info(0x0001, TAG, 'onDisconnect - 执行清理');
this.cleanupSystemResources();
}
onDestroy(): void {
hilog.info(0x0001, TAG, 'onDestroy - 最终清理');
this.cleanupSystemResources();
}
onForeground(): void {}
onBackground(): void {}
}
3.2 卸载前数据安全擦除
typescript
import { fileIo } from '@kit.CoreFileKit';
import { hilog } from '@kit.PerformanceAnalysisKit';
const TAG = '[SecureCleanup]';
/**
* 安全数据擦除工具类
* 提供不同安全等级的数据擦除方案
*/
export class SecureDataCleaner {
private context: Context;
constructor(context: Context) {
this.context = context;
}
/**
* 安全等级枚举
*/
enum SecurityLevel {
BASIC = 0, // 基础清理:直接删除文件
STANDARD = 1, // 标准清理:覆写后删除
ENHANCED = 2 // 增强清理:多次覆写后删除
}
/**
* 执行安全清理
* @param level 安全等级
*/
async secureClean(level: SecurityLevel): Promise<boolean> {
hilog.info(0x0001, TAG, `开始安全清理,等级: ${level}`);
try {
// 1. 清理应用文件目录
await this.cleanFilesDir(level);
// 2. 清理缓存目录
await this.cleanCacheDir(level);
// 3. 清理临时目录
await this.cleanTempDir(level);
// 4. 清理偏好设置
await this.cleanPreferences();
// 5. 清理数据库
await this.cleanDatabase();
hilog.info(0x0001, TAG, '安全清理完成');
return true;
} catch (err) {
hilog.error(0x0001, TAG, `安全清理失败: ${JSON.stringify(err)}`);
return false;
}
}
/**
* 安全删除文件
* 根据安全等级执行不同的擦除策略
*/
private async secureDeleteFile(filePath: string, level: SecurityLevel): Promise<void> {
try {
if (level === SecurityLevel.BASIC) {
// 基础清理:直接删除
await fileIo.unlink(filePath);
hilog.info(0x0001, TAG, `基础删除: ${filePath}`);
} else if (level === SecurityLevel.STANDARD) {
// 标准清理:先覆写一次再删除
await this.overwriteFile(filePath, 1);
await fileIo.unlink(filePath);
hilog.info(0x0001, TAG, `标准删除: ${filePath}`);
} else if (level === SecurityLevel.ENHANCED) {
// 增强清理:多次覆写后删除(DoD 5220.22-M 简化版)
await this.overwriteFile(filePath, 3);
await fileIo.unlink(filePath);
hilog.info(0x0001, TAG, `增强删除: ${filePath}`);
}
} catch (err) {
hilog.error(0x0001, TAG, `删除文件失败: ${filePath}, ${JSON.stringify(err)}`);
}
}
/**
* 覆写文件内容
* @param filePath 文件路径
* @param times 覆写次数
*/
private async overwriteFile(filePath: string, times: number): Promise<void> {
for (let i = 0; i < times; i++) {
try {
// 获取文件大小
const stat = await fileIo.stat(filePath);
const fileSize = stat.size;
// 生成随机数据覆写
const randomData = new Uint8Array(fileSize);
// 填充随机数据(简化处理,实际应使用加密安全的随机数)
for (let j = 0; j < fileSize; j++) {
randomData[j] = Math.floor(Math.random() * 256);
}
// 写入覆写数据
const file = await fileIo.open(filePath, fileIo.OpenMode.WRITE_ONLY);
await fileIo.write(file.fd, randomData);
await fileIo.close(file.fd);
} catch (err) {
hilog.warn(0x0001, TAG, `第${i + 1}次覆写失败: ${filePath}`);
}
}
}
/**
* 清理文件目录
*/
private async cleanFilesDir(level: SecurityLevel): Promise<void> {
try {
const filesDir = this.context.filesDir;
const files = await fileIo.listFile(filesDir);
for (const file of files) {
await this.secureDeleteFile(`${filesDir}/${file}`, level);
}
hilog.info(0x0001, TAG, '文件目录已清理');
} catch (err) {
hilog.error(0x0001, TAG, `清理文件目录失败: ${JSON.stringify(err)}`);
}
}
/**
* 清理缓存目录
*/
private async cleanCacheDir(level: SecurityLevel): Promise<void> {
try {
const cacheDir = this.context.cacheDir;
const files = await fileIo.listFile(cacheDir);
for (const file of files) {
await this.secureDeleteFile(`${cacheDir}/${file}`, level);
}
hilog.info(0x0001, TAG, '缓存目录已清理');
} catch (err) {
hilog.error(0x0001, TAG, `清理缓存目录失败: ${JSON.stringify(err)}`);
}
}
/**
* 清理临时目录
*/
private async cleanTempDir(level: SecurityLevel): Promise<void> {
try {
const tempDir = this.context.tempDir;
const files = await fileIo.listFile(tempDir);
for (const file of files) {
await this.secureDeleteFile(`${tempDir}/${file}`, level);
}
hilog.info(0x0001, TAG, '临时目录已清理');
} catch (err) {
hilog.error(0x0001, TAG, `清理临时目录失败: ${JSON.stringify(err)}`);
}
}
/**
* 清理偏好设置
*/
private async cleanPreferences(): Promise<void> {
try {
// 偏好设置随应用卸载自动清除
// 但如果有跨设备同步的数据,需要主动清理
hilog.info(0x0001, TAG, '偏好设置将随卸载自动清除');
} catch (err) {
hilog.error(0x0001, TAG, `清理偏好设置失败: ${JSON.stringify(err)}`);
}
}
/**
* 清理数据库
*/
private async cleanDatabase(): Promise<void> {
try {
// 数据库随应用卸载自动清除
// 但如果有分布式数据库副本,需要主动清理
hilog.info(0x0001, TAG, '本地数据库将随卸载自动清除');
} catch (err) {
hilog.error(0x0001, TAG, `清理数据库失败: ${JSON.stringify(err)}`);
}
}
}
3.3 卸载与重装数据保留方案
typescript
import { UIAbility, AbilityConstant, Want } from '@kit.AbilityKit';
import { cloudDatabase } from '@kit.CloudKit';
import { preferences } from '@kit.ArkData';
import { hilog } from '@kit.PerformanceAnalysisKit';
const TAG = '[ReinstallData]';
/**
* 卸载重装数据保留方案
*
* 核心思路:
* 1. 关键数据上传云端 → 卸载不影响
* 2. 使用应用账号框架 → 账号信息不随卸载清除
* 3. 分布式数据 → 跨设备同步,单设备卸载不影响
*/
export default class ReinstallDataAbility extends UIAbility {
// 需要跨卸载保留的数据键列表
private readonly PRESERVED_KEYS = [
'user_settings',
'purchase_records',
'achievement_data',
'favorite_list'
];
onCreate(want: Want, launchParam: AbilityConstant.LaunchParam): void {
hilog.info(0x0001, TAG, 'onCreate');
// 检查是否为重装启动
this.checkReinstallStatus();
}
onWindowStageCreate(windowStage: window.WindowStage): void {
windowStage.loadContent('pages/Index');
}
/**
* 检查是否为重装启动
* 通过云端数据判断
*/
private async checkReinstallStatus() {
try {
// 检查云端是否有该用户的历史数据
const hasCloudData = await this.checkCloudData();
if (hasCloudData) {
hilog.info(0x0001, TAG, '检测到云端历史数据,可能是重装用户');
// 提示用户是否恢复数据
this.promptDataRecovery();
} else {
hilog.info(0x0001, TAG, '新用户或无历史数据');
}
} catch (err) {
hilog.error(0x0001, TAG, `检查重装状态失败: ${JSON.stringify(err)}`);
}
}
/**
* 保存关键数据到云端
* 在 onBackground 中定期调用,确保数据最新
*/
async preserveDataToCloud() {
hilog.info(0x0001, TAG, '开始保存数据到云端');
try {
// 收集需要保留的数据
const preservedData: Record<string, string> = {};
for (const key of this.PRESERVED_KEYS) {
const value = AppStorage.get<string>(key);
if (value) {
preservedData[key] = value;
}
}
// 上传到云端(示意代码)
// 实际使用 cloudDatabase 或自定义云端接口
hilog.info(0x0001, TAG, `已保存 ${Object.keys(preservedData).length} 项数据到云端`);
} catch (err) {
hilog.error(0x0001, TAG, `保存云端数据失败: ${JSON.stringify(err)}`);
}
}
/**
* 从云端恢复数据
* 重装后首次启动时调用
*/
async restoreDataFromCloud() {
hilog.info(0x0001, TAG, '开始从云端恢复数据');
try {
// 从云端下载数据(示意代码)
const cloudData: Record<string, string> = {};
// 将数据写回 AppStorage
for (const [key, value] of Object.entries(cloudData)) {
AppStorage.setOrCreate(key, value);
}
hilog.info(0x0001, TAG, `已恢复 ${Object.keys(cloudData).length} 项数据`);
} catch (err) {
hilog.error(0x0001, TAG, `恢复云端数据失败: ${JSON.stringify(err)}`);
}
}
/**
* 检查云端是否有历史数据
*/
private async checkCloudData(): Promise<boolean> {
// 实际实现:调用云端接口检查
return false;
}
/**
* 提示用户恢复数据
*/
private promptDataRecovery() {
// 通过 AppStorage 通知 UI 层显示恢复提示
AppStorage.setOrCreate('showRecoveryPrompt', true);
}
onBackground(): void {
// 进入后台时保存数据到云端
this.preserveDataToCloud();
}
onForeground(): void {}
onDestroy(): void {}
}
// ============ UI 层:重装数据恢复界面 ============
@Entry
@Component
struct ReinstallRecoveryPage {
@StorageLink('showRecoveryPrompt') showRecoveryPrompt: boolean = false;
@State isRecovering: boolean = false;
@State recoveryProgress: number = 0;
@State recoveryComplete: boolean = false;
build() {
Column() {
Text('数据恢复')
.fontSize(24)
.fontWeight(FontWeight.Bold)
.margin({ bottom: 24 })
if (this.showRecoveryPrompt && !this.recoveryComplete) {
// 数据恢复提示卡片
Column() {
Text('检测到您之前的使用记录')
.fontSize(16)
.fontWeight(FontWeight.Medium)
.margin({ bottom: 8 })
Text('是否恢复您的历史数据?')
.fontSize(14)
.fontColor('#666666')
.margin({ bottom: 16 })
if (this.isRecovering) {
// 恢复进度
Progress({ value: this.recoveryProgress, total: 100, type: ProgressType.Linear })
.width('100%')
.color('#4CAF50')
.margin({ bottom: 8 })
Text(`恢复中... ${this.recoveryProgress}%`)
.fontSize(12)
.fontColor('#999999')
} else {
// 操作按钮
Row({ space: 12 }) {
Button('恢复数据')
.layoutWeight(1)
.backgroundColor('#4CAF50')
.fontColor('#FFFFFF')
.onClick(() => {
this.startRecovery();
})
Button('重新开始')
.layoutWeight(1)
.backgroundColor('#F5F5F5')
.fontColor('#333333')
.onClick(() => {
this.showRecoveryPrompt = false;
})
}
}
}
.width('100%')
.padding(20)
.backgroundColor('#FFFFFF')
.borderRadius(12)
.shadow({ radius: 8, color: '#1A000000', offsetY: 2 })
}
if (this.recoveryComplete) {
// 恢复完成提示
Column() {
Text('✓ 数据恢复完成')
.fontSize(18)
.fontWeight(FontWeight.Bold)
.fontColor('#4CAF50')
.margin({ bottom: 8 })
Text('您的设置和数据已成功恢复')
.fontSize(14)
.fontColor('#666666')
}
.width('100%')
.padding(20)
.backgroundColor('#E8F5E9')
.borderRadius(12)
}
}
.width('100%')
.height('100%')
.padding(16)
}
/**
* 开始数据恢复
*/
private async startRecovery() {
this.isRecovering = true;
this.recoveryProgress = 0;
// 模拟恢复进度
const steps = [
{ name: '恢复用户设置', progress: 25 },
{ name: '恢复购买记录', progress: 50 },
{ name: '恢复成就数据', progress: 75 },
{ name: '恢复收藏列表', progress: 100 }
];
for (const step of steps) {
// 模拟每步耗时
await new Promise<void>(resolve => setTimeout(resolve, 500));
this.recoveryProgress = step.progress;
}
this.isRecovering = false;
this.recoveryComplete = true;
this.showRecoveryPrompt = false;
}
}
四、踩坑与注意事项
4.1 卸载回调的时间窗口
HarmonyOS 中,被卸载的应用自身不会收到卸载回调 。系统直接终止进程并删除文件。所以你不能指望在 onDestroy 中做数据清理------因为 onDestroy 可能根本不会被调用。
正确的做法:
- 数据实时同步到云端,不要等到卸载时才保存
- 系统资源(定时器、通知、后台任务)在注册时就考虑清理策略
- 使用
bundleManager.on('bundleStatusChange')监听其他应用的卸载
4.2 分布式数据的卸载残留
分布式数据在多设备间同步,单设备卸载应用后,其他设备上的数据副本仍然存在。如果这些数据包含敏感信息,需要在卸载前主动清理。
typescript
// 在 onBackground 中检查是否需要清理分布式数据
onBackground(): void {
// 清理不再需要的分布式数据
this.cleanDistributedData();
}
4.3 后台代理的卸载残留
以下系统资源在应用卸载后可能不会自动清理:
| 资源类型 | 是否自动清理 | 处理方式 |
|---|---|---|
| 后台提醒(闹钟) | ❌ 不自动清理 | 需主动取消 |
| 通知订阅 | ⚠️ 部分清理 | 建议主动取消 |
| 后台长时任务 | ✅ 自动清理 | 系统终止时清理 |
| 系统事件监听 | ✅ 自动清理 | 进程终止时清理 |
踩坑:如果你的应用注册了闹钟或定时提醒,卸载后这些提醒仍然会触发,但对应的 Ability 已经不存在了,用户会看到"应用未找到"的提示。这非常影响用户体验。
4.4 安全擦除的性能问题
增强级安全擦除(多次覆写)对于大文件来说非常耗时。在实际项目中,应该根据数据敏感度选择合适的擦除等级:
- 普通缓存数据:基础清理即可
- 用户个人信息:标准清理
- 金融/医疗等高敏感数据:增强清理
4.5 重装数据恢复的隐私合规
从云端恢复数据时,必须遵守隐私法规:
- 明确告知用户:哪些数据会被保留,保留多久
- 用户同意:恢复数据前需要用户明确同意
- 数据最小化:只保留必要的数据,不要"什么都存"
- 删除权:用户有权要求彻底删除云端数据
五、HarmonyOS 6 适配
5.1 卸载通知机制增强
HarmonyOS 6 新增了更完善的卸载通知机制:
| 新增能力 | 说明 |
|---|---|
onBundleRemoved |
系统级卸载回调,通知所有已注册的应用 |
preUninstallHook |
预卸载钩子,允许在卸载前执行清理逻辑 |
uninstallDataPolicy |
卸载数据策略配置,声明式指定保留/清除规则 |
5.2 声明式数据保留配置
HarmonyOS 6 支持在 module.json5 中声明卸载数据保留策略:
json
{
"module": {
"uninstallDataPolicy": {
"preserveCloudData": true,
"preserveAccountBinding": true,
"clearLocalCache": true,
"secureWipeLevel": "standard"
}
}
}
5.3 安全擦除标准升级
HarmonyOS 6 的安全擦除符合国家标准 GB/T 35273:
- 基础清理:直接删除
- 标准清理:1次随机覆写 + 删除
- 增强清理:3次覆写(全0、全1、随机)+ 删除
- 军事级:7次覆写(DoD 5220.22-M 标准)
六、总结
#mermaid-svg-jxyl1jPNr2mu4xyu{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-jxyl1jPNr2mu4xyu .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-jxyl1jPNr2mu4xyu .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-jxyl1jPNr2mu4xyu .error-icon{fill:#552222;}#mermaid-svg-jxyl1jPNr2mu4xyu .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-jxyl1jPNr2mu4xyu .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-jxyl1jPNr2mu4xyu .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-jxyl1jPNr2mu4xyu .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-jxyl1jPNr2mu4xyu .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-jxyl1jPNr2mu4xyu .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-jxyl1jPNr2mu4xyu .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-jxyl1jPNr2mu4xyu .marker{fill:#333333;stroke:#333333;}#mermaid-svg-jxyl1jPNr2mu4xyu .marker.cross{stroke:#333333;}#mermaid-svg-jxyl1jPNr2mu4xyu svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-jxyl1jPNr2mu4xyu p{margin:0;}#mermaid-svg-jxyl1jPNr2mu4xyu .edge{stroke-width:3;}#mermaid-svg-jxyl1jPNr2mu4xyu .section--1 rect,#mermaid-svg-jxyl1jPNr2mu4xyu .section--1 path,#mermaid-svg-jxyl1jPNr2mu4xyu .section--1 circle,#mermaid-svg-jxyl1jPNr2mu4xyu .section--1 polygon,#mermaid-svg-jxyl1jPNr2mu4xyu .section--1 path{fill:hsl(240, 100%, 76.2745098039%);}#mermaid-svg-jxyl1jPNr2mu4xyu .section--1 text{fill:#ffffff;}#mermaid-svg-jxyl1jPNr2mu4xyu .node-icon--1{font-size:40px;color:#ffffff;}#mermaid-svg-jxyl1jPNr2mu4xyu .section-edge--1{stroke:hsl(240, 100%, 76.2745098039%);}#mermaid-svg-jxyl1jPNr2mu4xyu .edge-depth--1{stroke-width:17;}#mermaid-svg-jxyl1jPNr2mu4xyu .section--1 line{stroke:hsl(60, 100%, 86.2745098039%);stroke-width:3;}#mermaid-svg-jxyl1jPNr2mu4xyu .disabled,#mermaid-svg-jxyl1jPNr2mu4xyu .disabled circle,#mermaid-svg-jxyl1jPNr2mu4xyu .disabled text{fill:lightgray;}#mermaid-svg-jxyl1jPNr2mu4xyu .disabled text{fill:#efefef;}#mermaid-svg-jxyl1jPNr2mu4xyu .section-0 rect,#mermaid-svg-jxyl1jPNr2mu4xyu .section-0 path,#mermaid-svg-jxyl1jPNr2mu4xyu .section-0 circle,#mermaid-svg-jxyl1jPNr2mu4xyu .section-0 polygon,#mermaid-svg-jxyl1jPNr2mu4xyu .section-0 path{fill:hsl(60, 100%, 73.5294117647%);}#mermaid-svg-jxyl1jPNr2mu4xyu .section-0 text{fill:black;}#mermaid-svg-jxyl1jPNr2mu4xyu .node-icon-0{font-size:40px;color:black;}#mermaid-svg-jxyl1jPNr2mu4xyu .section-edge-0{stroke:hsl(60, 100%, 73.5294117647%);}#mermaid-svg-jxyl1jPNr2mu4xyu .edge-depth-0{stroke-width:14;}#mermaid-svg-jxyl1jPNr2mu4xyu .section-0 line{stroke:hsl(240, 100%, 83.5294117647%);stroke-width:3;}#mermaid-svg-jxyl1jPNr2mu4xyu .disabled,#mermaid-svg-jxyl1jPNr2mu4xyu .disabled circle,#mermaid-svg-jxyl1jPNr2mu4xyu .disabled text{fill:lightgray;}#mermaid-svg-jxyl1jPNr2mu4xyu .disabled text{fill:#efefef;}#mermaid-svg-jxyl1jPNr2mu4xyu .section-1 rect,#mermaid-svg-jxyl1jPNr2mu4xyu .section-1 path,#mermaid-svg-jxyl1jPNr2mu4xyu .section-1 circle,#mermaid-svg-jxyl1jPNr2mu4xyu .section-1 polygon,#mermaid-svg-jxyl1jPNr2mu4xyu .section-1 path{fill:hsl(80, 100%, 76.2745098039%);}#mermaid-svg-jxyl1jPNr2mu4xyu .section-1 text{fill:black;}#mermaid-svg-jxyl1jPNr2mu4xyu .node-icon-1{font-size:40px;color:black;}#mermaid-svg-jxyl1jPNr2mu4xyu .section-edge-1{stroke:hsl(80, 100%, 76.2745098039%);}#mermaid-svg-jxyl1jPNr2mu4xyu .edge-depth-1{stroke-width:11;}#mermaid-svg-jxyl1jPNr2mu4xyu .section-1 line{stroke:hsl(260, 100%, 86.2745098039%);stroke-width:3;}#mermaid-svg-jxyl1jPNr2mu4xyu .disabled,#mermaid-svg-jxyl1jPNr2mu4xyu .disabled circle,#mermaid-svg-jxyl1jPNr2mu4xyu .disabled text{fill:lightgray;}#mermaid-svg-jxyl1jPNr2mu4xyu .disabled text{fill:#efefef;}#mermaid-svg-jxyl1jPNr2mu4xyu .section-2 rect,#mermaid-svg-jxyl1jPNr2mu4xyu .section-2 path,#mermaid-svg-jxyl1jPNr2mu4xyu .section-2 circle,#mermaid-svg-jxyl1jPNr2mu4xyu .section-2 polygon,#mermaid-svg-jxyl1jPNr2mu4xyu .section-2 path{fill:hsl(270, 100%, 76.2745098039%);}#mermaid-svg-jxyl1jPNr2mu4xyu .section-2 text{fill:#ffffff;}#mermaid-svg-jxyl1jPNr2mu4xyu .node-icon-2{font-size:40px;color:#ffffff;}#mermaid-svg-jxyl1jPNr2mu4xyu .section-edge-2{stroke:hsl(270, 100%, 76.2745098039%);}#mermaid-svg-jxyl1jPNr2mu4xyu .edge-depth-2{stroke-width:8;}#mermaid-svg-jxyl1jPNr2mu4xyu .section-2 line{stroke:hsl(90, 100%, 86.2745098039%);stroke-width:3;}#mermaid-svg-jxyl1jPNr2mu4xyu .disabled,#mermaid-svg-jxyl1jPNr2mu4xyu .disabled circle,#mermaid-svg-jxyl1jPNr2mu4xyu .disabled text{fill:lightgray;}#mermaid-svg-jxyl1jPNr2mu4xyu .disabled text{fill:#efefef;}#mermaid-svg-jxyl1jPNr2mu4xyu .section-3 rect,#mermaid-svg-jxyl1jPNr2mu4xyu .section-3 path,#mermaid-svg-jxyl1jPNr2mu4xyu .section-3 circle,#mermaid-svg-jxyl1jPNr2mu4xyu .section-3 polygon,#mermaid-svg-jxyl1jPNr2mu4xyu .section-3 path{fill:hsl(300, 100%, 76.2745098039%);}#mermaid-svg-jxyl1jPNr2mu4xyu .section-3 text{fill:black;}#mermaid-svg-jxyl1jPNr2mu4xyu .node-icon-3{font-size:40px;color:black;}#mermaid-svg-jxyl1jPNr2mu4xyu .section-edge-3{stroke:hsl(300, 100%, 76.2745098039%);}#mermaid-svg-jxyl1jPNr2mu4xyu .edge-depth-3{stroke-width:5;}#mermaid-svg-jxyl1jPNr2mu4xyu .section-3 line{stroke:hsl(120, 100%, 86.2745098039%);stroke-width:3;}#mermaid-svg-jxyl1jPNr2mu4xyu .disabled,#mermaid-svg-jxyl1jPNr2mu4xyu .disabled circle,#mermaid-svg-jxyl1jPNr2mu4xyu .disabled text{fill:lightgray;}#mermaid-svg-jxyl1jPNr2mu4xyu .disabled text{fill:#efefef;}#mermaid-svg-jxyl1jPNr2mu4xyu .section-4 rect,#mermaid-svg-jxyl1jPNr2mu4xyu .section-4 path,#mermaid-svg-jxyl1jPNr2mu4xyu .section-4 circle,#mermaid-svg-jxyl1jPNr2mu4xyu .section-4 polygon,#mermaid-svg-jxyl1jPNr2mu4xyu .section-4 path{fill:hsl(330, 100%, 76.2745098039%);}#mermaid-svg-jxyl1jPNr2mu4xyu .section-4 text{fill:black;}#mermaid-svg-jxyl1jPNr2mu4xyu .node-icon-4{font-size:40px;color:black;}#mermaid-svg-jxyl1jPNr2mu4xyu .section-edge-4{stroke:hsl(330, 100%, 76.2745098039%);}#mermaid-svg-jxyl1jPNr2mu4xyu .edge-depth-4{stroke-width:2;}#mermaid-svg-jxyl1jPNr2mu4xyu .section-4 line{stroke:hsl(150, 100%, 86.2745098039%);stroke-width:3;}#mermaid-svg-jxyl1jPNr2mu4xyu .disabled,#mermaid-svg-jxyl1jPNr2mu4xyu .disabled circle,#mermaid-svg-jxyl1jPNr2mu4xyu .disabled text{fill:lightgray;}#mermaid-svg-jxyl1jPNr2mu4xyu .disabled text{fill:#efefef;}#mermaid-svg-jxyl1jPNr2mu4xyu .section-5 rect,#mermaid-svg-jxyl1jPNr2mu4xyu .section-5 path,#mermaid-svg-jxyl1jPNr2mu4xyu .section-5 circle,#mermaid-svg-jxyl1jPNr2mu4xyu .section-5 polygon,#mermaid-svg-jxyl1jPNr2mu4xyu .section-5 path{fill:hsl(0, 100%, 76.2745098039%);}#mermaid-svg-jxyl1jPNr2mu4xyu .section-5 text{fill:black;}#mermaid-svg-jxyl1jPNr2mu4xyu .node-icon-5{font-size:40px;color:black;}#mermaid-svg-jxyl1jPNr2mu4xyu .section-edge-5{stroke:hsl(0, 100%, 76.2745098039%);}#mermaid-svg-jxyl1jPNr2mu4xyu .edge-depth-5{stroke-width:-1;}#mermaid-svg-jxyl1jPNr2mu4xyu .section-5 line{stroke:hsl(180, 100%, 86.2745098039%);stroke-width:3;}#mermaid-svg-jxyl1jPNr2mu4xyu .disabled,#mermaid-svg-jxyl1jPNr2mu4xyu .disabled circle,#mermaid-svg-jxyl1jPNr2mu4xyu .disabled text{fill:lightgray;}#mermaid-svg-jxyl1jPNr2mu4xyu .disabled text{fill:#efefef;}#mermaid-svg-jxyl1jPNr2mu4xyu .section-6 rect,#mermaid-svg-jxyl1jPNr2mu4xyu .section-6 path,#mermaid-svg-jxyl1jPNr2mu4xyu .section-6 circle,#mermaid-svg-jxyl1jPNr2mu4xyu .section-6 polygon,#mermaid-svg-jxyl1jPNr2mu4xyu .section-6 path{fill:hsl(30, 100%, 76.2745098039%);}#mermaid-svg-jxyl1jPNr2mu4xyu .section-6 text{fill:black;}#mermaid-svg-jxyl1jPNr2mu4xyu .node-icon-6{font-size:40px;color:black;}#mermaid-svg-jxyl1jPNr2mu4xyu .section-edge-6{stroke:hsl(30, 100%, 76.2745098039%);}#mermaid-svg-jxyl1jPNr2mu4xyu .edge-depth-6{stroke-width:-4;}#mermaid-svg-jxyl1jPNr2mu4xyu .section-6 line{stroke:hsl(210, 100%, 86.2745098039%);stroke-width:3;}#mermaid-svg-jxyl1jPNr2mu4xyu .disabled,#mermaid-svg-jxyl1jPNr2mu4xyu .disabled circle,#mermaid-svg-jxyl1jPNr2mu4xyu .disabled text{fill:lightgray;}#mermaid-svg-jxyl1jPNr2mu4xyu .disabled text{fill:#efefef;}#mermaid-svg-jxyl1jPNr2mu4xyu .section-7 rect,#mermaid-svg-jxyl1jPNr2mu4xyu .section-7 path,#mermaid-svg-jxyl1jPNr2mu4xyu .section-7 circle,#mermaid-svg-jxyl1jPNr2mu4xyu .section-7 polygon,#mermaid-svg-jxyl1jPNr2mu4xyu .section-7 path{fill:hsl(90, 100%, 76.2745098039%);}#mermaid-svg-jxyl1jPNr2mu4xyu .section-7 text{fill:black;}#mermaid-svg-jxyl1jPNr2mu4xyu .node-icon-7{font-size:40px;color:black;}#mermaid-svg-jxyl1jPNr2mu4xyu .section-edge-7{stroke:hsl(90, 100%, 76.2745098039%);}#mermaid-svg-jxyl1jPNr2mu4xyu .edge-depth-7{stroke-width:-7;}#mermaid-svg-jxyl1jPNr2mu4xyu .section-7 line{stroke:hsl(270, 100%, 86.2745098039%);stroke-width:3;}#mermaid-svg-jxyl1jPNr2mu4xyu .disabled,#mermaid-svg-jxyl1jPNr2mu4xyu .disabled circle,#mermaid-svg-jxyl1jPNr2mu4xyu .disabled text{fill:lightgray;}#mermaid-svg-jxyl1jPNr2mu4xyu .disabled text{fill:#efefef;}#mermaid-svg-jxyl1jPNr2mu4xyu .section-8 rect,#mermaid-svg-jxyl1jPNr2mu4xyu .section-8 path,#mermaid-svg-jxyl1jPNr2mu4xyu .section-8 circle,#mermaid-svg-jxyl1jPNr2mu4xyu .section-8 polygon,#mermaid-svg-jxyl1jPNr2mu4xyu .section-8 path{fill:hsl(150, 100%, 76.2745098039%);}#mermaid-svg-jxyl1jPNr2mu4xyu .section-8 text{fill:black;}#mermaid-svg-jxyl1jPNr2mu4xyu .node-icon-8{font-size:40px;color:black;}#mermaid-svg-jxyl1jPNr2mu4xyu .section-edge-8{stroke:hsl(150, 100%, 76.2745098039%);}#mermaid-svg-jxyl1jPNr2mu4xyu .edge-depth-8{stroke-width:-10;}#mermaid-svg-jxyl1jPNr2mu4xyu .section-8 line{stroke:hsl(330, 100%, 86.2745098039%);stroke-width:3;}#mermaid-svg-jxyl1jPNr2mu4xyu .disabled,#mermaid-svg-jxyl1jPNr2mu4xyu .disabled circle,#mermaid-svg-jxyl1jPNr2mu4xyu .disabled text{fill:lightgray;}#mermaid-svg-jxyl1jPNr2mu4xyu .disabled text{fill:#efefef;}#mermaid-svg-jxyl1jPNr2mu4xyu .section-9 rect,#mermaid-svg-jxyl1jPNr2mu4xyu .section-9 path,#mermaid-svg-jxyl1jPNr2mu4xyu .section-9 circle,#mermaid-svg-jxyl1jPNr2mu4xyu .section-9 polygon,#mermaid-svg-jxyl1jPNr2mu4xyu .section-9 path{fill:hsl(180, 100%, 76.2745098039%);}#mermaid-svg-jxyl1jPNr2mu4xyu .section-9 text{fill:black;}#mermaid-svg-jxyl1jPNr2mu4xyu .node-icon-9{font-size:40px;color:black;}#mermaid-svg-jxyl1jPNr2mu4xyu .section-edge-9{stroke:hsl(180, 100%, 76.2745098039%);}#mermaid-svg-jxyl1jPNr2mu4xyu .edge-depth-9{stroke-width:-13;}#mermaid-svg-jxyl1jPNr2mu4xyu .section-9 line{stroke:hsl(0, 100%, 86.2745098039%);stroke-width:3;}#mermaid-svg-jxyl1jPNr2mu4xyu .disabled,#mermaid-svg-jxyl1jPNr2mu4xyu .disabled circle,#mermaid-svg-jxyl1jPNr2mu4xyu .disabled text{fill:lightgray;}#mermaid-svg-jxyl1jPNr2mu4xyu .disabled text{fill:#efefef;}#mermaid-svg-jxyl1jPNr2mu4xyu .section-10 rect,#mermaid-svg-jxyl1jPNr2mu4xyu .section-10 path,#mermaid-svg-jxyl1jPNr2mu4xyu .section-10 circle,#mermaid-svg-jxyl1jPNr2mu4xyu .section-10 polygon,#mermaid-svg-jxyl1jPNr2mu4xyu .section-10 path{fill:hsl(210, 100%, 76.2745098039%);}#mermaid-svg-jxyl1jPNr2mu4xyu .section-10 text{fill:black;}#mermaid-svg-jxyl1jPNr2mu4xyu .node-icon-10{font-size:40px;color:black;}#mermaid-svg-jxyl1jPNr2mu4xyu .section-edge-10{stroke:hsl(210, 100%, 76.2745098039%);}#mermaid-svg-jxyl1jPNr2mu4xyu .edge-depth-10{stroke-width:-16;}#mermaid-svg-jxyl1jPNr2mu4xyu .section-10 line{stroke:hsl(30, 100%, 86.2745098039%);stroke-width:3;}#mermaid-svg-jxyl1jPNr2mu4xyu .disabled,#mermaid-svg-jxyl1jPNr2mu4xyu .disabled circle,#mermaid-svg-jxyl1jPNr2mu4xyu .disabled text{fill:lightgray;}#mermaid-svg-jxyl1jPNr2mu4xyu .disabled text{fill:#efefef;}#mermaid-svg-jxyl1jPNr2mu4xyu .section-root rect,#mermaid-svg-jxyl1jPNr2mu4xyu .section-root path,#mermaid-svg-jxyl1jPNr2mu4xyu .section-root circle,#mermaid-svg-jxyl1jPNr2mu4xyu .section-root polygon{fill:hsl(240, 100%, 46.2745098039%);}#mermaid-svg-jxyl1jPNr2mu4xyu .section-root text{fill:#ffffff;}#mermaid-svg-jxyl1jPNr2mu4xyu .section-root span{color:#ffffff;}#mermaid-svg-jxyl1jPNr2mu4xyu .section-2 span{color:#ffffff;}#mermaid-svg-jxyl1jPNr2mu4xyu .icon-container{height:100%;display:flex;justify-content:center;align-items:center;}#mermaid-svg-jxyl1jPNr2mu4xyu .edge{fill:none;}#mermaid-svg-jxyl1jPNr2mu4xyu .mindmap-node-label{dy:1em;alignment-baseline:middle;text-anchor:middle;dominant-baseline:middle;text-align:center;}#mermaid-svg-jxyl1jPNr2mu4xyu :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 应用卸载
卸载监听
bundleManager.on
监听其他应用卸载
监听安装和更新
自身无卸载回调
数据需实时同步
资源需提前注册清理
数据清理
沙箱内数据
自动清除
无需手动处理
沙箱外数据
分布式数据需主动清理
云端数据需策略管理
系统资源
后台提醒需取消
通知订阅需取消
定时器需清除
安全擦除
基础清理
直接删除
标准清理
覆写1次+删除
增强清理
覆写3次+删除
重装数据保留
云端存储
关键数据实时上传
重装后自动检测
应用账号框架
账号信息不随卸载清除
分布式数据
跨设备副本保留
安全考量
敏感信息快照遮盖
后台代理残留清理
隐私合规
告知用户
获取同意
数据最小化
核心知识点回顾:
- 卸载监听 :被卸载的应用自身无回调,需通过
bundleManager.on('bundleStatusChange')监听其他应用 - 数据清理范围:沙箱内自动清除,沙箱外需主动清理,系统资源需逐个取消
- 安全擦除三级:基础(直接删除)、标准(覆写+删除)、增强(多次覆写+删除)
- 重装数据保留:云端存储是核心方案,配合应用账号框架和分布式数据
- 后台代理残留:闹钟、通知、定时器等不会随卸载自动清理,必须主动取消
- 隐私合规:数据保留需告知用户、获取同意、最小化原则
应用卸载是用户旅程的终点,但不是数据安全的终点。做好卸载场景的清理和保护,既是对用户负责,也是对开发者自身负责。