ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

【化学实验室|18】HarmonyOS ArkTS 权限与隐私实战:让 module.json5、功能说明和拒绝路径一致

【化学实验室|18】HarmonyOS ArkTS 权限与隐私实战:让 module.json5、功能说明和拒绝路径一致

权限与隐私检查最容易陷入两个误区:看到module.json5没有权限声明,就认为项目一定不存在隐私问题;或者写了一页很长的隐私政策,就认为代码行为已经被完整覆盖。真正可靠的结论必须同时核对能力声明、源码调用、本地数据、用户控制、应用内说明和发布材料。

本文基于化学实验室 1.0.5的真实 HarmonyOS ArkTS 工程。当前entry/src/main/module.json5没有requestPermissions,源码扫描也没有发现应用直接创建 HTTP、Web、Socket、定位、相机、麦克风、通讯录或相册访问调用。应用使用Preferences保存实验记录、收藏、笔记、答题记录和学习统计;阅读朗读由CoreSpeechKit的textToSpeech完成,没有录音或麦克风采集,但创建引擎时明确传入了online: 1。

这意味着实验、答题、笔记等核心路径符合本地优先和最小权限方向,但现有证据不足以把朗读功能写成“确定完全离线”。进一步核对还发现两类可验证的不一致:隐私页声称“清除全部数据”,设置页却没有清除all_favorites、daily_quiz_answers和wrong_questions;隐私页写明可通过帮助页邮箱联系,帮助页实际只引导到应用市场评论区。它们不属于权限弹窗问题,却直接影响用户对数据处理、控制入口和联系方式的理解。

隐私合规不是多写一页说明,而是让应用实际行为、权限声明、数据生命周期、拒绝路径和用户可见文案形成同一份可验证契约。

CHEM-LAB-ONE12-PRIVACY-CAPABILITY-CONTRACT-20260730

本文将完成以下内容:

  • 读取module.json5,确认当前权限与 Ability 声明。
  • 扫描工程中的敏感 Kit、网络和授权调用。
  • 区分文本转语音、麦克风录音与在线处理边界。
  • 建立 Preferences 数据键、用途和删除方式清单。
  • 复现“清除全部数据”实际漏清收藏与答题数据的问题。
  • 设计统一数据注册表和可测试的清除服务。
  • 核对隐私政策、设置页、帮助页与应用行为。
  • 设计未来新增权限时的授权、拒绝、永久拒绝与降级状态。
  • 给出自动扫描、运行验证和发布材料同步清单。

一、module.json5 当前没有请求用户权限

当前模块配置包含入口 Ability 和桌面卡片 ExtensionAbility,但没有requestPermissions:

{ "module": { "name": "entry", "type": "entry", "deviceTypes": ["phone", "tablet", "2in1"], "abilities": [ { "name": "EntryAbility", "exported": true, "skills": [ { "entities": ["entity.system.home"], "actions": ["ohos.want.action.home"] } ] } ], "extensionAbilities": [ { "name": "DailyStudyFormAbility", "type": "form" } ] } }

exported: true用于主入口被系统桌面拉起,它与相机、定位等用户授权不是同一概念。桌面卡片 ExtensionAbility 同样不意味着自动获得敏感能力。当前配置没有ohos.permission.INTERNET,也没有位置、相机、麦克风、通讯录或媒体读写权限。

因此本文不会虚构“首次启动授权弹窗”。对当前版本而言,最好的拒绝路径就是根本不申请不需要的权限,让核心实验、阅读和答题功能在零敏感授权下可用。

二、只看清单还不够,必须扫描真实调用

权限声明可能落后于代码,代码也可能通过 SDK 间接使用能力。第一轮静态扫描应覆盖授权接口、敏感 Kit、网络、Web 和第三方 SDK:

rg -n --glob '*.ets' ` 'requestPermissionsFromUser|checkAccessToken|camera|microphone|geoLocation|contacts|picker|http\.createHttp|fetch\(|Web\(|analytics|advert|push|socket' ` entry/src/main/ets

