NEXT体验官#实战鸿蒙 HarmonyOS 成功一款权限恳求框架

  • 电脑网络维修
  • 2024-11-15

想了解更多关于开源的内容,请访问:

鸿蒙开发者社区

一、放开权限的普通步骤

每次放开权限的时刻,都须要经过以上几个步骤,当放开的权限越来越多,少量的重复代码就产生了。为了缩小重复代码,我封装了一个权限恳求框架。

二、权限恳求框架

桃夭是鸿蒙系统上的一款权限恳求框架,封装了权限恳求逻辑,驳回链式调用的方式恳求权限,极大的简化了权限恳求的代码,同时支持在UI、UIAbility、UIExtensionAbility外面放开权限。须要留意的是,运行在UIExtensionAbility放开授权时,须要在onWindowStageCreate函数口头完结后或在onWindowStageCreate函数回调中放开权限。

本名目基于开源鸿蒙4.1开发,最低兼容到API 11,请将DevEco Studio更新到最新版,DevEco Studio版本低于5.0.3.403或许不可编译。

桃夭一词出自现代第一部诗歌总集《诗经》中《诗经·桃夭》,“桃之夭夭,灼灼其华。”桃花盛开千万朵,色调娇艳红似火。

四、桃夭的经常使用方式

下载

ohpm install @shijing/taoyao

放开权限

TaoYao.with(this).runtime()// 要放开的权限.permission(permissions).onGranted(() => {// 权限放开成功}).onDenied(() => {// 权限放开失败}).request()

放开权限变得如此之便捷。

五、成功原理

1.如何支持在UI、UIAbility、UIExtensionAbility外面放开权限。

可以经常使用联结类型,也可以经常使用重载。这里经过重载的方式来实如今UI、UIAbility、UIExtensionAbility外面放开权限。

/*** 间接在UIExtensionAbility中放开权限** @param uiAbility* @returns*/static with(extensionAbility: UIExtensionAbility): IAccessControl;/*** 在UI中向用户放开授权** @param context* @returns*/static with(context: common.UIAbilityContext): IAccessControl;/*** 间接在UIAbility中放开权限** @param uiAbility* @returns*/static with(uiAbility: UIAbility): IAccessControl;static with(context: common.UIAbilityContext | UIAbility | UIExtensionAbility): IAccessControl {if (context instanceof UIAbility) {return new AccessControl(new UIAbilityOrigin(context))} else if (context instanceof UIExtensionAbility) {return new AccessControl(new UIExtensionAbilityOrigin(context))} else {return new AccessControl(new ContextOrigin(context))}}

UI、UIAbility、UIExtensionAbility外面最关键就是Context对象,放开权限的时刻须要传入Context对象,咱们须要从UI、UIAbility、UIExtensionAbility外面失掉Context对象。这里驳回战略形式。创立接口Origin,Origin代表从哪放开权限,定义getContext方法,由子类成功该方法。

/** * 须要UI、UIAbility、UIExtensionAbility放开权限,同时失掉Context对象。 */export interface Origin {/*** 失掉context对象** @returns*/getContext(): Context}

ContextOrigin代表在在UI中放开权限,成功Origin接口,重写getContext方法。

/** * 在UI中放开权限 */export class ContextOrigin implements Origin {private context: common.UIAbilityContextconstructor(context: common.UIAbilityContext) {this.context = context}getContext(): Context {return this.context}}

UIAbilityOrigin代表在在UIAbility中放开权限,雷同成功Origin接口,重写getContext方法。

/** * 在UIAbility中放开权限 */export class UIAbilityOrigin implements Origin {private uiAbility: UIAbilityconstructor(uiAbility: UIAbility) {this.uiAbility = uiAbility}getContext(): Context {return this.uiAbility.context}}

UIExtensionAbilityOrigin代表在在UIExtensionAbility中放开权限,雷同成功Origin接口,重写getContext方法。

