【共创稿事节】HarmonyOS 应用上架全流程中的隐私合规:权限最小化、数据本地化与 AppAnalyzer 体检

适用版本 :HarmonyOS NEXT / HarmonyOS 7,DevEco Studio 7.0(AppAnalyzer),AppGallery Connect(AGC)

写给谁看 :应用功能已经跑通、第一次走鸿蒙上架流程的开发者。

要解决的问题:驳回理由里"权限与隐私"常年高居榜首。本文把"从代码到上架"的合规动作拆成可执行项,并给出免权限的替代方案。

目录

一、为什么上架才是真正的"验收"

应用开发完只是走了一半。上架这一半才决定你的应用能否被用户看到,而驳回理由里,权限与隐私合规常年高居榜首。

本文以一个真实的本地化专注类应用为例。它的核心特征只有一句话:整包只声明 2 项权限、零联网。整条链路可以概括为六步:

代码与资源检查 → 权限最小化与隐私声明 → AppAnalyzer 上架体检 → 签名打包 → 提交 AGC 审核 → 上架与版本迭代。

下面挑最容易驳回的三步展开,每一步都给可核对的代码与验证方式。

二、权限最小化:能不申请就不申请

隐私合规的第一性原则是最小必要 。以这个专注类应用为例,它在 module.json5 里声明的权限克制到只有两项:

json5 复制代码
// entry/src/main/module.json5
{
  "module": {
    "name": "entry",
    "requestPermissions": [
      {
        "name": "ohos.permission.KEEP_BACKGROUND_RUNNING",
        "reason": "$string:reason_keep_running",     // 必须提供可展示的用途说明
        "usedScene": {
          "abilities": ["EntryAbility"],
          "when": "always"
        }
      },
      {
        "name": "ohos.permission.NOTIFICATION_CONTROLLER",
        "reason": "$string:reason_notification",
        "usedScene": {
          "abilities": ["EntryAbility"],
          "when": "inuse"
        }
      }
    ]
  }
}

配套的用途说明要写在资源文件里,审核会逐条核对"声明与行为是否一致":

json5 复制代码
// entry/src/main/resources/base/element/string.json
{
  "string": [
    {
      "name": "reason_keep_running",
      "value": "用于在锁屏或退到后台时保持计时准确,仅在计时任务进行中使用"
    },
    {
      "name": "reason_notification",
      "value": "用于在通知栏展示当前计时剩余时间,便于用户随时查看进度"
    }
  ]
}

2.1 最容易被"顺手申请"的是存储权限

很多应用习惯性申请读写用户存储的权限,然后在审核时被追问"为什么要读我整个相册"。这个应用把这类需求彻底绕开了:

  • 保存文件 走 DocumentViewPicker:用户自己选目录并确认,用时授权、用完即弃,应用不持有任何静态存储权限;
  • 分享文件 走 systemShare 系统分享面板:写入应用沙箱后把 URI 交给系统面板,应用从不下场碰用户文件。
typescript 复制代码
// services/ReportExportService.ets ------ 保存:不需要任何静态存储权限
import { picker } from '@kit.CoreFileKit';
import { fileIo } from '@kit.CoreFileKit';
import { common } from '@kit.AbilityKit';
import { BusinessError } from '@kit.BasicServicesKit';

export async function exportReport(
  ctx: common.UIAbilityContext,
  markdown: string,
  fileName: string = 'focus-weekly-report.md'
): Promise<boolean> {
  try {
    const options = new picker.DocumentSaveOptions();
    options.newFileNames = [fileName];
    const documentPicker = new picker.DocumentViewPicker(ctx);

    const uris = await documentPicker.save(options);
    if (!uris || uris.length === 0) {
      // 用户点了取消:这是正常路径,不要弹"导出失败"
      return false;
    }

    const file = fileIo.openSync(uris[0], fileIo.OpenMode.READ_WRITE);
    fileIo.writeSync(file.fd, markdown);
    fileIo.closeSync(file);
    return true;
  } catch (err) {
    // 部分版本会以错误码形式表达取消,同样按"非失败"处理
    const e = err as BusinessError;
    console.warn(`export canceled or failed: ${e.code} ${e.message}`);
    return false;
  }
}
typescript 复制代码
// services/ShareService.ets ------ 分享:交给系统面板,应用不接触第三方
import { systemShare } from '@kit.ShareKit';
import { uniformTypeDescriptor as utd } from '@kit.ArkData';
import { common } from '@kit.AbilityKit';