对本项目的扫描结果,没有发现运行时授权请求,也没有发现应用直接使用 HTTP、Web、Socket、定位、相机、麦克风、通讯录、相册、广告、统计或推送接口。module.json5与核心实验、答题和本地数据代码在“无敏感权限、无应用自建网络请求”这一点上是一致的;但ReadingSpeechService使用系统CoreSpeechKit,且引擎参数包含online: 1,不能把静态扫描结论扩大成“所有能力绝对不联网”。

静态搜索并不是最终证明。后续还要检查依赖清单、Core Speech Kit 个人数据处理说明、构建产物、真实设备系统权限页和朗读场景的运行网络行为。它能快速指出显式 API 与声明之间的冲突,却不能替代系统服务的数据流核验。

三、文本转语音不等于录音,但在线边界必须核验

帮助页写有“化学阅读可以朗读”,源码通过:

import { textToSpeech } from '@kit.CoreSpeechKit' ReadingSpeechService.engine = await textToSpeech.createEngine({ language: 'zh-CN', person: 0, online: 1 })

这是把应用内文本转换为语音播放,不是采集用户声音。代码中没有audioCapturer、语音识别或麦克风授权调用,因此隐私页“不访问麦克风”的表述与当前实现一致。

但online: 1是当前源码不可忽略的事实。仅凭这段代码和无INTERNET声明,不能进一步断言朗读文本绝不会经过在线处理;失败文案却写着“当前设备不支持离线朗读”,也与引擎配置形成了需要澄清的语义偏差。正确做法是核对当前 SDK 版本的官方 API 与个人数据处理说明,在断网和联网条件下实测,并据此决定是改为真正的端侧模式,还是在隐私页中披露朗读文本的处理边界。结论确认前,文案只能承诺“不录音、不读取麦克风”,不能承诺“朗读内容绝不联网”。

四、真正的数据边界在 Preferences

DataStore.ets使用:

import { preferences } from '@kit.ArkData' const PREF_NAME = 'physics_app_data' DataStore.prefInstance = await preferences.getPreferences(context, PREF_NAME)

虽然应用名是“化学实验室”,Preferences 文件仍叫physics_app_data。这个名称不会把数据发送给其他应用,但会增加维护和迁移时的误解。若直接改名,旧版本数据会看似丢失;更安全的做法是保留旧名或实现一次性迁移,再删除旧存储。

隐私页说明数据保存在应用沙箱中,源码确实使用 Preferences,没有云同步、账号、导出或上传逻辑。这里的关键任务不是增加权限,而是把每一个键的用途、保留期和删除入口盘点清楚。

五、建立本地数据清单

从DataStore.ets、答题页和统计页可以整理出当前主要键:

数据键内容产生场景用户可见用途
favorite_experiments旧版实验 ID 列表收藏实验收藏兼容
all_favorites多类型收藏对象收藏实验、文章、公式收藏列表
experiment_records实验记录数组完成模拟实验记录与统计
user_notes用户笔记新建或编辑笔记笔记管理
experiment_count实验次数完成实验学习统计
learning_seconds学习秒数阅读或学习学习统计
learning_minutes旧版分钟数旧版本兼容数据迁移
daily_quiz_answers每日答题历史提交每日题答题统计
exam_practice_records专项练习记录提交试卷正确率与进度
wrong_questions错题记录回答错误错题本

这些内容不包含姓名、手机号、位置或设备标识,但笔记内容由用户手动输入,仍应在隐私页中明确说明其本地保存和删除方式。不能因为数据只在本机,就忽略数据生命周期。

六、隐私页的本地数据说明基本正确

PrivacyPolicyPage.ets写明应用保存:实验次数、学习时长、收藏 ID、笔记和偏好设置,并声明数据只保存在本机 Preferences。这个方向与源码一致。

