uniapp android权限需静态声明+动态申请:manifest.json中在app-plus→distribute→android→permissions下填标准权限字符串,再用plus.android.requestpermissions()调用并处理granted/deniedpresent/deniedalways三种状态。

UniApp 的 Android 权限必须分两步走:先在 manifest.json 里静态声明,再用 plus.android.requestPermissions() 动态申请。只做前者,6.0+ 设备上权限永远拿不到;只做后者,应用打包会直接报错或被市场拒审。
manifest.json 中怎么填 Android 权限声明
必须在 app-plus → distribute → android → permissions 节点下写字符串数组,每个元素是完整权限常量(不是 XML 标签)。常见错误是抄错拼写、多加空格、用了斜杠结尾、或者误写成 <uses-permission ...></uses-permission> 形式。
-
"android.permission.CAMERA"—— 不是CAMERA_PERMISSION,也不是android.hardware.camera -
"android.permission.RECORD_AUDIO"—— 注意是RECORD_AUDIO,不是RECORD_MIC或AUDIO_RECORD -
"android.permission.ACCESS_FINE_LOCATION"—— 定位要精确就用这个;若只需粗略定位,可用ACCESS_COARSE_LOCATION,但多数地图/扫码场景仍需精细 - 存储权限在 Android 10+ 已被弃用,
WRITE_EXTERNAL_STORAGE在 targetSdkVersion ≥ 29 后默认无效,应改用MANAGE_EXTERNAL_STORAGE(需额外说明用途)或适配 Scoped Storage
plus.android.requestPermissions() 怎么调用才不崩
这个 API 是原生桥接入口,仅在 App 端(app-plus)可用,H5 和小程序环境会静默失败。它不返回 Promise,必须传成功和失败两个回调函数。最常踩的坑是:在用户还没同意隐私政策前就调用、没判断当前是否已授权就重复弹窗、对 deniedAlways 情况不做降级处理。
远程控制 Android 设备,支持截图、自动化操作手机 App 等功能。本应用使用 uiautomator2 作为底层框架,通过 ADB 连接设备,用户无需 Root(1)查看手机屏幕;(2)自动化操作手机 App;(3)远程安装/卸载应用……
- 调用前先用
plus.android.hasPermission("android.permission.CAMERA")检查是否已有权限,避免无意义打扰 - 回调里的
resultObj有三个关键字段:granted(已授予权限数组)、deniedPresent(本次拒绝)、deniedAlways(勾选“不再提示”后永久拒绝) - 遇到
deniedAlways,必须引导用户去系统设置页:调用plus.android.gotoSettings(),不能只弹个提示就卡住 - 不要在
onLoad里直接申请——等页面渲染完成、用户有心理预期后再触发,比如点击「开始录音」按钮时
为什么 uni.authorize() 在 Android 上基本不管用
uni.authorize() 是为微信小程序设计的兼容层,在 UniApp 的 Android 打包中不会触发系统权限弹窗,只会返回 fail: not supported 或静默失败。它底层不走 Android 的 requestPermissions 流程,也不读 manifest.json 配置。如果你看到文档里写了这个 API,注意上下文是否限定在小程序平台。
- Android 端必须用
plus.android.requestPermissions(),这是唯一可靠的原生权限申请方式 -
uni.getSetting()可以查当前授权状态,但它返回的是微信风格的 scope 映射(如scope.camera),和 Android 原生权限名不对应,不能替代hasPermission() - 某些插件(如
leven-permissions-PermissionModule)封装了更友好的接口,但底层仍是调用同一个原生方法,只是帮你做了状态判断和跳转封装
targetSdkVersion 和 Android 版本的隐性影响
你的 manifest.json 里没有显式配置 targetSdkVersion,HBuilderX 默认设为最新稳定版(目前是 33 或 34)。这意味着:即使你只申请一个权限,只要设备是 Android 6.0+,就必须走动态流程;而 Android 13(API 33)起,READ_MEDIA_IMAGES、READ_MEDIA_VIDEO、READ_MEDIA_AUDIO 替代了旧的存储权限,且需单独申请——WRITE_EXTERNAL_STORAGE 在此版本后完全失效。
- 别指望降级
targetSdkVersion来绕过动态申请,主流应用市场(华为、小米、vivo)已强制要求 ≥30 - Android 11+ 开始,
ACCESS_BACKGROUND_LOCATION需要额外在AndroidManifest.xml里声明android:foregroundServiceType="location",UniApp 目前不支持该字段自动注入,需自定义基座或使用原生插件 - 每次修改
manifest.json后,必须重新云打包或自定义调试基座,热更新不生效
真正麻烦的不是写那几行代码,而是把「权限是否已获」、「用户点了拒绝还是永久拒绝」、「系统是否已禁用该权限入口」这三重状态在 UI 层做准确映射,并保持功能可降级。很多人卡在审核,问题不出在 API 调用,而出在拒绝后没提供替代路径,或弹窗文案没说明白“为什么需要这个权限”。










