查看: 124|回复: 0

鸿蒙ASCF权限拒绝后openSetting引导设置与状态重置

[复制链接]
发表于 1 小时前 | 显示全部楼层 |阅读模式
在鸿蒙 ASCF 开发权限相关功能时,常见现象是:用户拒绝授权后,再调用 has.authorize 不再弹窗,而是直接进入 fail。此时不能继续死等系统弹窗,需要使用设置类 API 把用户引导到元服务设置页,并在返回后重新确认权限状态。ASCF 里主要涉及两个方法:has.openSetting 用来打开元服务设置页,has.getSetting 用来查询已授权的权限列表。

一、openSetting 与 getSetting 的分工

has.openSetting 调用后拉起元服务设置页面,用户可在其中手动开启或关闭权限。调用示例:
  1. has.openSetting({
  2.   success: (res) => {
  3.     console.info('设置页返回:', res);
  4.     // res 是用户在设置页操作后的权限状态
  5.   },
  6.   fail: (err) => {
  7.     console.error('打开设置页失败:', err);
  8.   }
  9. });
复制代码

has.getSetting 查询当前已授权的权限列表:
  1. has.getSetting({
  2.   success: (res) => {
  3.     console.info('已授权权限:', res);
  4.     if (res['scope.userLocation']) {
  5.       console.info('定位权限已开启');
  6.     }
  7.     if (res['scope.camera']) {
  8.       console.info('相机权限已开启');
  9.     }
  10.   },
  11.   fail: (err) => {
  12.     console.error('查询失败:', err);
  13.   }
  14. });
复制代码

返回值是一个对象,key 是 scope 名称,value 是 boolean。只会返回明确授权或拒绝的权限,没申请过的权限不会出现在结果里。两个方法的 success 返回值格式一样,都是 { 'scope.xxx': true/false } 对象。

设置页中显示给用户的权限名称,和代码里的 scope 不一定一样。例如设置页显示“位置信息”“相机”,而代码里是 scope.userLocation、scope.camera。弹窗引导时不要对用户说“请开启 scope.camera”,应说“请开启相机权限”。

二、最容易踩的坑:openSetting 必须先 authorize

openSetting 必须在 has.authorize 之后调用。如果项目从未调用过 authorize,直接调用 openSetting 会报错:
  1. {"errMsg":"operateWXData:fail no authorize"}
复制代码

也就是说,至少先弹过一次授权弹窗,不管用户同意还是拒绝,之后才能打开设置页。正确顺序是:先 authorize(成功或失败都算),再 openSetting。

三、设置页返回后状态会重置

用户在设置页操作后,之前 authorize 的授权状态会被重置。从设置页返回后,需要重新调用 authorize 或 getSetting 获取最新状态。

同时,openSetting 的 success 回调里的权限状态有些情况下可能不是最新的。保险起见,从设置页返回后再调一次 getSetting 确认。

另外,openSetting 不能代替 authorize。它只是引导用户手动操作,用户可以在设置页选择开启或关闭,开发者无法控制。

getSetting 的另一个限制是:它只返回之前 authorize 过的 scope。从来没申请过的权限不会出现在结果里。要检查某个权限是否已授权,得先 authorize 过一次。

四、典型使用场景

场景一:授权失败后弹窗引导去设置页。这是最常见流程:authorize 失败后调用 has.showModal,用户点“去设置”再调用 has.openSetting。
  1. Page({
  2.   getLocationWithGuide() {
  3.     has.authorize({
  4.       scope: 'scope.userLocation',
  5.       success: () => {
  6.         has.getLocation({
  7.           success: (res) => {
  8.             console.info('位置:', res);
  9.           }
  10.         });
  11.       },
  12.       fail: (err) => {
  13.         console.info('授权失败,引导去设置:', err);
  14.         has.showModal({
  15.           title: '需要定位权限',
  16.           content: '请在设置中开启定位权限,否则无法使用此功能',
  17.           confirmText: '去设置',
  18.           cancelText: '取消',
  19.           success: (modalRes) => {
  20.             if (modalRes.confirm) {
  21.               has.openSetting({
  22.                 success: (settingRes) => {
  23.                   console.info('用户从设置页返回');
  24.                 }
  25.               });
  26.             }
  27.           }
  28.         });
  29.       }
  30.     });
  31.   }
  32. });
复制代码

场景二:先用 getSetting 检查,未授权再申请。
  1. Page({
  2.   checkAndAuthorize() {
  3.     has.getSetting({
  4.       success: (res) => {
  5.         if (res['scope.camera']) {
  6.           this.openCamera();
  7.         } else {
  8.           has.authorize({
  9.             scope: 'scope.camera',
  10.             success: () => {
  11.               this.openCamera();
  12.             },
  13.             fail: () => {
  14.               has.showToast({ title: '需要相机权限' });
  15.             }
  16.           });
  17.         }
  18.       }
  19.     });
  20.   },
  21.   openCamera() {
  22.     const ctx = has.createCameraContext();
  23.     // ...
  24.   }
  25. });