但当前文案遗漏了每日答题历史、专项练习记录和错题记录。统计页明确读取:

const answersJson = await DataStore.getString('daily_quiz_answers', '[]') const examJson = await DataStore.getString('exam_practice_records', '[]') const wrongJson = await DataStore.getString('wrong_questions', '[]')

建议把“答题记录、专项练习进度、错题记录”加入本地数据说明,并解释用途是恢复学习进度与生成本机统计。这样政策文本不是笼统写“设置等数据”,而是能与真实键映射。

七、清除按钮没有覆盖全部本地数据

设置页当前的确定操作是:

await DataStore.putString('favorite_experiments', '[]') await DataStore.putString('experiment_records', '[]') await DataStore.putString('user_notes', '[]') await DataStore.putNumber('experiment_count', 0) await DataStore.putNumber('learning_minutes', 0) await DataStore.putNumber('learning_seconds', 0) await DataStore.putString('exam_practice_records', '[]')

它遗漏了至少三个正在使用的键:

  • all_favorites
  • daily_quiz_answers
  • wrong_questions

其中all_favorites的影响最直观。loadAllFavorites()会读取新收藏列表,再把旧版favorite_experiments合并进去。只清空旧键、不清空all_favorites,用户返回收藏页后仍能看到收藏项目。界面提示“已清除本机学习数据”,隐私页又声称可以清除全部数据,实际行为却不符合这两个承诺。

八、把数据键集中为唯一注册表

最稳妥的修复不是在设置页再补三行字符串,而是把所有持久化键集中定义:

export class PreferenceKeys { static readonly FAVORITE_EXPERIMENTS = 'favorite_experiments' static readonly ALL_FAVORITES = 'all_favorites' static readonly EXPERIMENT_RECORDS = 'experiment_records' static readonly USER_NOTES = 'user_notes' static readonly EXPERIMENT_COUNT = 'experiment_count' static readonly LEARNING_MINUTES = 'learning_minutes' static readonly LEARNING_SECONDS = 'learning_seconds' static readonly DAILY_QUIZ_ANSWERS = 'daily_quiz_answers' static readonly EXAM_PRACTICE_RECORDS = 'exam_practice_records' static readonly WRONG_QUESTIONS = 'wrong_questions' }

业务页面、统计页、清除服务和测试都引用同一常量。新增持久化数据时,代码审查可以要求同时更新注册表、隐私清单和清除用例。

九、清除逻辑应由 DataStore 负责

设置页不应知道每个存储键。把删除编排放到数据服务:

static async clearLearningData(): Promise<void> { const emptyListKeys: string[] = [ PreferenceKeys.FAVORITE_EXPERIMENTS, PreferenceKeys.ALL_FAVORITES, PreferenceKeys.EXPERIMENT_RECORDS, PreferenceKeys.USER_NOTES, PreferenceKeys.DAILY_QUIZ_ANSWERS, PreferenceKeys.EXAM_PRACTICE_RECORDS, PreferenceKeys.WRONG_QUESTIONS ] for (const key of emptyListKeys) { await DataStore.putString(key, '[]') } await DataStore.putNumber(PreferenceKeys.EXPERIMENT_COUNT, 0) await DataStore.putNumber(PreferenceKeys.LEARNING_MINUTES, 0) await DataStore.putNumber(PreferenceKeys.LEARNING_SECONDS, 0) await DataStore.refreshStatsSnapshot() }

页面只调用DataStore.clearLearningData(),根据成功或失败显示结果。未来增加新数据键时,不必在 UI 里寻找零散清除代码。

十、清除操作必须返回真实结果

当前DataStore.putString()和putNumber()在异常时会吞掉错误,设置页无论是否写入成功都会显示“已清除本机学习数据”。隐私控制属于用户明确操作,不能用乐观提示替代结果。

可以让服务返回结构化结果:

