- 示例工程
- 前端
- 移动开发
- 跨平台
【免费下载链接】uni-app
A cross-platform framework using Vue.js
本篇指南围绕 uni-app x(当前开源仓库gh_mirrors/un/uni-app)中的uni.createRequestPermissionListener()API 展开,讲解如何在 App 端全局监听系统权限申请确认框的弹出与关闭,并结合 Android 平台源码实现、uni.requestSystemPermission配套调用以及华为应用市场合规要求,给出可复制、可运行的完整示例。读完本文,你将掌握权限监听对象RequestPermissionListener的创建、onRequest/onConfirm/onComplete/stop四个方法的使用要点、底层实现原理与全局合规弹窗的落地写法。
API 概述与适用场景
uni.createRequestPermissionListener()用于创建一个监听权限申请的对象。在 app-android 平台上,无论业务代码在哪一处申请权限,只要系统权限申请确认框弹出或关闭,都会触发对应的监听事件。
该能力最典型的应用场景是合规告知:华为应用市场审核要求“APP 在调用终端权限时,应同步告知用户申请该权限的目的”。此时即可使用本 API,在app.uvue里做全局监听,在系统确认框弹出的同时,展示自定义的“申请权限目的说明”,既满足审核要求,也提升用户体验。
监听对象创建后返回RequestPermissionListener,随后可调起其onRequest、onConfirm和onComplete:
- 当权限申请的确认框在手机端弹出时,触发
onConfirm; - 当权限申请的确认框被用户关闭后,触发
onComplete; - 当业务代码发起系统权限申请时,触发
onRequest(申请前的时机点)。
兼容性说明
| Web | Android | iOS | HarmonyOS | | :- | :- | :- | :- | | x | 4.0+ | x | x |
该 API 仅支持app-android平台(HBuilderX 4.0 及以上版本),不支持 Web、iOS、HarmonyOS。从 接口类型声明 中的@uniPlatform标注也可确认:android 平台为uniVer: "4.0+",其余平台均为x(不支持)。
返回值与 RequestPermissionListener 方法
调用uni.createRequestPermissionListener()后返回一个 RequestPermissionListener 实例,其接口定义如下:
export interface RequestPermissionListener { /** * 监听申请系统权限 * @param callback 申请系统权限回调,permissions为触发权限申请的所有权限 */ onRequest(callback: RequestPermissionListenerRequestCallback): void /** * 监听弹出系统权限授权框 * @param callback 弹出系统权限授权框回调,permissions为触发弹出权限授权框的所有权限 */ onConfirm(callback: RequestPermissionListenerConfirmCallback): void /** * 监听权限申请完成 * @param callback 权限申请完成回调,permissions为申请完成的所有权限 */ onComplete(callback: RequestPermissionListenerCompleteCallback): void /** * 取消所有监听 */ stop(): void } export type RequestPermissionListenerRequestCallback = (permissions: Array<string>) => void export type RequestPermissionListenerConfirmCallback = (permissions: Array<string>) => void export type RequestPermissionListenerCompleteCallback = (permissions: Array<string>) => voidonRequest(callback):监听申请系统权限
| 参数 | 类型 | 必填 | 描述 | | :- | :- | :-: | :- | | callback | (permissions: Array<string>) => void | 是 | 申请系统权限回调,permissions为触发权限申请的所有权限 |
当任意业务代码发起系统权限申请时触发,回调参数为本次申请涉及的全部权限字符串(如["android.permission.READ_CALENDAR"])。
onConfirm(callback):监听弹出系统权限授权框
| 参数 | 类型 | 必填 | 描述 | | :- | :- | :-: | :- | | callback | (permissions: Array<string>) => void | 是 | 弹出系统权限授权框回调,permissions为触发弹出权限授权框的所有权限 |
系统权限确认框在手机端弹出时触发。这是展示“申请权限目的”说明的最佳时机,也是满足华为应用市场审核要求的关键钩子。
onComplete(callback):监听权限申请完成
| 参数 | 类型 | 必填 | 描述 | | :- | :- | :-: | :- | | callback | (permissions: Array<string>) => void | 是 | 权限申请完成回调,permissions为申请完成的所有权限 |
权限申请确认框被用户关闭后触发,可用于隐藏全局提示、清理定时器等收尾工作。
stop():取消所有监听
无参数。调用后取消当前监听对象注册的所有回调(onRequest/onConfirm/onComplete),通常在页面卸载或应用退出时调用,避免泄漏。
源码级实现原理
仓库中的 uni-createRequestPermissionListener 插件实现 展示了该 API 在 Android 平台的底层实现。入口函数如下:
export const createRequestPermissionListener: CreateRequestPermissionListener = function (): RequestPermissionListener { return new AndroidPermissionRequestManager() }AndroidPermissionRequestManager内部持有三个回调字段,分别对应requestCallback、confirmCallback、completeCallback。以onConfirm为例:
onConfirm(callback: RequestPermissionListenerConfirmCallback) { if (this.confirmCallback == null) { this.confirmCallback = callback } else { UTSAndroid.offPermissionConfirm(this.confirmCallback!) this.confirmCallback = callback } UTSAndroid.onPermissionConfirm(this.confirmCallback!) }可以看到,该 API 本质上是封装了 UTSAndroid 底层的权限事件总线:
- 注册时调用
UTSAndroid.onPermissionRequest/onPermissionConfirm/onPermissionComplete; - 重复注册时先
offPermissionConfirm等取消旧回调,再注册新回调,保证始终只保留最新监听; stop()则依次offPermissionComplete、offPermissionConfirm、offPermissionRequest,并把三个回调字段置为null,彻底解除监听。
从该实现可以推断:同一监听对象上,每个事件同时只会生效一个回调,重复调用onConfirm等注册方法会用新回调覆盖旧回调;而stop()是幂等的清理操作,即使未注册任何回调也可安全调用。
插件配置 config.json 中声明了"minSdkVersion": "19",即要求 Android 最低系统版本为 4.4(API 19)。
完整示例:监听日历权限申请
仓库示例页面 create-request-permission-listener.uvue 与该文档示例同步,演示了监听 + 发起申请 + 顶部提示条的完整闭环。核心逻辑如下(节选):
<script setup lang="uts"> const isPermissionAlertShow = ref(false) const timeoutId = ref(-1) const permissionListener = ref(null as RequestPermissionListener | null) onUnload(() => { permissionListener.value?.stop() permissionListener.value = null clearTimeout(timeoutId.value) }) const watchPermissionRRequest = () => { permissionListener.value = uni.createRequestPermissionListener() permissionListener.value!.onConfirm((_) => { // 注意:onConfirm 触发时机与系统弹窗存在微小时间差,示例中延迟 100ms 再展示提示条 timeoutId.value = setTimeout(() => { isPermissionAlertShow.value = true }, 100) }) permissionListener.value!.onComplete((_) => { clearTimeout(timeoutId.value) isPermissionAlertShow.value = false }) } const requestPermission = () => { // #ifdef APP-ANDROID uni.requestSystemPermission({ permissions: ["android.permission.READ_CALENDAR"], success: (res) => { console.log(res.grantedList) console.log(res.deniedList) }, fail: (err) => { uni.showToast({ title: "权限被拒绝了", position: "bottom" }) console.log(err.errCode) } }) // #endif } onReady(() => { watchPermissionRRequest() }) </script>模板部分包含一个可滑入滑出的“权限申请说明”提示条和一个申请按钮:
<template> <!-- #ifdef APP && !VUE3-VAPOR --> <scroll-view style="flex:1"> <!-- #endif --> <page-head title="权限申请监听"></page-head> <view class="permission-alert" id="permission-alert" :style="{'transform':isPermissionAlertShow ? 'translateY(0)':'translateY(-110px)'}"> <text style="font-size: 20px;margin-bottom: 10px;margin-top: 5px;">访问日历权限申请说明:</text> <text style="color: darkgray;">uni-app x正在申请访问日历权限用于演示,允许或拒绝均不会获取任何隐私信息。</text> </view> <button type="primary" style="margin: 10px;" @click="requestPermission">点击申请日历权限</button> <!-- #ifdef APP && !VUE3-VAPOR --> </scroll-view> <!-- #endif --> </template>需要说明的是,示例页面中的弹框仅用于演示,实际开发中监听权限申请的代码应放在app.uvue中,弹框应全局处理(可参考插件uni-prompt的 app-android 实现,自行封装一个 uts 全局弹框)。
另外,仓库示例在发起申请前还使用了UTSAndroid.checkSystemPermissionGranted做预检查,若权限已授予则直接提示“权限已经同意了,不需要再申请”,避免重复弹窗:
if (UTSAndroid.checkSystemPermissionGranted(UTSAndroid.getUniActivity()!, ["android.permission.READ_CALENDAR"])) { uni.showToast({ title: "权限已经同意了,不需要再申请", position: "bottom" }) return } UTSAndroid.requestSystemPermission(UTSAndroid.getUniActivity()!, ["android.permission.READ_CALENDAR"], ...)与 uni.requestSystemPermission 的配合使用
本 API 监听的是通过uni.requestSystemPermission和UTSAndroid.requestSystemPermission申请的权限。相关配套 API uni.requestSystemPermission 的参数与回调如下:
| 名称 | 类型 | 必填 | 描述 | | :- | :- | :-: | :- | | options | RequestSystemPermissionOptions | 是 | 请求系统权限参数 |
其中options支持:
permissions: Array<string>(必填):申请的系统权限列表,如["android.permission.CAMERA"]、["android.permission.ACCESS_FINE_LOCATION", "android.permission.ACCESS_COARSE_LOCATION"];success: (result) => void:成功回调,结果包含:grantedList: Array<string>:已授权权限列表,仅包含当前系统支持的权限;deniedList: Array<string>:已拒绝权限列表;doNotAskAgainList: Array<string>:不再询问权限列表;
fail: (result) => void:失败回调,可通过errCode区分原因,例如1560601表示“申请权限为空”,1560604表示“不支持申请权限”;complete: (result) => void:申请结束(无论成败)均会触发的回调。
建议的权限字符串(Android)包括但不限于:android.permission.CAMERA、android.permission.READ_CALENDAR、android.permission.ACCESS_FINE_LOCATION、android.permission.ACCESS_COARSE_LOCATION等,完整列表可参考仓库文档 app-nativeresource-android 中的权限章节。
全局合规监听的最佳实践(app.uvue 落地)
针对华为应用市场“调用终端权限时同步告知申请目的”的审核要求,推荐把监听放到app.uvue中全局注册,并使用全局弹框组件展示说明:
<script setup lang="uts"> let permissionListener: RequestPermissionListener | null = null onLaunch(() => { permissionListener = uni.createRequestPermissionListener() // 申请发起时:可在此记录即将申请的权限 permissionListener.onRequest((permissions: Array<string>) => { console.log('正在申请权限:', permissions) }) // 确认框弹出时:展示全局“权限用途说明” permissionListener.onConfirm((permissions: Array<string>) => { // TODO 根据 permissions 内容匹配对应文案,全局弹框提示 }) // 确认框关闭时:收起全局弹框 permissionListener.onComplete((permissions: Array<string>) => { // TODO 隐藏全局弹框 }) }) onUnload(() => { permissionListener?.stop() permissionListener = null }) </script>全局弹框建议封装为独立的 uts 插件/组件(类似仓库中uni-prompt插件的实现思路),保证在任何页面发起权限申请时都能同步展示合规说明,而无需在每个业务页面重复编写监听代码。
Tips 与注意事项
综合文档提示与源码实现,使用时需注意以下几点:
- 已授权权限不触发 onConfirm:如果权限已经申请并且允许之后,
onConfirm不会触发。因此已授权权限的二次申请不会重复弹窗,展示层逻辑应兼容这种情况。 - onComplete 可能多次触发:如果同时申请多个权限,
onComplete可能会触发多次(例如系统对多个权限分别弹框确认),UI 收尾逻辑需做幂等处理。 - 永久拒绝后的特殊行为:uni-app x 中如果请求一个已经被永久拒绝的权限,可能会触发
onConfirm,此时提示文案应避免“已拒绝不再询问”的误判。 - 监听范围有限:权限监听仅支持通过调用
uni.requestSystemPermission和UTSAndroid.requestSystemPermission申请的权限;通过原生三方 SDK 内部直接发起的权限申请可能不会被监听到。 - 及时清理:页面卸载时务必调用
stop()取消监听(参见示例中的onUnload),防止监听回调持有页面上下文造成泄漏。 - 平台限制:该 API 不支持 Web、iOS、HarmonyOS,使用前建议用条件编译
// #ifdef APP-ANDROID包裹相关代码,并在非 Android 平台提供降级处理。 - onConfirm 时机精度:从仓库示例代码中的注释(“目前 onConfirm 监听实现的在时间上不够精确,暂时需要延迟弹框”)可以推断,
onConfirm与系统弹窗的实际展示存在微小时间差,生产环境可通过setTimeout微调提示条展示时机。 - 全局监听可参考现成插件:全局监听权限申请可参考插件
uni-registerRequestPermissionTips,快速复用成熟方案。
通用类型
监听回调与多数 uni API 一样,可统一处理GeneralCallbackResult:
| 名称 | 类型 | 必备 | 描述 | | :- | :- | :-: | :- | | errMsg | string | 是 | 错误信息 |
参考阅读
- API 文档:create-request-permission-listener.md
- 配套申请 API:request-system-permission.md
- 插件源码:uni-createRequestPermissionListener 实现
- 接口声明:interface.uts
- 可运行示例:create-request-permission-listener.uvue
- Android 权限清单说明:app-nativeresource-android
提示:该 API 不支持 Web,请将 hello uni-app x 运行到 App 平台体验完整效果。
- 示例工程
- 前端
- 移动开发
- 跨平台
【免费下载链接】uni-app
A cross-platform framework using Vue.js
相关推荐
uni-app x DOM API 实战:UniResizeObserver 元素尺寸监听完全指南
uni app x DOM API 实战:UniResizeObserver 元素尺寸监听完全指南 UniResizeObserver 是 uni app x
示例工程前端移动开发跨平台uni-app/uni-app x 组件相交监听实战:uni-createIntersectionObserver UTS 插件源码解析与使用指南
uni app/uni app x 组件相交监听实战:uni createIntersectionObserver UTS 插件源码解析与使用指南 本篇技术指南
示例工程前端移动开发跨平台uni-app权限管理终极指南:动态权限申请与控制完整教程
uni app权限管理终极指南:动态权限申请与控制完整教程 uni app作为跨平台应用开发框架,权限管理是其核心功能之一。在移动应用开发中,合理的权限申请与控
示例工程前端移动开发跨平台
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考