/** * 在UIExtensionAbility中放开权限 */export class UIExtensionAbilityOrigin implements Origin {private uiExtensionAbility: UIExtensionAbilityconstructor(uiExtensionAbility: UIExtensionAbility) {this.uiExtensionAbility = uiExtensionAbility}getContext(): Context {return this.uiExtensionAbility.context}}

2.检测放开的权限能否在module.json5文件中申明

放开的权限必需在module.json5文件中申明,否则桃夭会间接抛意外。如何检测放开的权限能否在性能文件中申明?如下代码,经过bundleManager对象失掉运行消息,之后就可以失掉运行在性能文件中申明的权限了。假设要放开的权限没有module.json5文件中申明,那就会抛意外。

/*** 审核要放开的权限能否在module.json5文件中申明** @param permissions 要放开的权限*/private checkCommonConfig(permissions: Array) {const bundleFlags = bundleManager.BundleFlag.GET_BUNDLE_INFO_WITH_REQUESTED_PERMISSION;// 同步失掉在module.json5文件中申明的一切权限const bundleInfo = bundleManager.getBundleInfoForSelfSync(bundleFlags)const reqPermissionDetails = bundleInfo.reqPermissionDetailsif (ArrayUtils.isEmpty(reqPermissionDetails)) {throw new Error('请在module.json5文件中申明权限')}const reqPermissions = new ArrayList()reqPermissionDetails.forEach(reqPermissionDetail => {reqPermissions.add(reqPermissionDetail.name)})permissions.forEach((permission) => {if (!reqPermissions.has(permission)) {// 要放开的权限没有module.json5文件中申明throw new Error(`请在module.json5文件中申明${permission}权限`)}})}

3.检测其它性能

关于位置权限,有三种状况:

第一:放开含糊位置权限,大局部状况下,不会放开含糊位置权限,更多的是第二种状况。

第二:放开准确位置权限。

第三:放开后盾位置权限。针对位置权限,咱们须要额外的性能下。

假设用户放开准确位置权限,那就要先放开粗略位置权限。

假设用户放开后盾位置权限,那就先放开含糊位置权限和准确位置权限。当赞同这两个权限后,弹窗揭示用户到系统设置中关上相应的权限,用户在设置界面中的选用“一直准许”运行访问位置消息权限,运行就失掉了后盾位置权限。

/*** 审核权限的其它性能** @param permissions*/checkOtherConfig(permissions: Array) {const locationPermissionIndex = permissions.indexOf(this.LOCATION_PERMISSION)const locationBackgroundIndex = permissions.indexOf(this.LOCATION_IN_BACKGROUND)if (locationPermissionIndex >= 0 && locationBackgroundIndex < 0) {/** 关于位置权限,有两种状况* 第一:放开含糊位置权限,大局部状况下,不会放开含糊位置权限,更多的是第二种状况。* 第二:放开准确位置权限,须要先放开含糊位置权限。*/permissions = []permissions.push(this.APPROXIMATELY_LOCATION)permissions.push(this.LOCATION_PERMISSION)}if (locationBackgroundIndex >= 0) {// 放开后盾位置权限,须要先放开含糊位置权限和准确位置权限。当用户点击弹窗授予前台位置权限后,运行经过弹窗、揭示窗等方式告知用户返回设置界面授予后盾位置权限。permissions = []permissions.push(this.APPROXIMATELY_LOCATION)permissions.push(this.LOCATION_PERMISSION)permissions.push(this.LOCATION_IN_BACKGROUND)}this.setNewPermission(permissions)}

4.判别能否有权限

当一切的检测都经事先,就可以判别能否有权限了。调用checkAccessToken()方法来校验能否曾经授权。假设曾经授权,则回调告知调用者曾经有权限,否则须要启动下一步操作,即向用户放开授权。

hasPermission(permissions: Array): boolean {for (let i = 0; i < permissions.length; i++) {const permission = permissions[i]let atManager: abilityAccessCtrl.AtManager = abilityAccessCtrl.createAtManager();let grantStatus: abilityAccessCtrl.GrantStatus = abilityAccessCtrl.GrantStatus.PERMISSION_DENIED;// 失掉运行程序的accessTokenIDlet tokenId: number = 0;try {let bundleInfo = bundleManager.getBundleInfoForSelfSync(bundleManager.BundleFlag.GET_BUNDLE_INFO_WITH_APPLICATION);let appInfo: bundleManager.ApplicationInfo = bundleInfo.appInfo;tokenId = appInfo.accessTokenId;} catch (error) {const err: BusinessError = error as BusinessError;console.error(`Failed to get bundle info for self. Code is ${err.code}, message is ${err.message}`);}// 校验运行能否被授予权限grantStatus = atManager.checkAccessTokenSync(tokenId, permission);if (grantStatus !== abilityAccessCtrl.GrantStatus.PERMISSION_GRANTED) {return false}}return true}

5.放开权限

调用requestPermissionsFromUser(),假设用户授权,则调用mOnGranted。假设用户拒绝授权,揭示用户必需授权能力访问页面的性能,并疏导用户到系统设置中关上相应的权限。

/*** 放开权限** @param permissions*/requestPermission(permissions: Permissions[]) {let atManager: abilityAccessCtrl.AtManager = abilityAccessCtrl.createAtManager();// requestPermissionsFromUser会判别权限的授权形态来选择能否唤起弹窗atManager.requestPermissionsFromUser(this.origin.getContext(), permissions).then((data) => {let grantStatus: Array =>

6.系统设置弹窗

用户拒绝授权,揭示用户必需授权能力访问页面的性能,并疏导用户到系统设置中关上相应的权限。但在跳转系统设置之前,须要弹窗揭示用户,这里提供一个自动的弹窗。假设这个弹窗不满足你的要求,你可以改掉。当用户在弹窗外面点击敞开,则暗藏弹窗。当用户在弹窗外面点击去设置,则跳转到系统设置页面。

import { TaoYao } from '@shijing/taoyao/Index'import { common, Permissions } from '@kit.AbilityKit'import { hilog } from '@kit.PerformanceAnalysisKit'/** * 跳转系统设置之前,须要先弹窗 */@CustomDialogexport struct PermissionDialog {private title: string = '权限设置'private subtitle?: Resourceprivate left: string = '敞开'private right: string = '去设置'private permissions = new Array()private context = getContext(this) as common.UIAbilityContextcontroller: CustomDialogControlleraboutToAppear(): void {if (this.permissions.indexOf(('ohos.permission.ACCESS_BLUETOOTH' as Permissions)) >= 0) {this.subtitle = $r('app.string.access_bluetooth')} else if (this.permissions.indexOf(('ohos.permission.MEDIA_LOCATION' as Permissions)) >= 0) {this.subtitle = $r('app.string.media_location')} else if (this.permissions.indexOf(('ohos.permission.APP_TRACKING_CONSENT' as Permissions)) >= 0) {this.subtitle = $r('app.string.app_tracking_consent')} else if (this.permissions.indexOf(('ohos.permission.ACTIVITY_MOTION' as Permissions)) >= 0) {this.subtitle = $r('app.string.activity_motion')} else if (this.permissions.indexOf(('ohos.permission.CAMERA' as Permissions)) >= 0) {this.subtitle = $r('app.string.camera')} else if (this.permissions.indexOf(('ohos.permission.DISTRIBUTED_DATASYNC' as Permissions)) >= 0) {this.subtitle = $r('app.string.distributed_datasync')} else if (this.permissions.indexOf(('ohos.permission.LOCATION_IN_BACKGROUND' as Permissions)) >= 0) {this.subtitle = $r('app.string.location_in_background')} else if (this.permissions.indexOf(('ohos.permission.LOCATION' as Permissions)) >= 0) {this.subtitle = $r('app.string.location')} else if (this.permissions.indexOf(('ohos.permission.APPROXIMATELY_LOCATION' as Permissions)) >= 0) {this.subtitle = $r('app.string.approximately_location')} else if (this.permissions.indexOf(('ohos.permission.MICROPHONE' as Permissions)) >= 0) {this.subtitle = $r('app.string.microphone')} else if (this.permissions.indexOf(('ohos.permission.READ_CALENDAR' as Permissions)) >= 0) {this.subtitle = $r('app.string.read_calendar')} else if (this.permissions.indexOf(('ohos.permission.WRITE_CALENDAR' as Permissions)) >= 0) {this.subtitle = $r('app.string.write_calendar')} else if (this.permissions.indexOf(('ohos.permission.READ_HEALTH_DATA' as Permissions)) >= 0) {this.subtitle = $r('app.string.read_health_data')} else if (this.permissions.indexOf(('ohos.permission.READ_MEDIA' as Permissions)) >= 0) {this.subtitle = $r('app.string.read_media')} else if (this.permissions.indexOf(('ohos.permission.WRITE_MEDIA' as Permissions)) >= 0) {this.subtitle = $r('app.string.write_media')}}build() {Column() {Text(this.title).fontSize(20).fontColor('#151724')Text(this.subtitle).fontColor('#151724').fontSize(15).margin({top: 30})Row() {Button(this.left).fontColor('#585a5c').borderRadius(24).backgroundColor('#eeeeee').width('40%').height(48).margin({right: 20}).onClick(() => {this.controller.close()})Button(this.right).fontColor('#ffffff').borderRadius(24).backgroundColor('#4b54fa').width('40%').height(48).onClick(() => {this.controller.close()TaoYao.goToSettingPage(this.context)})}.margin({top: 30}).justifyContent(FlexAlign.SpaceBetween)}.width('100%').borderRadius(20).backgroundColor('#ffffff').padding({left: 24, right: 24, top: 30, bottom: 28})}}

7.跳转到设置页面

经常使用上方的代码即可跳转到系统设置页面。构建一个want对象,指定bundleName、abilityName、uri、parameters等参数,调用startAbility。

function openPermissionsInSystemSettings(context: common.UIAbilityContext): void {let wantInfo: Want = {bundleName: 'com.huawei.hmos.settings', // 系统设置的包名abilityName: 'com.huawei.hmos.settings.MainAbility', // 系统设置权限页面的类名uri: 'application_info_entry',parameters: {pushParams: 'com.example.myapplication' // 运行的包名,也就是关上指定运行的概略页面}}context.startAbility(wantInfo).then(() => {// ...}).catch((err: BusinessError) => {// ...})

目前只要华为手机经常使用了开源鸿蒙系统,不扫除后续会有其它的厂商经常使用开源鸿蒙系统,到时want对象的bundleName、abilityName、uri或许会不一样。在这种状况下,上方的代码就会有兼容性疑问。这就须要针对不同的品牌,创立不同的want对象。这里驳回战略形式。如下代码,创立SettingWant接口,定义getWant方法,由子类成功该方法,也就是由子类来创立want对象。

export interface SettingWant {/*** 失掉want对象** @param bundleName* @returns*/getWant(bundleName: string): Want}

新建DefaultSettingWant类,DefaultSettingWant是一个自动创立Want对象的子类。

/** * 自动失掉的want参数 */export class DefaultSettingWant implements SettingWant {getWant(bundleName: string): Want {let wantInfo: Want = {bundleName: 'com.huawei.hmos.settings',abilityName: 'com.huawei.hmos.settings.MainAbility',uri: 'application_info_entry',parameters: {pushParams: bundleName // 关上指定运行的概略页面}}return wantInfo}}

关于华为手机,咱们就承袭DefaultSettingWant,间接经常使用自动创立的Want对象。

/** * 失掉华为手机上的want参数 */export class HuaWeiSettingWant extends DefaultSettingWant {}

如下代码,先创立SettingWant对象,经过deviceInfo.brand判别品牌,假设是华为手机,则创立HuaWeiSettingWant。调用getWant失掉到Want对象,调用startAbility跳转到系统设置。

gToSettingPage(): void {const bundleName = this.getContext().abilityInfo.bundleNamelet settingWant: SettingWantif (deviceInfo.brand === "HUAWEI") {settingWant = new HuaWeiSettingWant()} else {settingWant = new DefaultSettingWant()}const want = settingWant.getWant(bundleName)if (this.origin instanceof UIExtensionAbilityOrigin) {// 在UIExtensionAbility中跳转到系统设置页面this.startAbilityFromUIExtensionAbility(want)} else {// 在UI或许UIAbility中跳转到系统设置页面this.startAbilityFromUIAbility(want)}} /*** 在UIExtensionAbility中跳转到系统设置页面** @param want*/private startAbilityFromUIExtensionAbility(want: Want) {(this.origin.getContext() as common.UIExtensionContext).startAbility(want).then(() => {// 跳转成功}).catch((err: BusinessError) => {console.error(`Failed to request permissions from user. Code is ${err.code}, message is ${err.message}`);})}/*** 在UI或许UIAbility中跳转到系统设置页面** @param want*/private startAbilityFromUIAbility(want: Want) {this.getContext().startAbility(want).then(() => {// 跳转成功}).catch((err: BusinessError) => {console.error(`Failed to request permissions from user. Code is ${err.code}, message is ${err.message}`);})}getContext(): common.UIAbilityContext {return (this.origin.getContext()) as common.UIAbilityContext}

六、源码

更多详细的代码,请下载 源码 或许检查 OpenHarmony三方库核心仓 。

想了解更多关于开源的内容,请访问:

鸿蒙开发者社区

  • 关注微信

本网站的文章部分内容可能来源于网络和网友发布,仅供大家学习与参考,如有侵权,请联系站长进行删除处理,不代表本网站立场,转载联系作者并注明出处:https://duobeib.com/diannaowangluoweixiu/7334.html

猜你喜欢

热门标签

洗手盆如何疏浚梗塞 洗手盆为何梗塞 iPhone提价霸占4G市场等于原价8折 明码箱怎样设置明码锁 苏泊尔电饭锅保修多久 长城画龙G8253YN彩电输入指令画面变暗疑问检修 彩星彩电解除童锁方法大全 三星笔记本培修点上海 液晶显示器花屏培修视频 燃气热水器不热水要素 热水器不上班经常出现3种处置方法 无氟空调跟有氟空调有什么区别 norltz燃气热水器售后电话 大连站和大连北站哪个离周水子机场近 热水器显示屏亮显示温度不加热 铁猫牌保险箱高效开锁技巧 科技助力安保无忧 创维8R80 汽修 a1265和c3182是什么管 为什么电热水器不能即热 标致空调为什么不冷 神舟培修笔记本培修 dell1420内存更新 青岛自来水公司培修热线电话 包头美的洗衣机全国各市售后服务预定热线号码2024年修缮点降级 创维42k08rd更新 空调为什么运转异响 热水器为何会漏水 该如何处置 什么是可以自己处置的 重庆华帝售后电话 波轮洗衣机荡涤价格 鼎新热水器 留意了!不是水平疑问! 马桶产生了这5个现象 方便 极速 邢台空调移机电话上门服务 扬子空调缺点代码e4是什么疑问 宏基4736zG可以装置W11吗 奥克斯空调培修官方 为什么突然空调滴水很多 乐视s40air刷机包 未联络视的提高方向 官网培修 格力空调售后电话 皇明太阳能电话 看尚X55液晶电视进入工厂形式和软件更新方法 燃气热水器缺点代码

热门资讯

关注我们

微信公众号