interface ClearResult { success: boolean clearedKeys: string[] failedKeys: string[] } const result = await DataStore.clearLearningData() if (result.success) { this.showToast('已清除本机学习数据') } else { this.showToast(`部分数据未清除,请重试`) }

失败提示不必暴露内部文件路径,但日志应记录失败键,便于定位存储异常。用户再次进入收藏、笔记、记录和统计页时,应看到一致的空状态。

十一、删除后要刷新内存快照

DataStore.notifyStatsChanged()会更新AppStorage中的收藏数、实验次数、学习秒数和版本号,但新增的all_favorites、错题和每日答题清除也要触发相关页面刷新。

清除完成后建议执行:

await DataStore.refreshStatsSnapshot() AppStorage.setOrCreate<number>('privacy_data_version', Date.now())

依赖统计的页面监听版本变化后重新加载。否则 Preferences 已经清空,当前页面仍可能展示删除前的内存数组,用户会误以为删除失败。

十二、确认对话框要准确描述范围

当前对话框写着“将清除本机保存的实验记录、收藏、笔记和学习统计,且不可恢复”。修复后还应补充答题与错题数据,避免用户只想清实验记录却误删学习进度。

推荐文案:

将清除本机保存的实验记录、全部收藏、笔记、答题记录、错题和学习统计。 此操作不可恢复,不会删除应用内置实验与文章。是否继续?

“取消”保持为主安全路径,“确定清除”使用危险状态色。删除开始后禁用重复点击,结束后再显示成功或部分失败结果。

十三、隐私页与帮助页的联系方式不一致

隐私页写明:可通过“我的-帮助与反馈”页面中的邮箱联系。但HelpPage.ets实际只有:

如遇到实验、阅读或答题异常,可在应用市场评论区反馈具体页面和操作步骤。

页面没有邮箱地址,也没有邮件跳转。二者必须选择一个真实方案:

  1. 在帮助页提供有效邮箱,并说明用户不要在邮件中发送不必要的个人信息。
  2. 删除隐私页的“邮箱”表述,改为当前真实可用的应用市场评论渠道。

不能保留一个用户无法找到的联系入口。修改后还要核对 AGC 隐私声明中的开发者联系方式,避免应用内外再次不一致。

十四、政策版本与应用版本要建立更新规则

隐私页显示应用版本 1.0.5,但生效日期固定为 2024 年 4 月 1 日。仅从源码无法判断该日期是否仍代表最新政策,因此发布前应由负责人确认。

建议维护明确元数据:

export class PrivacyMeta { static readonly POLICY_VERSION = '1.0.1' static readonly EFFECTIVE_DATE = '2026-07-17' static readonly APP_VERSION = Constants.APP_VERSION }

只有数据类型、权限、第三方 SDK、联网行为或联系方式发生变化时才更新政策版本和日期,不能每次构建自动改日期。更新记录应能说明“改了什么、为什么改、从何时生效”。

十五、无权限版本也需要拒绝路径设计

当前应用没有权限弹窗,但仍有能力不可用场景。例如朗读引擎创建失败时,ReadingSpeechService返回“当前设备不支持离线朗读”;由于同一配置包含online: 1,这条提示应改成不预设实现方式的“当前设备暂不支持朗读”。这是一种设备或服务能力降级,不是权限拒绝。

需要区分:

状态当前例子用户反馈核心功能是否可用
无需权限实验、笔记、收藏直接使用是
能力不支持朗读引擎不可用显示准确原因是,仍可阅读
本地写入失败Preferences 异常提示保存失败或重试是,可能不持久化
用户取消危险操作取消清除保持数据不变是

这种状态划分能避免把所有失败都误写成“请授权”。

十六、未来新增权限时使用四态模型

如果未来真的增加拍照记录实验、语音提问或位置服务,应先判断功能是否必要,再设计:

enum PermissionState { NotRequired, NotRequested, Granted, Denied } interface CapabilityDecision { state: PermissionState canContinueWithoutPermission: boolean userMessage: string }