export function shareText(ctx: common.UIAbilityContext, text: string): void {
  const data = new systemShare.SharedData({
    utd: utd.UniformDataType.PLAIN_TEXT,
    content: text
  });
  const controller = new systemShare.ShareController(data);
  controller.show(ctx, {
    selectionMode: systemShare.SelectionMode.SINGLE,
    previewMode: systemShare.SharePreviewMode.DETAIL
  });
}

✅ 收益 :不需要 READ_USER_STORAGE 这类静态权限,也就没有了"为什么读我整个存储"的审核追问。这是上架前最省事的一处架构决策。

三、数据本地化:数据不出设备

权限之外,"数据怎么存、怎么传"同样是审核关注点。这个应用的做法是全程本地化:

typescript 复制代码
// services/historyStore.ets ------ 本地关系型数据库 + 参数化谓词
import { relationalStore } from '@kit.ArkData';
import { common } from '@kit.AbilityKit';

const STORE_CONFIG: relationalStore.StoreConfig = {
  name: 'focus_history.db',
  securityLevel: relationalStore.SecurityLevel.S1   // 本地最小安全等级,不上云
};

export async function querySessions(
  ctx: common.UIAbilityContext,
  dayStart: number,
  dayEnd: number
): Promise<relationalStore.ResultSet> {
  const store = await relationalStore.getRdbStore(ctx, STORE_CONFIG);

  // 参数化谓词:不要拼接 SQL 字符串
  const predicates = new relationalStore.RdbPredicates('session');
  predicates.greaterThanOrEqualTo('start_time', dayStart);
  predicates.lessThan('start_time', dayEnd);
  predicates.orderByAsc('start_time');

  return store.query(predicates, ['id', 'start_time', 'end_time', 'mode']);
}

三条纪律:

  • 本地存储 :会话历史存关系型数据库(relationalStore),参数化谓词查询,避免拼接注入;
  • 零联网:不声明任何网络权限,数据不出设备;
  • 导出可控 :导出内容仅包含会话记录数组,不含任何个人标识,且是否导出完全由用户发起。

这套"本地算、本地存、按需导出"的模式,让隐私声明可以写得极其干净------没有需要向第三方共享的数据,也就没有复杂的数据流转要解释。

⚠️ 风险提示 :如果后续为了统计或云备份引入了任何网络权限,隐私声明必须同步更新实际采集字段。声明与实际行为不一致是最高频的驳回理由之一,且属于主观违规而非技术缺陷,申辩空间很小。

四、AppAnalyzer:把上架问题提前暴露

DevEco Studio 提供的 AppAnalyzer 会在开发阶段发现可能影响上架的兼容性、性能、功耗、稳定性问题,并支持场景化检测。把它放进提审前的固定动作里,能把大量"提交后才知道"的问题变成"提交前已修复"。

原则很简单:别让审核方替你发现你自己的 bug。

打包前先做一次干净构建,确认产物可复现:

bash 复制代码
# 1) 干净构建并产出签名 HAP
hvigorw clean --no-daemon
hvigorw assembleHap --mode module -p product=default --no-daemon

