本教程将详细介绍在UniApp中开发安卓APP时,如何正确配置和动态申请系统权限,涵盖权限声明、运行时申请、状态处理等完整流程。
一、权限配置基础
1.1 在manifest.json中声明权限
在项目根目录的manifest.json文件中配置所需权限。有两种配置方式:
方式一:可视化配置(推荐)
打开HBuilderX,在manifest.json编辑界面切换到"App权限配置" -> "Android权限配置",勾选需要的权限项。
方式二:代码视图配置
在manifest.json的代码视图中添加:
{
"app-plus": {
"distribute": {
"android": {
"permissions": [
"android.permission.CAMERA",
"android.permission.ACCESS_FINE_LOCATION",
"android.permission.WRITE_EXTERNAL_STORAGE"
]
}
}
}
}
1.2 常用安卓权限列表
| 权限名称 | 说明 | 权限常量 |
|---|---|---|
| 相机权限 | 访问摄像头拍照 | android.permission.CAMERA |
| 存储权限 | 读写外部存储 | android.permission.WRITE_EXTERNAL_STORAGE |
| 定位权限 | 获取精确位置 | android.permission.ACCESS_FINE_LOCATION |
| 麦克风权限 | 录音 | android.permission.RECORD_AUDIO |
| 电话权限 | 拨打电话 | android.permission.CALL_PHONE |
| 联系人权限 | 读取联系人 | android.permission.READ_CONTACTS |
二、动态权限申请
Android 6.0+系统要求运行时动态申请敏感权限。UniApp通过plus.android.requestPermissionsAPI实现。
2.1 基本申请方法
// 单个权限申请示例
plus.android.requestPermissions(
['android.permission.CAMERA'],
function(resultObj) {
// 权限申请结果回调
console.log('权限申请结果:', resultObj);
if (resultObj.granted.length > 0) {
// 权限已授予
console.log('相机权限已获取');
} else if (resultObj.deniedPresent.length > 0) {
// 本次拒绝
console.log('用户本次拒绝授权');
} else if (resultObj.deniedAlways.length > 0) {
// 永久拒绝
console.log('用户永久拒绝授权');
}
},
function(error) {
// 申请出错
console.error('权限申请出错:', error);
}
);
2.2 多个权限同时申请
// 同时申请多个权限
const permissions = [
'android.permission.CAMERA',
'android.permission.WRITE_EXTERNAL_STORAGE'
];
plus.android.requestPermissions(
permissions,
function(resultObj) {
// 处理多个权限结果
console.log('已授权:', resultObj.granted);
console.log('本次拒绝:', resultObj.deniedPresent);
console.log('永久拒绝:', resultObj.deniedAlways);
}
);
三、权限状态处理
3.1 检查权限状态
在申请权限前,可以先检查当前权限状态:
// 检查权限是否已授予(仅适用于部分权限)
uni.getSetting({
success(res) {
if (res.authSetting['scope.camera']) {
console.log('相机权限已开启');
} else {
// 需要申请权限
requestCameraPermission();
}
}
});
3.2 处理用户拒绝场景
当用户拒绝权限时,需要提供友好的引导:
function handlePermissionResult(resultObj) {
if (resultObj.deniedAlways.length > 0) {
// 用户勾选了"不再询问",需要引导到设置页
uni.showModal({
title: '权限提示',
content: '您已永久拒绝该权限,请前往设置手动开启',
success(res) {
if (res.confirm) {
// 跳转到应用设置页
openAppSettings();
}
}
});
} else if (resultObj.deniedPresent.length > 0) {
// 本次拒绝,可以稍后再次尝试
uni.showToast({
title: '权限未获取,部分功能无法使用',
icon: 'none'
});
}
}
四、跳转到系统设置页
当用户永久拒绝权限时,需要引导用户到系统设置页手动开启:
// 跳转到应用权限设置页
function openAppSettings() {
if (plus.os.name === 'Android') {
const Intent = plus.android.importClass('android.content.Intent');
const Settings = plus.android.importClass('android.provider.Settings');
const Uri = plus.android.importClass('android.net.Uri');
const mainActivity = plus.android.runtimeMainActivity();
const intent = new Intent();
intent.setAction(Settings.ACTION_APPLICATION_DETAILS_SETTINGS);
const uri = Uri.fromParts('package', mainActivity.getPackageName(), null);
intent.setData(uri);
mainActivity.startActivity(intent);
}
}
五、完整示例代码
5.1 相机权限申请完整示例
// 封装相机权限申请函数
export function requestCameraPermission() {
return new Promise((resolve, reject) => {
// 先检查是否已授权
uni.getSetting({
success(res) {
if (res.authSetting['scope.camera']) {
resolve(true);
return;
}
// 向用户解释权限用途
uni.showModal({
title: '权限申请',
content: '我们需要访问您的相机以使用拍照功能',
success(modalRes) {
if (modalRes.confirm) {
// 发起权限申请
plus.android.requestPermissions(
['android.permission.CAMERA'],
function(resultObj) {
if (resultObj.granted.length > 0) {
resolve(true);
} else if (resultObj.deniedAlways.length > 0) {
// 永久拒绝,引导到设置页
uni.showModal({
title: '提示',
content: '请前往设置开启相机权限',
success(settingRes) {
if (settingRes.confirm) {
openAppSettings();
}
resolve(false);
}
});
} else {
resolve(false);
}
},
function(error) {
console.error('权限申请错误:', error);
reject(error);
}
);
} else {
resolve(false);
}
}
});
}
});
});
}
// 使用示例
async function takePhoto() {
const hasPermission = await requestCameraPermission();
if (hasPermission) {
// 执行拍照操作
uni.chooseImage({
count: 1,
success(res) {
console.log('拍照成功:', res.tempFilePaths[0]);
}
});
}
}
5.2 定位权限申请示例
// 定位权限申请
function requestLocationPermission() {
const permissions = [
'android.permission.ACCESS_FINE_LOCATION',
'android.permission.ACCESS_COARSE_LOCATION'
];
plus.android.requestPermissions(
permissions,
function(resultObj) {
if (resultObj.granted.length > 0) {
// 获取定位
uni.getLocation({
type: 'wgs84',
success(res) {
console.log('定位成功:', res);
}
});
} else {
uni.showToast({
title: '定位权限未获取',
icon: 'none'
});
}
}
);
}
六、常见问题与解决方案
6.1 权限弹窗不显示
-
原因1:manifest.json中未正确配置权限
-
解决:检查manifest.json权限配置,确保权限名称正确
-
原因2:在非主线程调用权限申请
-
解决:确保在页面生命周期(如onLoad、onShow)或用户交互事件中调用
6.2 权限申请回调不执行
- 原因:Android系统限制,用户点击"拒绝"后短时间内不会再次弹窗
- 解决:添加合理的重试机制,或引导用户到设置页
6.3 应用市场审核被拒
- 原因:权限使用说明不充分
- 解决:在权限申请前向用户清晰说明权限用途,并在隐私政策中说明数据使用方式
七、最佳实践建议
- 按需申请:在真正需要使用功能时才申请权限,不要一次性申请所有权限
- 用户引导:申请权限前向用户解释为什么需要该权限,提升用户体验
- 错误处理:对权限拒绝、永久拒绝等不同状态做差异化处理
- 测试覆盖:在不同Android版本、不同厂商ROM上测试权限申请流程
- 隐私合规:确保符合应用市场审核要求,在隐私政策中说明权限使用目的
八、总结
UniApp开发安卓APP的权限管理涉及manifest配置、运行时申请、状态处理等多个环节。通过plus.android.requestPermissionsAPI可以方便地实现动态权限申请,但需要注意不同Android版本的兼容性、用户引导、错误处理等细节。建议将权限申请逻辑封装成工具函数,提高代码复用性和可维护性。
在实际开发中,建议参考官方文档和最新API,因为不同HBuilderX版本可能会有细微差异。同时,关注各大应用市场的审核要求,确保权限使用符合规范。
注意:本教程基于UniApp官方文档和常见开发实践编写,具体实现时请根据实际项目需求和HBuilderX版本进行调整。建议在真机上进行充分测试,确保权限申请流程正常工作。