复制代码

场景三:页面加载时检查所有权限状态,适合设置页展示开关。
  1. Page({
  2.   data: {
  3.     permissions: {
  4.       location: false,
  5.       camera: false,
  6.       microphone: false,
  7.       contact: false
  8.     }
  9.   },
  10.   onShow() {
  11.     this.checkPermissions();
  12.   },
  13.   checkPermissions() {
  14.     has.getSetting({
  15.       success: (res) => {
  16.         this.setData({
  17.           permissions: {
  18.             location: !!res['scope.userLocation'],
  19.             camera: !!res['scope.camera'],
  20.             microphone: !!res['scope.record'],
  21.             contact: !!res['scope.contact']
  22.           }
  23.         });
  24.       }
  25.     });
  26.   },
  27.   goToSetting() {
  28.     has.openSetting();
  29.   }
  30. });
复制代码

场景四:结合辅助功能权限。项目若用 has.openAccessibility 做辅助功能检测,用户拒绝后可引导去设置。err.code === 203 表示用户手动关闭了辅助功能。
  1. has.openAccessibility({
  2.   success: () => {
  3.     console.info('辅助功能已开启');
  4.   },
  5.   fail: (err) => {
  6.     if (err.code === 203) {
  7.       has.showModal({
  8.         title: '需要辅助功能',
  9.         content: '请在设置中开启辅助功能',
  10.         confirmText: '去设置',
  11.         success: (res) => {
  12.           if (res.confirm) {
  13.             has.openSetting();
  14.           }
  15.         }
  16.       });
  17.     }
  18.   }
  19. });
复制代码

五、完整流程与封装

authorize + openSetting 的流程可以概括为:
  1. 调用 has.authorize
  2. ├── success → 权限已拿到,继续业务
  3. └── fail
  4.     ├── 弹窗提示用户
  5.     │   ├── 用户点“去设置”
  6.     │   │   └── 调用 has.openSetting
  7.     │   │       └── 用户在设置页操作
  8.     │   │           └── 返回后重新 authorize/getSetting
  9.     │   └── 用户点“取消”
  10.     │       └── 结束或降级处理
  11.     └── getSetting 检查是否还有其他权限需要申请
复制代码

由于流程固定,可以封装 ensurePermission:
  1. function ensurePermission(scope, scopeName) {
  2.   return new Promise((resolve, reject) => {
  3.     has.authorize({
  4.       scope: scope,
  5.       success: () => resolve(),
  6.       fail: () => {
  7.         has.showModal({
  8.           title: '需要' + scopeName + '权限',
  9.           content: '请在设置中开启' + scopeName + '权限',
  10.           confirmText: '去设置',
  11.           cancelText: '取消',
  12.           success: (res) => {
  13.             if (res.confirm) {
  14.               has.openSetting({
  15.                 success: () => reject(new Error('需要用户重新授权'))
  16.               });
  17.             } else {
  18.               reject(new Error('用户拒绝授权'));
  19.             }
  20.           }
  21.         });
  22.       }
  23.     });
  24.   });
  25. }
  26. async function getLocation() {
  27.   try {
  28.     await ensurePermission('scope.userLocation', '定位');
  29.     const res = await new Promise((resolve, reject) => {
  30.       has.getLocation({ success: resolve, fail: reject });
  31.     });
  32.     return res;
  33.   } catch (err) {
  34.     console.error(err.message);
  35.   }
  36. }
复制代码

六、调试与落地提醒

在 DevEco Studio 的模拟器上,openSetting 可能表现和真机不一样,建议真机测试。项目里的 openSetting Demo 已经注册到“开放能力”分类下,可以直接体验;getSetting 的示例没有单独做,但文中代码可直接复制使用。

最后记住几个关键点:openSetting 必须在 authorize 之后;设置页返回后授权状态会重置,要重新确认;getSetting 只返回申请过的权限;openSetting 回调状态可能不是最新,必要时用 getSetting 二次确认。
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 注册

本版积分规则

指导单位

江苏省公安厅

江苏省通信管理局

浙江省台州刑侦支队

DEFCON GROUP 86025

Hacking Group 021A

旗下站点

态势感知中心

应急响应中心

红盟安全

联系我们

官方QQ群:112851260

官方邮箱:security#ihonker.org(#改成@)

官方核心成员

关注微信公众号

Archiver|手机版|小黑屋| ( 沪ICP备2021026908号 )

GMT+8, 2026-9-17 14:29 , Processed in 0.021454 second(s), 18 queries , Gzip On, Redis On.

Powered by ihonker.com

Copyright © 2015-现在.

  • 返回顶部