manifest.json 中 permissions 必须用全限定名数组格式,如["android.permission.CAMERA"],漏配、简写或对象格式会导致真机静默失败;动态申请前需 manifest 声明且运行时调用严格匹配;启动时自动申请高危权限易被厂商驳回。

uni-app 的 manifest.json 里配权限,不是“开了就能用”,而是“不配就彻底失效”——很多真机跑不通的问题,根源就在这一层漏配或写错。
manifest.json 中的 permissions 必须写全限定名
Android 权限字段必须带 android.permission. 前缀,比如 android.permission.CAMERA,不能简写成 CAMERA 或 camera。HBuilderX 打包时不会报错,但生成的 APK 在 Android 6.0+ 上会直接拒绝调用,plus.android.requestPermissions 回调里的 deniedAlways 可能为空,容易误判为“用户没点”,实际是系统压根不认这个权限。
- 正确写法:
"android.permission.ACCESS_FINE_LOCATION" - 错误写法:
"ACCESS_FINE_LOCATION"、"location"、"fine_location" - 常见遗漏项:
android.permission.READ_EXTERNAL_STORAGE和android.permission.WRITE_EXTERNAL_STORAGE在 Android 10+ 需配合requestLegacyExternalStorage(仅调试期),正式上架建议迁移到 Scoped Storage 方案
不要在 app-plus.distribute.android.permissions 里混用对象和字符串
早期文档有误导写法,比如把权限写成 {"android.permission.CAMERA": {}} 这种键值对形式,但 HBuilderX 实际只认数组格式。如果你看到项目里是对象结构,大概率运行时权限请求静默失败。
- 必须用数组:
"permissions": ["android.permission.CAMERA", "android.permission.RECORD_AUDIO"] - 不要用对象:
"permissions": {"android.permission.CAMERA": {}}—— 这种写法在云端打包时会被忽略 - 可视化勾选只是快捷方式,最终仍转成数组;手动改源码后必须重新「发行」或「云打包」才生效
动态权限触发前,必须先确保 manifest 已声明且与运行时调用严格对应
manifest.json 里声明的权限,只是告诉系统“我可能要用”,不是自动弹窗授权;真正触发弹窗的是运行时调用 plus.android.requestPermissions 或 uni.authorize,且权限名必须一一对应。
- 只配
manifest不调 API → 完全不会弹窗 - 只调 API 不配
manifest→ 真机静默失败,uni.authorize直接进fail回调,控制台无提示 - 微信小程序场景还要额外注意:
requiredPrivateInfos必须精准匹配接口名,比如只用uni.getLocation就只能写"getLocation",漏了"chooseLocation"就会静默失败,返回errCode: -1
厂商审核高危行为:App 启动时自动触发权限申请
很多开发者在 App.vue 的 onLaunch 里直接调 plus.android.requestPermissions,结果用户还没看到隐私协议就弹出系统权限框——这在华为、OPPO、vivo 等厂商的应用市场审核中属于高危行为,直接驳回。
- 基础权限(如存储)可启动时申请,但需前置展示隐私协议
- 功能权限(如相机、定位)必须等到用户触发相关操作时再申请
- 高级权限(如通讯录)建议放在设置页由用户主动开启
- 务必用
uni.getSetting先查状态,别依赖uni.authorize的回调判断是否可用
最容易被忽略的其实是 iOS 和 Android 对「永久拒绝」的判定差异:Android 有 deniedAlways 明确标识,而 iOS 没有等价字段,所有 getSetting 返回 false 都得按永久拒绝处理——必须引导至系统设置页,且不能再主动申请。


