业务流程必须覆盖首次请求、用户拒绝、再次进入和系统设置中撤销。拒绝后应保留不依赖该权限的功能,不能让整个应用停在空白页,也不能循环弹出授权请求。

十七、权限申请要在功能触发时发生

未来需要敏感能力时,不应在冷启动批量请求。正确时机是用户主动点击对应功能后:

  1. 先用一句话说明用途和不授权的影响。
  2. 用户继续后调用系统授权界面。
  3. 授权成功进入功能。
  4. 拒绝后返回可用页面并提供替代方式。
  5. 后续撤销时再次检查,不缓存永久 granted。

例如拍照实验记录被拒绝后,可以允许用户继续保存纯文字笔记;语音输入被拒绝后,保留键盘输入。权限只决定增量功能,不应破坏实验、答题、笔记等本地核心学习路径。

十八、声明、调用和文案必须同批修改

新增能力时至少同步六处:

module.json5 权限声明 ArkTS 能力调用与授权状态 功能页用途说明和拒绝降级 PrivacyPolicyPage 数据与权限章节 HelpPage 使用说明和联系方式 AGC 隐私声明、权限与 SDK 字段

若只改module.json5,会出现申请了权限却没有可达功能;若只写代码不加声明,功能运行失败;若代码和声明都改了但隐私页没改,用户看到的解释仍不真实。把六处作为同一个任务的验收项,比发布前临时补文案可靠。

十九、第三方 SDK 的结论也要有证据

隐私页声明未集成统计、广告或推送 SDK。源码扫描没有发现相关调用,但还应检查oh-package.json5与锁文件:

Get-Content .\oh-package.json5 Get-Content .\oh-package-lock.json5 rg -n 'analytics|advert|ads|push|track|oaid' .

系统 Kit 不等于第三方 SDK,桌面卡片也不等于推送。结论要以依赖和调用为依据。未来引入任何 SDK 时,应记录 SDK 名称、版本、用途、处理数据、隐私链接和关闭方式。

二十、把隐私契约分成四层

推荐结构如下:

Capability Layer module 声明、Kit 调用、系统授权状态 Data Layer Preferences 键、用途、保留、迁移、删除 UX Layer 用途说明、拒绝降级、失败反馈、删除确认 Release Layer 应用内政策、用户协议、AGC 隐私与权限字段

实际行为是最底层真源,上层说明不能超过或弱化代码行为。发布材料声称离线时,工程不能隐藏联网;政策声称可删除全部数据时,设置按钮必须覆盖所有键。

二十一、建立自动化一致性扫描

一个轻量脚本可以在每次发布前生成审计摘要:

interface PrivacyAudit { declaredPermissions: string[] sensitiveApiHits: string[] networkHits: string[] localDataKeys: string[] clearDataKeys: string[] missingClearKeys: string[] } const missingClearKeys = localDataKeys.filter( (key) => !clearDataKeys.includes(key) )

当前项目的检查应能指出all_favorites、daily_quiz_answers、wrong_questions不在设置页清除集合。脚本只负责发现差异,最终仍要由开发者判断某个键是否属于用户数据、是否需要保留迁移标记以及删除后如何刷新 UI。

二十二、权限与网络运行验证

在真实设备或模拟器上执行以下步骤:

操作预期结果通过标准
首次安装并启动不出现敏感权限弹窗首页可直接使用
打开实验、答题、笔记不要求联网或登录本地核心功能完整
查看系统应用权限页无相机、麦克风、位置等授权与 module 声明一致
断网启动和切换页面页面与本地数据可用无网络错误阻断
断网点击文章朗读记录引擎创建、播放和失败状态不出现麦克风授权,文案不误导
联网点击文章朗读与断网结果及网络日志对比确认online: 1的真实数据流
前后台切换不产生隐藏授权或异常弹窗状态保持正常

运行时可以配合网络日志和系统权限页检查。若后续依赖升级后出现新权限或联网,应立即回到声明与隐私清单重新审计。

