最佳实践 - 鸿蒙全场景开发:桃夭权限框架助力媒体 / 定位场景化解决方案
最佳实践 - 鸿蒙全场景开发:桃夭权限框架助力媒体 / 定位场景化解决方案
前言
本文以 "全场景开发" 为核心视角,从桃夭框架的核心模块解析入手,深入拆解模块配置文件的权限声明逻辑、全局开关工具类的超级隐私模式适配机制、权限弹窗组件的用户体验优化思路,结合相机、麦克风、位置媒体与定位高频场景案例,呈现鸿蒙桃夭框架如何实现 "权限开发 + 用户体验 + 设备兼容" 。
鸿蒙权限框架:桃夭核心模块解析
模块配置文件:权限声明入口
1、模块配置文件 module.json5 鸿蒙应用权限声明,requestPermissions 数组,明确应用所需的各类权限网络、蓝牙、相机等,并通过 reason 说明权限使用缘由、usedScene 定义使用场景
{ "module": { "name": "entry", "type": "entry", "requestPermissions": [ { "name": "ohos.permission.INTERNET" }, { "name": "ohos.permission.ACCESS_BLUETOOTH", "reason": "$string:access_bluetooth", "usedScene": { "abilities": [ ], "when": "inuse" } }, { "name": "ohos.permission.MEDIA_LOCATION", "reason": "$string:media_location", "usedScene": { "abilities": [ ], "when": "inuse" } }, { "name": "ohos.permission.APP_TRACKING_CONSENT", "reason": "$string:app_tracking_consent", "usedScene": { "abilities": [ ], "when": "inuse" } }, { "name": "ohos.permission.ACTIVITY_MOTION", "reason": "$string:activity_motion", "usedScene": { "abilities": [ ], "when": "inuse" } }, { "name": "ohos.permission.CAMERA", "reason": "$string:camera", "usedScene": { "abilities": [ ], "when": "inuse" } }, { "name": "ohos.permission.DISTRIBUTED_DATASYNC", "reason": "$string:distributed_datasync", "usedScene": { "abilities": [ ], "when": "inuse" } }, { "name": "ohos.permission.LOCATION_IN_BACKGROUND", "reason": "$string:location_in_background", "usedScene": { "abilities": [ ], "when": "inuse" } }, { "name": "ohos.permission.LOCATION", "reason": "$string:location", "usedScene": { "abilities": [ ], "when": "inuse" } }, { "name": "ohos.permission.APPROXIMATELY_LOCATION", "reason": "$string:approximately_location", "usedScene": { "abilities": [ ], "when": "inuse" } }, { "name": "ohos.permission.MICROPHONE", "reason": "$string:microphone", "usedScene": { "abilities": [ ], "when": "inuse" } }, { "name": "ohos.permission.READ_CALENDAR", "reason": "$string:read_calendar", "usedScene": { "abilities": [ ], "when": "inuse" } }, { "name": "ohos.permission.WRITE_CALENDAR", "reason": "$string:write_calendar", "usedScene": { "abilities": [ ], "when": "inuse" } }, { "name": "ohos.permission.READ_HEALTH_DATA", "reason": "$string:read_health_data", "usedScene": { "abilities": [ ], "when": "inuse" } }, ], "description": "$string:module_desc", "mainElement": "EntryAbility", "deviceTypes": [ "phone", "tablet", "2in1" ], "deliveryWithInstall": true, "installationFree": false, "pages": "$profile:main_pages", "abilities": [ { "name": "EntryAbility", "srcEntry": "./ets/entryability/EntryAbility.ets", "description": "$string:EntryAbility_desc", "icon": "$media:icon", "label": "$string:EntryAbility_label", "startWindowIcon": "$media:icon", "startWindowBackground": "$color:start_window_background", "exported": true, "skills": [ { "entities": [ "entity.system.home" ], "actions": [ "action.system.home" ] } ] } ] } }
全局开关工具类:超级隐私模式把关人
2、GlobalSwitchUtil.ets 应用获取相机、麦克风、位置这三类敏感权限后,检测对应硬件功能是否因超级隐私模式被禁用,若禁用则引导用户开启,保障权限申请成功后功能能正常使用
import { Permissions } from "@kit.AbilityKit"; import { TaoYao } from "@shijing/taoyao"; import { promptAction } from '@kit.ArkUI'; export class GlobalSwitchUtil { /** * * 系统提供了超级隐私模式,在系统设置打开超级隐私模式后,相机、麦克风、位置将不可用。 * 在获取相机权限、麦克风权限和位置权限后,如果超级隐私模式开启,需要引导用户关闭超级隐私模式。 * * @param context * @param permissions */ static requestGlobalSwitch(context: Context, permissions: Array<Permissions>) { const locationPermission = "ohos.permission.LOCATION" as Permissions const approximatelyPermission = "ohos.permission.APPROXIMATELY_LOCATION" as Permissions const backgroundPermission = "ohos.permission.LOCATION_IN_BACKGROUND" as Permissions const microphonePermission = "ohos.permission.MICROPHONE" as Permissions const cameraPermission = "ohos.permission.CAMERA" as Permissions if (permissions.indexOf(locationPermission) >= 0 || permissions.indexOf(approximatelyPermission) >= 0 || permissions.indexOf(backgroundPermission) >= 0) { GlobalSwitchUtil.requestLocationGlobalSwitch(context) } else if (permissions.indexOf(microphonePermission) >= 0) { GlobalSwitchUtil.requestMicrophoneGlobalSwitch(context) } else if (permissions.indexOf(cameraPermission) >= 0) { GlobalSwitchUtil.requestCameraGlobalSwitch(context) } } static requestLocationGlobalSwitch(context: Context) { if (!TaoYao.isLocationEnabled()) { // 开启了超级隐私模式或者未打开定位开关,拉起全局开关弹窗引导用户关闭超级隐私模式或者打开定位开关 TaoYao.requestLocationGlobalSwitch(context).then((isOpen => { if (isOpen) { GlobalSwitchUtil.toast("定位开关已打开") } else { GlobalSwitchUtil.toast("定位开关已关闭") } })) } else { GlobalSwitchUtil.toast("定位开关已打开") } } static requestMicrophoneGlobalSwitch(context: Context) { TaoYao.isMicrophoneMute().then(isMute => { if (isMute) { TaoYao.requestMicrophoneGlobalSwitch(context).then((isOpen => { if (isOpen) { GlobalSwitchUtil.toast("麦克风已开启") } else { GlobalSwitchUtil.toast("麦克风已静音") } })) } else { GlobalSwitchUtil.toast("麦克风开启") } }) } static requestCameraGlobalSwitch(context: Context) { if (TaoYao.isCameraMuted(context)) { TaoYao.requestCameraGlobalSwitch(context).then(isOpen => { if (isOpen) { GlobalSwitchUtil.toast("相机已开启") } else { GlobalSwitchUtil.toast("相机关闭") } }) } else { GlobalSwitchUtil.toast("相机已开启") } } private static toast(text: string) { promptAction.showToast({ message: text }) } }
权限弹窗组件:用户体验桥梁
3、PermissionDialog.ets 自定义弹窗组件,核心作用是在权限申请被拒绝后,向用户展示权限用途说明并并引导用户前往系统设置授权,是权限申请流程中提升用户体验的关键环节
import { ArrayUtils, TaoYao } from '@shijing/taoyao/Index' import { common, Permissions } from '@kit.AbilityKit' import { hilog } from '@kit.PerformanceAnalysisKit' import { promptAction } from '@kit.ArkUI' import { GlobalSwitchUtil } from '../utils/GlobalSwitchUtil' /** * 跳转系统设置之前,需要先弹窗 */ @CustomDialog export struct PermissionDialog { private title: string = '权限设置' private subtitle?: Resource | string private left: string = '取消' private right: string = '去设置' private permissions = new Array<Permissions>() private message = '' private context = getContext(this) as common.UIAbilityContext controller: CustomDialogController aboutToAppear(): 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') } else { this.subtitle = this.message } } 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() if (ArrayUtils.isEmpty(this.permissions)) { // 通知权限只能跳转到系统设置页面,系统权限设置弹窗不支持通知权限 TaoYao.goToSettingPage(this.context) } else { this.showSystemPermissionDialog() } }) } .margin({top: 30}) .justifyContent(FlexAlign.SpaceBetween) } .width('100%') .borderRadius(20) .backgroundColor('#ffffff') .padding({left: 24, right: 24, top: 30, bottom: 28}) } private showSystemPermissionDialog() { TaoYao .showSystemPermissionDialog(this.context, this.permissions) .onGranted(() => { // 直接拉起系统权限设置弹窗后,用户授权 this.toast('直接拉起系统权限设置弹窗后,有权限了') /* * 系统提供了超级隐私模式,在系统设置打开超级隐私模式后,相机、麦克风、位置将不可用。 * 在获取相机权限、麦克风权限、位置权限后,如果开启了超级隐私模式,需要引导用户关闭超级隐私模式。 */ GlobalSwitchUtil.requestGlobalSwitch(this.context, this.permissions) }) .onDenied(() => { // 直接拉起系统权限设置弹窗后,用户未授权 this.toast('直接拉起系统权限设置弹窗后,没权限') }) .onFailed(() => { /* * 拉起系统设置弹窗失败,无法直接判断用户是否在系统设置页面授权,可以在onPageShow方法里面判断是否有权限 * 目前发现ohos.permission.READ_HEALTH_DATA健康数据权限无法直接拉起系统设置弹窗,只能跳转到系统设置页面 */ }) } private toast(text: string = "有权限了") { promptAction.showToast({ message: text }) } }
主入口页面:权限与功能集成中枢
4、 Index.ets 应用主入口页面,通过数据源驱动权限申请弹窗(dataSource+ PermissionDialog),集成摄像头/文档/音视频等系统文件选择器(cameraPicker/documentPicker),并与上下文(UIAbilityContext)联动实现系统能力调用
import { hilog } from '@kit.PerformanceAnalysisKit'; import { common, Permissions } from '@kit.AbilityKit'; import {AudioBuilder,CameraBuilder, CameraSelector, ContactBuilder,DocumentBuilder,MediaBuilder,MediaMimeType,TaoYao,UseCase } from '@shijing/taoyao/Index'; import { PermissionDetail } from '../model/PermissionDetail'; import { PermissionViewModel } from '../viewmodel/PermissionViewModel'; import DataSource from '../viewmodel/DataSource'; import { PermissionDialog } from '../view/PermissionDialog'; import { promptAction } from '@kit.ArkUI'; import { GlobalSwitchUtil } from '../utils/GlobalSwitchUtil'; @Entry @Component struct Index { @State message: string = 'Hello World' private dataSource = new DataSource<PermissionDetail>(PermissionViewModel.getReqPermissionDetails()) private context = getContext(this) as common.UIAbilityContext private dialogController = new CustomDialogController({ builder: PermissionDialog(), }) private toast(text: string = "有权限了") { promptAction.showToast({ message: text }) } private showPermissionDialog(permission: Array<Permissions>) { this.dialogController = new CustomDialogController({ builder: PermissionDialog({permissions: permission}), }) this.dialogController.open() } private notification(reason: string) { TaoYao.with(this.context) .notification() .permission() .onGranted(() => { this.toast() }) .onDenied(() => { this.dialogController = new CustomDialogController({ builder: PermissionDialog({message: reason}), }) this.dialogController.open() }) .request() } build() { Column() { Grid() { LazyForEach(this.dataSource, (permissionDetail: PermissionDetail) => { GridItem() { Button(permissionDetail.chineseName) .width('100%') .height(50) .fontSize(16) .onClick(() => { if (permissionDetail.name === "shijing.taoyao.NOTIFICATION") { // 申请通知权限 this.notification(permissionDetail.reason) TaoYao.isNotificationEnabled().then((enable: boolean) => { console.log(`yunfei是否有通知权限:${enable}`) }) TaoYao.isDistributedEnabled().then((enable: boolean) => { console.log(`yunfei是否支持分布式通知:${enable}`) }) return } if (permissionDetail.name === "contact_picker") { // 联系人选择器,通过联系人选择器获取联系人,不需要申请通讯录权限 this.contactPicker() return } if (permissionDetail.name === "camera_picker") { // 相机选择器,拉起系统相机不需要申请相机权限 this.cameraPicker() return } if (permissionDetail.name === "media_picker") { // 图片、视频选择器,拉起系统图库不需要申请权限 this.mediaPicker() return } if (permissionDetail.name === "document_picker") { // 文档选择器,拉起文档选择器不需要申请权限 this.documentPicker() return } if (permissionDetail.name === "audio_picker") { // 音频选择器,拉起音频选择器不需要申请权限 this.audioPicker() return } const name = permissionDetail.name as Permissions const permissions: Array<Permissions> = [name] TaoYao.with(this.context) .runtime() .permission(permissions) .onGranted(permissions => { // 权限申请成功 this.toast() /* * 系统提供了超级隐私模式,在系统设置打开超级隐私模式后,相机、麦克风、位置将不可用。 * 在获取相机权限、麦克风权限、位置权限后,如果开启了超级隐私模式,需要引导用户关闭超级隐私模式。 */ GlobalSwitchUtil.requestGlobalSwitch(this.context, permissions) }) .onDenied(permissions => { /* * 1、由于安全隐私要求,应用不能通过系统弹窗的形式被授予后台位置权限,应用如果需要使用后台位置权限,会先申请模糊位置权限和精确位置权限, * 然后弹窗提示用户到系统设置中打开相应的权限,用户在设置界面中的选择“始终允许”应用访问位置信息权限,应用就获取了后台位置权限。 * 2、权限申请失败,当用户拒绝授权时,将无法再次拉起系统的权限弹窗,此时只能弹窗提示用户到系统设置中打开相应的权限 */ this.showPermissionDialog(permissions) }) // 开始申请权限 .request() }) } }, (permissionDetail: PermissionDetail) => permissionDetail.name) } .columnsTemplate('1fr 1fr') .columnsGap(10) .rowsGap(10) .width('100%') .height('100%') } .width('100%') .height('100%') .padding(16) } /** * 联系人选择器,通过联系人选择器获取联系人,不需要申请通讯录权限。 * 如果申请通讯录权限,应用就能获取到所有的联系人,联系人选择器只允许应用获取到用户选择的联系人 */ contactPicker() { TaoYao.with(this.context) .contact() .onSuccess((data) => { // 联系人列表 if (data.length > 0) { // 联系人名称 console.log("yunfei", data[0].name?.fullName) // 联系人号码 console.log("yunfei", data[0]?.phoneNumbers?.[0]?.phoneNumber) } }) .onError((err) => { console.log(err.message) }) .selectContacts(new ContactBuilder() // 可以选择多个联系人 .setMultiSelect(true)) } /** * 相机选择器,拉起系统相机不需要申请相机权限 */ cameraPicker() { TaoYao.with(this.context) .camera() .onSuccess((uri) => { // 拍照或者录像的文件沙箱路径 console.log(uri) }) .onError((err) => { console.log(err.stack) }) .openSystemCamera(new CameraBuilder() // 后置相机,默认使用后摄 .setCameraSelector(CameraSelector.CAMERA_POSITION_BACK) // 可以只要拍照,只要录像,默认拍照和录像都有 .setUseCase([UseCase.PHOTO, UseCase.VIDEO]) // 文件保存路径,可以不设置 //.setSaveUri("") // 录制视频最大时长,可以不设置 //.setVideoMaxDuration() ) } /** * 图片、视频选择器,拉起系统图库不需要申请存储权限,只能获取选中的图片、视频 */ mediaPicker() { TaoYao.with(this.context) .media() .onSuccess((uris) => { uris.forEach((uri) => { console.log("yunfei", uri) }) }) .onError((err) => { console.log(err.stack) }) .select(new MediaBuilder() // 选择媒体文件的最大数目 .setMaxSelectNumber(10) // 可选择的媒体文件类型,图片类型、视频类型、图片和视频类型、动态照片类型 .setMediaMineType(MediaMimeType.IMAGE_VIDEO_TYPE) ) } /** * 文档选择器,拉起文档选择器不需要申请权限,只能获取选中的文档 */ documentPicker() { TaoYao.with(this.context) .document() .onSuccess((uris) => { uris.forEach((uri) => { console.log("yunfei", uri) }) }) .onError((err) => { console.log(err.stack) }) .select(new DocumentBuilder() // 选择媒体文件的最大数目 .setMaxSelectNumber(10) // 指定选择的文件或者目录路径(可选) //.setDefaultFilePathUri("") // 选择是否对指定文件或目录授权,true为授权,当为true时,defaultFilePathUri为必选参数,拉起文管授权界面;false为非授权,拉起常规文管界面(可选) //.setAuthMode(false) // 选择文件的后缀类型['后缀类型描述|后缀类型'](可选) 若选择项存在多个后缀名,则每一个后缀名之间用英文逗号进行分隔(可选),后缀类型名不能超过100,选择所有文件:'所有文件(*.*)|.*'; // 例如:['图片(.png, .jpg)|.png,.jpg', '文档|.txt', '视频|.mp4', '.pdf'] .setFileSuffixFilters(['文档|.docx']) ) } /** * 音频选择器,拉起音频选择器不需要申请权限,只能获取选中的文档 */ audioPicker() { TaoYao.with(this.context) .audio() .onSuccess((uris) => { uris.forEach((uri) => { console.log("yunfei", uri) }) }) .onError((err) => { console.log(err.stack) }) // 目前音频选择器不支持参数配置,默认可以选择所有类型的用户文件。 .select(new AudioBuilder()) } }
相机权限:系统相机能力的 "轻量调用"
用户点击 "相机权限" 相关触发逻辑对应页面中相机权限或相机选择器按钮交互,会调用 cameraPicker 方法,借助桃夭框架无需直接申请相机权限的情况下,拉起系统相机功能,通过 CameraBuilder 可配置相机为后置 CAMERA_POSITION_BACK、设置使用场景拍照和录像,UseCase.PHOTO与 UseCase.VIDEO,拍照或录像完成后,成功回调 onSuccess 中能获取到拍摄文件的沙箱路径 uri,便于后续对拍摄内容进行处理,过程中出现错误,错误回调 onError 会输出错误堆栈信息
// 相机选择器方法,拉起系统相机不需要申请相机权限相关调用逻辑 cameraPicker() { TaoYao.with(this.context) .camera() .onSuccess((uri) => { // 拍照或者录像的文件沙箱路径 console.log(uri) }) .onError((err) => { console.log(err.stack) }) .openSystemCamera(new CameraBuilder() // 后置相机,默认使用后摄 .setCameraSelector(CameraSelector.CAMERA_POSITION_BACK) // 可以只要拍照,只要录像,默认拍照和录像都有 .setUseCase([UseCase.PHOTO, UseCase.VIDEO]) ) }
麦克风权限:隐私与功能的 "平衡术"
涉及麦克风权限时,通过 TaoYao 的 runtime().permission(permissions).request() 发起申请,若权限授予 onGranted,会调用GlobalSwitchUtil.requestGlobalSwitch 检测系统超级隐私模式,因为该模式开启后麦克风会不可用,若检测到需引导用户关闭。同时,requestMicrophoneGlobalSwitch 方法会专门检测麦克风是否因超级隐私模式被静音,若静音则引导用户开启,保障麦克风权限获取后功能能正常使用;若权限被拒绝 onDenied,则展示权限弹窗引导用户去系统设置授权。
// 权限申请核心逻辑中与麦克风权限相关部分,以及超级隐私模式检测 const name = permissionDetail.name as Permissions const permissions: Array<Permissions> = [name] TaoYao.with(this.context) .runtime() .permission(permissions) .onGranted(permissions => { this.toast() /* * 系统提供了超级隐私模式,在系统设置打开超级隐私模式后,相机、麦克风、位置将不可用。 * 在获取相机权限、麦克风权限、位置权限后,如果开启了超级隐私模式,需要引导用户关闭超级隐私模式。 */ GlobalSwitchUtil.requestGlobalSwitch(this.context, permissions) }) .onDenied(permissions => { this.showPermissionDialog(permissions) }) .request() // 麦克风超级隐私模式检测相关方法 static requestMicrophoneGlobalSwitch(context: Context) { TaoYao.isMicrophoneMute().then(isMute => { if (isMute) { TaoYao.requestMicrophoneGlobalSwitch(context).then((isOpen => { if (isOpen) { GlobalSwitchUtil.toast("麦克风已开启") } else { GlobalSwitchUtil.toast("麦克风已静音") } })) } else { GlobalSwitchUtil.toast("麦克风开启") } }) }
位置权限:超级隐私模式的 "特殊适配"
位置权限包括模糊位置、后台定位等申请时,同样通过 TaoYao 的权限申请流程,权限授予后,GlobalSwitchUtil.requestGlobalSwitch 会检测超级隐私模式,因为该模式下位置功能会被禁用,requestLocationGlobalSwitch方法会检查定位功能是否开启,若因超级隐私模式或手动关闭导致未开启,会拉起系统弹窗引导用户打开定位开关;若已开启则直接提示,确保位置权限获取后能正常使用定位相关功能,若权限被拒则引导用户去系统设置授权
// 权限申请核心逻辑中与位置权限相关部分,以及超级隐私模式检测 const name = permissionDetail.name as Permissions const permissions: Array<Permissions> = [name] TaoYao.with(this.context) .runtime() .permission(permissions) .onGranted(permissions => { this.toast() GlobalSwitchUtil.requestGlobalSwitch(this.context, permissions) }) .onDenied(permissions => { this.showPermissionDialog(permissions) }) .request() // 位置超级隐私模式检测相关方法 static requestLocationGlobalSwitch(context: Context) { if (!TaoYao.isLocationEnabled()) { TaoYao.requestLocationGlobalSwitch(context).then((isOpen => { if (isOpen) { GlobalSwitchUtil.toast("定位开关已打开") } else { GlobalSwitchUtil.toast("定位开关已关闭") } })) } else { GlobalSwitchUtil.toast("定位开关已打开") } }
项目总结:桃夭权限框架的实践价值与优势
桃夭权限框架最实在的就是省了不少事,不用再为手机、平板等设备的权限差异反复改代码,也不用重复写权限申请、异常处理的逻辑,框架里的配置文件、工具类、弹窗组件直接就能用,拿来就适配多设备场景,像做媒体拍摄、定位这类功能时,能自动处理超级隐私模式的坑,比如相机、定位开了权限却用不了的情况,会主动引导用户解决,连用户弹窗的话术都配好了,不用再纠结怎么跟用户解释权限用途
✅全场景多设备无缝适配:统一封装跨终端权限逻辑,无需针对手机、平板等设备单独适配,轻松支撑全场景应用权限管理需求
✅场景化能力直达:深度整合媒体、定位等核心场景权限处理,从申请流程到超级隐私模式兼容一键搞定,快速落地业务功能
✅稳定性与扩展性双保障:基于鸿蒙原生能力构建,适配系统迭代与政策变更,同时模块化设计支持灵活扩展,降低长期维护成本
总结
鸿蒙桃夭权限框架能够帮助开发者解决鸿蒙权限开发核心痛点:多设备不用重复适配,权限申请、异常处理有现成模块,媒体、定位场景还能自动兼容超级隐私模式,连用户引导弹窗都配好了,省出时间能多琢磨业务逻辑。
👉想解锁更多干货?点击立即加入鸿蒙知识共建交流群:https://work.weixin.qq.com/gm/afdd8c7246e72c0e94abdbd21bc9c5c1
更多推荐
所有评论(0)