HarmonyOS技术精讲-Camera Kit(相机服务)第2篇:开发环境与权限申请

📅 2026/7/14 23:16:01
HarmonyOS技术精讲-Camera Kit(相机服务)第2篇:开发环境与权限申请
HarmonyOS技术精讲-Camera Kit相机服务第2篇开发环境与权限申请开篇一个绕不开的坎很多人第一次接触Camera Kit时最容易在权限配置环节出问题。官方文档虽然清楚列出了需要的权限声明但实际开发中权限申请失败、弹窗不显示、真机模拟器行为不一致这些问题很常见。HarmonyOS NEXT对隐私权限的管控比之前更严格尤其是相机、麦克风这类敏感权限必须走动态申请流程。如果只是简单在module.json5里声明权限应用启动时直接调用相机接口大概率会碰到“Permission denied”错误。这篇内容主要解决三个问题权限需要在哪些地方声明、动态申请的正确流程怎么写、以及实际开发中容易踩的权限相关坑点。它解决什么问题Camera Kit相机服务是HarmonyOS提供的相机能力封装开发者通过它可以快速实现拍照、录像、预览流获取等功能。但相机功能有一个前提条件必须先获取相机权限。如果没有正确处理权限申请应用在调用相机接口时会直接报错甚至导致闪退。它适合的场景需要调用相机进行拍照、录像的应用需要获取相机预览流的场景如扫码、AR同时需要录制音频的视频拍摄场景不适合的场景仅读取媒体文件应使用Media Library Kit需要深度定制相机硬件的场景应考虑C/C接口与其它平台的权限模型相比HarmonyOS NEXT的权限体系更接近原生Android的动态权限模型但实现方式有自己的特点主要体现在权限组划分和状态查询上。环境说明DevEco Studio 版本DevEco Studio 6.1.0 及以上 HarmonyOS SDK 版本HarmonyOS 6.1.0(23) 及以上 目标设备手机真机或模拟器均可核心实现三步完成权限配置1. 在module.json5中声明权限这一段代码的作用是在应用安装时告知系统这个应用需要哪些敏感权限。如果没有这一步动态申请时会直接被拒绝。// entry/src/main/module.json5 { module: { // ... 其他配置 requestPermissions: [ { name: ohos.permission.CAMERA, reason: 用于拍照和录像功能, usedScene: { abilities: [EntryAbility], when: inuse } }, { name: ohos.permission.MICROPHONE, reason: 用于录像时采集音频, usedScene: { abilities: [EntryAbility], when: inuse } } ] } }reason字段不能随便写应用上架审核时会检查这个描述是否与实际功能对应。建议写清楚具体用途比如“用于拍照”、“用于录像时录制声音”。usedScene配置了权限在哪个Ability中被使用以及使用时机是“仅在前台使用”还是“后台也使用”。相机权限一般都是inuse前台使用时才申请。2. 动态申请权限声明权限只是第一步系统不会在应用启动时自动弹窗。需要在调用相机接口前主动发起权限申请流程。// utils/PermissionHelper.etsimport{abilityAccessCtrl,bundleManager,common,Permissions}fromkit.AbilityKit;import{BusinessError}fromkit.BasicServicesKit;exportclassPermissionHelper{privatecontext:common.UIAbilityContext;constructor(context:common.UIAbilityContext){this.contextcontext;}/** * 检查并请求相机权限 * returns Promiseboolean true表示权限已授权 */publicasyncrequestCameraPermission():Promiseboolean{try{// 获取目标应用的bundleInfoletbundleInfo:bundleManager.BundleInfoawaitbundleManager.getBundleInfoForSelf(bundleManager.BundleFlag.GET_BUNDLE_INFO_WITH_APPLICATION);// 获取权限管理实例letatManager:abilityAccessCtrl.AtManagerabilityAccessCtrl.createAtManager();// 检查权限状态letgrantStatus:numberawaitatManager.checkAccessToken(bundleInfo.appInfo.accessTokenId,ohos.permission.CAMERA);// 如果已经授权直接返回trueif(grantStatusabilityAccessCtrl.GrantStatus.PERMISSION_GRANTED){console.info(PermissionHelper: CAMERA already granted);returntrue;}// 权限未授权发起动态申请console.info(PermissionHelper: Requesting CAMERA permission);letresult:voidawaitatManager.requestPermissionsFromUser(this.context,[ohos.permission.CAMERA]);console.info(PermissionHelper: CAMERA permission request success);returntrue;}catch(error){// 用户拒绝或请求失败leterr:BusinessErrorerrorasBusinessError;console.error(PermissionHelper: CAMERA permission request failed, code:${err.code}, message:${err.message});returnfalse;}}/** * 申请相机麦克风权限用于录像 */publicasyncrequestCameraAndMicPermission():Promiseboolean{try{letbundleInfo:bundleManager.BundleInfoawaitbundleManager.getBundleInfoForSelf(bundleManager.BundleFlag.GET_BUNDLE_INFO_WITH_APPLICATION);letatManager:abilityAccessCtrl.AtManagerabilityAccessCtrl.createAtManager();// 检查相机权限letcameraGrant:numberawaitatManager.checkAccessToken(bundleInfo.appInfo.accessTokenId,ohos.permission.CAMERA);// 检查麦克风权限letmicGrant:numberawaitatManager.checkAccessToken(bundleInfo.appInfo.accessTokenId,ohos.permission.MICROPHONE);// 两者都已授权直接返回if(cameraGrantabilityAccessCtrl.GrantStatus.PERMISSION_GRANTEDmicGrantabilityAccessCtrl.GrantStatus.PERMISSION_GRANTED){returntrue;}// 需要申请letresult:voidawaitatManager.requestPermissionsFromUser(this.context,[ohos.permission.CAMERA,ohos.permission.MICROPHONE]);returntrue;}catch(error){leterr:BusinessErrorerrorasBusinessError;console.error(PermissionHelper: CameraMic permission request failed, code:${err.code});returnfalse;}}}这里有一个容易被忽略的细节requestPermissionsFromUser如果传入多个权限系统会把它们合并到一个弹窗中。如果用户拒绝了其中一个其它权限也不会被授予。所以如果只需要相机不要顺便申请麦克风。3. 在Ability初始化时传递ContextPermissionHelper需要传入UIAbilityContext通常在主Ability中初始化然后传递给业务页面。// entry/src/main/ets/entryability/EntryAbility.etsimport{UIAbility,common}fromkit.AbilityKit;import{window}fromkit.ArkUI;import{PermissionHelper}from../utils/PermissionHelper;exportdefaultclassEntryAbilityextendsUIAbility{privatepermissionHelper:PermissionHelper|nullnull;onCreate(want,launchParam):void{console.info(EntryAbility onCreate);// 创建权限帮助类实例this.permissionHelpernewPermissionHelper(this.context);}onWindowStageCreate(windowStage:window.WindowStage):void{windowStage.loadContent(pages/Index,(err,data){if(err.code){console.error(Failed to load content, code:${err.code}, message:${err.message});return;}console.info(Succeeded in loading the content.);// 将PermissionHelper传递给页面// 可以通过AppStorage或全局变量传递AppStorage.setOrCreate(permissionHelper,this.permissionHelper);});}}4. 在页面中调用权限检测// entry/src/main/ets/pages/Index.etsimport{PermissionHelper}from../utils/PermissionHelper;EntryComponentstruct Index{Statemessage:string点击按钮申请相机权限;privatepermissionHelper:PermissionHelper|nullAppStorage.getPermissionHelper(permissionHelper)||null;build(){Row(){Column(){Text(this.message).fontSize(16).margin(16)Button(申请相机权限).onClick(async(){if(!this.permissionHelper){this.message权限帮助类未初始化;return;}this.message正在申请权限...;letgranted:booleanawaitthis.permissionHelper.requestCameraPermission();if(granted){this.message权限已授予可以调用相机;}else{this.message权限被拒绝;}}).margin(8)}.width(100%).height(100%)}}}常见问题与踩坑问题1权限弹窗未弹出直接进入失败回调现象调用requestPermissionsFromUser后没有弹窗出现直接返回错误码200或异常。原因最常见的原因是UIAbilityContext传递错误。如果在非UIAbility的类中创建PermissionHelper或者传入了错误的context会导致权限申请接口无法正确弹窗。解决方案确保context的类型是common.UIAbilityContext。可以在基类或Ability初始化阶段就创建好权限帮助类实例然后通过全局变量或依赖注入传递给子页面。另一个可能的原因是权限已经在module.json5中声明了ohos.permission.CAMERA但usedScene配置中的abilities字段没有匹配到当前Ability的名称。检查EntryAbility的类名是否与module.json5中的abilities一致。问题2第一次授权成功第二次启动应用后权限状态变成了拒绝现象首次运行时弹窗授权成功用户下次再打开应用时调用相机接口直接报错“权限不足”。原因用户可能在系统设置中手动关闭了相机权限。这种情况常见于测试阶段需要明确区分“未被授予”和“已被用户手动关闭”两种状态。解决方案在requestCameraPermission方法中通过checkAccessToken检查权限状态。如果返回PERMISSION_DENIED需要重新调用requestPermissionsFromUser。用户手动关闭权限后再次弹窗时系统会再次询问用户可以重新授权。但如果用户选择了“不再询问”系统不会弹窗而是直接拒绝。这时需要引导用户去系统设置中手动开启权限。可以通过ohos.settings接口跳转到应用设置页面。// 跳转到应用详情页让用户手动开启权限asyncfunctionopenAppSettings():Promisevoid{letcontext:common.UIAbilityContextgetContext(this)ascommon.UIAbilityContext;letwant:Want{bundleName:com.huawei.hmos.settings,abilityName:com.huawei.hmos.settings.MainAbility,uri:application_info_entry?bundleNamecontext.applicationInfo.name,};awaitcontext.startAbility(want);}问题3多权限同时申请时拒绝其中一个导致整个弹窗失败现象同时申请相机和麦克风权限用户只拒绝了麦克风相机权限也没有被授予。原因在HarmonyOS中requestPermissionsFromUser传入多个权限时它们会被当成一组请求。用户拒绝组中的任意一个整个请求都会被标记为失败。这是系统设计的行为为了统一用户体验。解决方案建议分开申请权限。先申请相机权限成功后再申请麦克风权限。或者根据业务场景判断如果当前只需要拍照只申请相机权限只有在开启录像时才同时申请麦克风权限。// 先申请相机letcameraGranted:booleanawaitthis.requestCameraPermission();if(!cameraGranted){// 相机权限被拒无法使用相机功能return;}// 如果需要录像再单独申请麦克风letmicGranted:booleanawaitthis.requestMicPermission();// 如果麦克风被拒相机功能依然可以使用只是录像无声最佳实践权限申请尽量放在功能调用前而不是应用启动时。用户刚打开应用就弹窗要求相机权限体验很差。建议等用户点击了“拍照”或“录像”按钮后再触发权限申请。这样用户能理解为什么需要这个权限。不要一次性申请不相关的权限。如果只需要拍照只申请ohos.permission.CAMERA。ohos.permission.MICROPHONE只在录像时才需要。分开申请可以减少用户拒绝的风险。权限被拒绝后要有合理的降级处理。如果用户拒绝相机权限应用不能直接闪退或卡在空白页面。建议给出提示“相机权限未开启无法使用拍照功能”并提供一个“去设置”按钮引导用户手动开启。FAQQ为什么真机上权限弹窗正常模拟器上提示“权限请求失败”A模拟器上部分权限尤其是麦克风可能不支持弹窗。模拟器环境不完整时requestPermissionsFromUser会直接返回失败。建议以真机测试为准。Q用户拒绝一次后第二次调用requestPermissionsFromUser为什么没有弹窗了A用户拒绝后系统在一定时间内通常为24小时或直到应用重启不会再弹窗直接返回拒绝结果。这是为了防止应用频繁骚扰用户。此时只能引导用户去系统设置中手动授权。Q权限申请成功后调用相机接口仍然报错“permission denied”A这种情况一般有两个原因一是申请的是相机权限但调用的是麦克风相关接口二是权限申请成功但缓存的accessTokenId未更新。建议权限授权流程之后重新获取一次bundleInfo和accessTokenId进行二次校验。总结相机权限配置是调用Camera Kit的第一步也是新手最容易出错的地方。核心就是三件事module.json5声明、动态申请、Context正确传递。权限被拒绝后的降级处理同样重要直接决定了用户体验好坏。如果遇到权限弹窗不弹出或状态不正确的情况优先检查Context的类型和生命周期。权限相关的错误通常不会直接崩应用但不处理的话用户的相机功能会完全不可用。