二十三、本地数据删除验证

删除测试要先造数据,再检查所有入口:

  1. 收藏一个实验、一篇文章和一个公式。
  2. 完成一次实验并创建一条笔记。
  3. 提交每日答题、专项练习并产生一个错题。
  4. 进入统计页记录当前数量。
  5. 在设置中点击清除,先取消,确认数据不变。
  6. 再次清除并确认。
  7. 重新进入收藏、记录、笔记、统计、错题和答题页。
  8. 杀进程后重启,再检查一次。
检查点预期结果
取消清除所有数据保持不变
确认清除所有用户生成学习数据为空
内存页面不显示旧数组或旧计数
冷启动已清数据不会恢复
内置内容实验、文章、公式仍存在
失败场景不显示虚假成功提示

二十四、隐私页面验收

文案项源码证据验收标准
本地核心功能无 INTERNET 与应用自建 HTTP/Web断网可完成实验、答题、笔记
朗读处理边界CoreSpeechKit且配置online: 1依据官方说明和运行证据准确披露
不申请敏感权限module 无 requestPermissions系统权限页一致
本地保存Preferencesphysics_app_data数据不离开沙箱
数据类型收藏、记录、笔记、答题、错题、统计政策逐类覆盖
清除入口我的-设置-清除实验数据覆盖全部注册键
卸载删除应用沙箱随卸载移除重装不恢复旧数据
联系方式帮助页或真实邮箱用户能实际到达
第三方 SDK依赖与调用扫描声明与依赖一致

二十五、构建与发布前检查

源码修改后运行项目既有构建:

$env:DEVECO_SDK_HOME='D:\huawei\DevEco Studio\sdk' $env:PATH='D:\huawei\DevEco Studio\jbr\bin;' + $env:PATH & 'D:\huawei\DevEco Studio\tools\hvigor\bin\hvigorw.bat' --no-daemon assembleHap

发布前再核对:

  • 包内module.json5与源码声明一致。
  • 应用内隐私政策版本和联系方式正确。
  • 权限、联网、第三方 SDK 与 AGC 字段如实填写。
  • 桌面卡片不引入隐藏网络或额外数据处理。
  • 清除入口可达,确认、失败和成功状态完整。
  • 安装、启动、核心流程与卸载正常。

华为官方发布指引强调应用上线前需要完成隐私、安全、兼容性与稳定性检查,并根据实际情况如实填写隐私声明。这里的“实际”应由源码与运行证据支持,而不是仅依赖产品描述。

二十六、常见错误与修复策略

没有权限声明,就不检查数据

本地笔记、收藏和答题记录仍有保留与删除生命周期。修复方式是建立逐键数据清单和清除验证。

清除按钮逐行写键

新增功能后容易漏清。修复方式是集中PreferenceKeys和DataStore.clearLearningData(),并由测试比较写入键与清除键集合。

把朗读误写成录音,或反过来直接写成离线

朗读是文本转语音,不读取麦克风;但当前引擎配置包含online: 1,也不能在没有证据时写成“离线处理”。修复方式是按实际 Kit、配置参数、官方数据说明和运行证据描述。

联系方式只存在于政策文字

用户找不到真实入口。修复方式是提供可达邮箱,或把政策改为帮助页实际支持的反馈渠道。

新增权限后只修改 module.json5

缺少用途说明、拒绝降级和隐私更新。修复方式是把代码、声明、UX、政策和 AGC 字段作为同一个变更交付。

二十七、源码事实与建议实现的边界

本文提出的数据注册表、四态权限模型和自动审计脚本是建议方案,并非当前工程已经实现的能力。下面将可直接复核的源码事实与后续目标分开。

