Skip to content

权限适配最佳实践

鸿蒙权限适配:动态申请、优雅降级、用户体验优化。


1. 权限申请策略

1.1 渐进式申请

❌ 错误:应用启动时申请所有权限
✅ 正确:在用户需要功能时申请对应权限

用户点击拍照 → 申请相机权限
用户点击定位 → 申请位置权限
用户点击上传 → 申请存储权限

1.2 代码实现

typescript
class PermissionAdapter {
    // 渐进式申请权限
    async requestPermissionForAction(
        action: string,
        permission: string,
        actionCallback: () => Promise<void>
    ): Promise<void> {
        // 1. 检查权限
        let status = securityAccessToken.verifyAccessToken({
            permissionName: permission
        })

        if (status === securityAccessToken.GrantStatus.PERMISSION_GRANTED) {
            // 已有权限,直接执行
            await actionCallback()
            return
        }

        // 2. 向用户解释为什么需要权限
        let reason = this.getPermissionReason(permission)
        await this.showPermissionDialog(reason, action)

        // 3. 请求权限
        let granted = await bundleManager.requestPermission({
            permission: permission
        })

        if (granted) {
            // 用户授权,执行操作
            await actionCallback()
        } else {
            // 用户拒绝,降级处理
            this.handlePermissionDenied(permission, action)
        }
    }

    private getPermissionReason(permission: string): string {
        const reasons: Record<string, string> = {
            'ohos.permission.CAMERA': '需要相机权限来拍摄照片',
            'ohos.permission.LOCATION': '需要位置权限来推荐附近的内容',
            'ohos.permission.READ_MEDIA': '需要读取相册来选择图片'
        }
        return reasons[permission] || '需要此权限来完成操作'
    }

    private handlePermissionDenied(permission: string, action: string): void {
        // 降级处理
        switch (permission) {
            case 'ohos.permission.CAMERA':
                // 无法拍照,提供从相册选择
                this.showAlbumPicker()
                break
            case 'ohos.permission.LOCATION':
                // 无法获取位置,让用户手动选择城市
                this.showCityPicker()
                break
            case 'ohos.permission.READ_MEDIA':
                // 无法读取相册,提示用户去设置页授权
                this.showSettingsGuide()
                break
        }
    }
}

2. 权限被拒处理

2.1 引导用户授权

typescript
@Component
struct PermissionDeniedDialog {
    @State showDialog: boolean = false
    @State permission: string = ''

    build() {
        if (this.showDialog) {
            AlertDialog({
                title: '权限被拒绝',
                message: `${this.getPermissionName(this.permission)}已被拒绝\n\n` +
                         '请在设置中手动开启此权限,以继续使用此功能',
                primaryButton: {
                    value: '去设置',
                    action: () => {
                        this.openSettings()
                    }
                },
                secondaryButton: {
                    value: '取消',
                    action: () => {
                        this.showDialog = false
                    }
                }
            })
        }
    }

    private openSettings(): void {
        // 跳转到应用设置页
        router.pushUrl({
            url: 'pages/SettingsPage'
        })
    }

    private getPermissionName(permission: string): string {
        const names: Record<string, string> = {
            'ohos.permission.CAMERA': '相机',
            'ohos.permission.LOCATION': '位置',
            'ohos.permission.MICROPHONE': '麦克风'
        }
        return names[permission] || permission
    }
}

3. 权限检查工具类

typescript
class PermissionUtils {
    // 检查多个权限
    static async checkPermissions(permissions: string[]): Promise<Map<string, boolean>> {
        let results = new Map<string, boolean>()
        
        for (let perm of permissions) {
            let status = securityAccessToken.verifyAccessToken({
                permissionName: perm
            })
            results.set(perm, status === securityAccessToken.GrantStatus.PERMISSION_GRANTED)
        }
        
        return results
    }

    // 请求多个权限
    static async requestPermissions(permissions: string[]): Promise<Map<string, boolean>> {
        let results = new Map<string, boolean>()
        
        for (let perm of permissions) {
            let granted = await bundleManager.requestPermission({
                permission: perm
            })
            results.set(perm, granted)
        }
        
        return results
    }

    // 是否有任一权限
    static hasAnyPermission(permissions: string[]): boolean {
        for (let perm of permissions) {
            let status = securityAccessToken.verifyAccessToken({
                permissionName: perm
            })
            if (status === securityAccessToken.GrantStatus.PERMISSION_GRANTED) {
                return true
            }
        }
        return false
    }

    // 是否所有权限都有
    static hasAllPermissions(permissions: string[]): boolean {
        for (let perm of permissions) {
            let status = securityAccessToken.verifyAccessToken({
                permissionName: perm
            })
            if (status !== securityAccessToken.GrantStatus.PERMISSION_GRANTED) {
                return false
            }
        }
        return true
    }
}

4. 面试高频考点

Q1: 权限申请最佳实践?

回答:渐进式申请(用时再申请)、向用户解释原因、权限被拒后优雅降级、引导用户去设置页。

Q2: 如何处理权限被拒?

回答:显示友好提示、提供降级方案、引导用户去设置页手动授权。


🐱 小猫提示:权限适配记住 "渐进式申请、解释原因、优雅降级、引导设置"