# 2) 确认产物存在(路径以工程结构为准)
ls -lh entry/build/default/outputs/default/*.hap

预期输出:

text 复制代码
> hvigor Finished :entry:default@CompileArkTS...
> hvigor Finished :entry:default@PackageHap...
BUILD SUCCESSFUL in 24s

-rw-r--r--  1 user  staff   3.2M  entry-default-signed.hap

产物体积本身也是一项可核对指标:如果明显大于预期,通常意味着打进了无用资源或重复依赖。

五、常见驳回原因自查表

提审前对着这张表逐条过,能挡掉绝大多数低级驳回:

检查项 自查要点 对应动作
权限必要性 每项权限能否说明具体用途?有无可替代的免权限方案? 逐条核对 requestPermissions
隐私声明 是否与实际采集行为一致?有无超范围采集? 对照 string.json 用途文案
网络行为 是否声明了非必要的网络权限? 检查 ohos.permission.INTERNET
数据流向 数据是否出设备?若出,是否明示且最小化? 检查数据库与上传逻辑
兼容 / 性能 是否有 AppAnalyzer 未处理的告警? DevEco Studio 内运行 AppAnalyzer
签名与包 签名配置是否正确,包名与 AGC 一致? 核对 AGC 应用信息与签名证书

六、真实界面(上架形态)

下面两张图来自该应用的统计与多屏形态,均为本地数据驱动、无任何联网请求:

七、适用边界与限制

  • 仅 2 项权限是结果,不是目标 。如果你的业务确实需要定位或相机,正确做法是逐项说明用途,而不是硬删权限------删掉必要权限导致功能不可用,同样会被驳回。
  • 本文方案适用于"数据不需要出设备"的本地类应用。涉及云端同步、账号体系的应用,需要额外准备数据流转说明与第三方共享清单。
  • AppAnalyzer 覆盖的是技术类问题,不替代人工的合规自查。权限与隐私声明的语义正确性仍需人工确认。

八、小结

回到开头:上架不是开发完成后的"补作业",而是从第一行代码就要做的架构决策 。能靠系统能力(选择器、系统分享面板)解决的,就别申请权限;能留在本地的,就别上云。

当你的权限清单只剩两三项、隐私声明简洁到一页说完,上架这件事就从一个"拦路虎"变成了一个"走过场"。你在上架时被哪条理由驳回过?欢迎在评论区交流,我会整理成后续的避坑清单。

参考

相关推荐
贾伟康2 小时前
HarmonyOS API 26 UKey PIN 授权门禁:状态查询、失败归因与资源闭环
华为·harmonyos
Dream-Y.ocean8 小时前
鸿蒙PC平台星图遥感适配实战:基于 Electron 壳方案的纯 JavaScript 遥感图像处理引擎开发
javascript·electron·harmonyos
七月稻草人12 小时前
鸿蒙平板能当电脑第二块屏幕吗?ToDesk、向日葵镜像/副屏实测
华为·电脑·harmonyos
贾伟康13 小时前
【共创稿事节】HarmonyOS 7.0 Want隐式跳转:从接入到生产验证
性能优化·实战·harmonyos·arkts·arkui
NJPJI_Yang13 小时前
鸿蒙 HarmonyOS 7 应用内存优化:appMemoryOptimizer 释放 so 页缓存实战
pytorch·深度学习·harmonyos
熊猫钓鱼>_>15 小时前
三方库 NDK 原生模块跨平台适配与调试【鸿蒙心迹】
开发语言·华为·ai编程·harmonyos·鸿蒙·openharmony·鸿蒙心迹
行者-全栈开发1 天前
【鸿蒙心迹】三方库移植(二)——CMake 交叉编译与符号冲突排查实录(HarmonyOS 7.x)
harmonyos·cmake·交叉编译·三方库移植·ohos·符号冲突
梦想不只是梦与想1 天前
鸿蒙三方库源码调试
harmonyos·断点调试·三方源码调试
用户117910488331 天前
AI输出满屏井号星号?hmharness把Markdown渲染成终端表格 GitHub:swsgbl/hmharness
ai编程·harmonyos