维度当前源码事实本文建议实现
权限声明module.json5没有requestPermissions保持最小权限,新增能力时按用途同批声明
应用网络接口扫描未发现应用直接创建 HTTP、Web 或 Socket构建产物和运行日志继续验证,结论不扩展到系统服务
文本朗读使用CoreSpeechKit.textToSpeech,未读取麦克风核对官方数据说明,明确端侧或在线处理边界
朗读配置createEngine参数包含online: 1根据真实产品要求调整配置,并同步隐私与失败文案
本地存储DataStore使用 Preferences,名称为physics_app_data保留兼容或实施可验证迁移,不能直接改名丢失旧数据
清除入口设置页逐项写入空值,漏掉多个实际数据键建立键注册表,由DataStore返回结构化清除结果
清除反馈写入方法吞掉异常,设置页仍提示已清除只有持久化确认成功后显示成功,并刷新内存快照
联系方式隐私页说帮助页有邮箱,帮助页只指向评论区二者统一为真实可达渠道
数据说明隐私页列出实验、收藏、笔记和偏好补充答题、专项练习、错题与朗读处理边界
第三方 SDK当前源码和依赖扫描未见广告、统计、推送 SDK每次依赖变化重新生成 SDK 与数据处理清单
拒绝路径当前版本没有敏感授权弹窗未来按可申请、已拒绝、永久拒绝、能力不可用分别降级
构建与真机结果本文未声称建议改造已构建或上机验证修改后执行权限页、断网、朗读、清除和卸载回归

特别需要强调:当前源码能证明“朗读不采集麦克风”,却不能在online: 1存在时证明“朗读一定完全离线”。隐私结论必须停在证据边界内,待官方资料与运行验证完成后再更新政策。

二十八、结语

化学实验室当前没有敏感权限声明,实验、答题、笔记等核心数据保存在 Preferences,源码也没有应用自建 HTTP、Web 或 Socket。与此同时,朗读引擎配置包含online: 1,因此“完全离线”的政策表述需要单独核验,不能由无INTERNET权限直接推出。真正需要修复的并不是“少了一个授权弹窗”,而是朗读处理边界、本地数据清单与删除行为之间的证据偏差。

通过真实源码可以看到:新旧收藏同时存在,设置页只清空旧收藏键;统计页读取每日答题和错题,清除操作却没有覆盖;隐私页指向邮箱,帮助页没有邮箱。把这些差异修复后,再用数据注册表、结构化清除结果、内存刷新和回归表固化,隐私承诺才能长期跟随功能演进。

权限最小化只是起点。最终交付标准是用户能够理解应用做什么、拒绝或退出后会发生什么、哪些数据保存在本机、如何真正删除,以及应用内外所有说明都能被代码和运行结果证明。

参考资料

  1. Huawei Developer,提交 HarmonyOS 应用:https://developer.huawei.com/consumer/cn/app/submit
  2. Huawei Developer,在应用市场分发:https://developer.huawei.com/consumer/cn/appgallery/devstart/
  3. Huawei Developer,数据管理概述:https://developer.huawei.com/consumer/cn/doc/harmonyos-guides/data-mgmt-overview
  4. 项目配置:entry/src/main/module.json5
  5. 项目源码:entry/src/main/ets/views/mine/PrivacyPolicyPage.ets
  6. 项目源码:entry/src/main/ets/views/mine/SettingsPage.ets
  7. 项目源码:entry/src/main/ets/views/mine/HelpPage.ets
  8. 项目源码:entry/src/main/ets/utils/DataStore.ets
  9. 项目源码:entry/src/main/ets/utils/ReadingSpeechService.ets
  10. Huawei Developer,Core Speech Kit:https://developer.huawei.com/consumer/cn/sdk/core-speech-kit/
  11. Huawei Developer,Core Speech Kit API:https://developer.huawei.com/consumer/cn/doc/harmonyos-references/core-speech-api

AI 辅助声明:本文由 AI 辅助完成源码梳理、结构校对与配图设计,技术结论均以文中列出的本地 ArkTS 源码、模块配置和官方资料为依据,并由作者复核后发布。

RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进