HarmonyOS APP开发中的应用卸载:卸载监听、数据清理与安全考量全指南
HarmonyOS APP开发中的应用卸载:卸载监听、数据清理与安全考量全指南
📌 核心要点:掌握 HarmonyOS 应用卸载的监听与回调机制,实现卸载前数据清理、卸载后数据保留(重装恢复),以及卸载场景下的安全防护策略。
一、背景与动机
你有没有这样的经历:卸载一个 App 后重新安装,发现登录态没了、设置全丢了、之前买的东西找不回来了?用户对此的感受是——“这 App 不靠谱”。而另一边,有些 App 卸载后,你的个人信息还残留在设备上,这又是另一种"不靠谱"——安全隐患。
应用卸载,看似只是用户点一下"确认卸载"就结束了,但对开发者来说,这是一场与时间的赛跑。你需要在极短的时间内完成数据清理、状态同步、安全擦除等一系列操作。更复杂的是,HarmonyOS 的分布式数据可能在多个设备上存在副本,卸载时是否要同步清理?用户卸载后重装,哪些数据应该保留?
这些问题不是"锦上添花",而是关乎用户体验和数据安全的"必答题"。
二、核心原理
2.1 应用卸载流程
2.2 卸载数据清理范围
2.3 卸载与重装数据保留策略
| 数据类型 | 卸载时 | 重装后 | 保留方式 |
|---|---|---|---|
| 应用沙箱内文件 | ❌ 清除 | ❌ 不可恢复 | 系统自动清除 |
| 偏好设置 | ❌ 清除 | ❌ 不可恢复 | 系统自动清除 |
| 本地数据库 | ❌ 清除 | ❌ 不可恢复 | 系统自动清除 |
| 云端数据 | ✅ 保留 | ✅ 可恢复 | 服务器存储 |
| 分布式数据 | ⚠️ 取决于配置 | ⚠️ 取决于配置 | 跨设备同步 |
| 公共媒体文件 | ✅ 保留 | ✅ 可恢复 | 公共目录 |
| 应用账号信息 | ✅ 保留 | ✅ 可恢复 | 系统账号框架 |
核心原则:沙箱内的数据随卸载而清除,沙箱外的数据需要开发者主动管理。
三、代码实战
3.1 卸载监听与数据清理
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 卸载前数据安全擦除
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 卸载与重装数据保留方案
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 分布式数据的卸载残留
分布式数据在多设备间同步,单设备卸载应用后,其他设备上的数据副本仍然存在。如果这些数据包含敏感信息,需要在卸载前主动清理。
// 在 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 中声明卸载数据保留策略:
{
"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 标准)
六、总结
核心知识点回顾:
- 卸载监听:被卸载的应用自身无回调,需通过
bundleManager.on('bundleStatusChange')监听其他应用 - 数据清理范围:沙箱内自动清除,沙箱外需主动清理,系统资源需逐个取消
- 安全擦除三级:基础(直接删除)、标准(覆写+删除)、增强(多次覆写+删除)
- 重装数据保留:云端存储是核心方案,配合应用账号框架和分布式数据
- 后台代理残留:闹钟、通知、定时器等不会随卸载自动清理,必须主动取消
- 隐私合规:数据保留需告知用户、获取同意、最小化原则
应用卸载是用户旅程的终点,但不是数据安全的终点。做好卸载场景的清理和保护,既是对用户负责,也是对开发者自身负责。
更多推荐

所有评论(0)