写点什么

使用 APICloud 开发 app 的动态权限及 Android 平台 targetSdkVersion 设置教程

作者:APICloud
  • 2022 年 6 月 06 日
  • 本文字数:5872 字

    阅读完需:约 19 分钟

​先介绍一下关于 Android 动态权限和 targetSdkVersion 背景:


targetSdkVersion:自 2018 年 11 月开始,GooglePlay 以及国内大部分应用市场要求 app 编译目标 SDK 必须为 26 及以上,否则不予提交审核;有许多已有 app 转到 APICloud 开发后,因 targetSdkVersion 降级而导致无法覆盖安装;2020 年以来,国家网信办等监管机构也加强了对 app 权限合规的监管。


动态权限:Android 自系统 6.0 开始,提供动态权限机制,对于敏感权限(存储,定位,录音,拍照,录像等),需要在 app 运行过程中动态向用户申请,这就和 iOS 系统的权限使用体验保持一致了(iOS 一直以来就是动态权限)。


使用 APICloud 开发平台开发 app 时,如果需要获取权限,需要动态申请。因此 APICloud 开发平台统一了 Android 和 iOS 两个平台的动态权限操作,提供两个 API:hasPermission 和 requestPermission。文档地址为:https://docs.apicloud.com/Client-API/api


在 Android 上使用动态权限,要求 app 编译的目标 SDK(即 targetSdkVersion)为 23 及以上(对应为 android6.0 及以上系统),22 及以下系统会执行缺省处理(手机厂商也可能定制处理),APICloud 为满足更普遍的开发需求,默认配置 targetSdkVersion 为 22,即权限走系统缺省处理。


开启动态权限,需要按照以下说明操作:

1、新建 manifest.xml 文件,添加如下代码:

<?xml version="1.0" encoding="UTF-8"?><manifest>    <application name="targetSdkVersion" value="28"/></manifest>
复制代码

将其中的 targetSdkVersion 更新为目标值,例如 30;


2、将 manifest.xml 置于你的/项目代码/res/目录下(widget/res/manifest.xml);


3、将你的 app 代码中所有涉及到需要动态权限的操作,参照示例中的代码,改造一遍(例如进行拍照录制视频等需要使用摄像头,以前的缺省处理中不需要申请摄像头权限,而开启动态权限后,必须在进行拍照之前,判断是否有摄像头权限,没有则进行申请,只有用户同意了摄像头权限才能进行接下来拍照的操作);


4、提交代码;


5、云编译界面勾选 app 所需的权限;


6、云编译 app 或自定义 loader 即可。


在这里需要注意的是,当你设置的 targetSdkVersion 大于等于 23 时,即意味着开启了动态权限,如果你的 app 带有获取 IMEI、定位、录音、拍照、录像等敏感功能时,必须使用动态权限机制,先判断是否具有该功能操作权限,再进行操作,如果不具备相应的权限,对应的功能是失效的,也可能导致 app 崩溃。


为保证动态权限尽可能适配更多厂商的手机以及顺利上线 Google Play,targetSdkVersion 目前推荐设置为 30。


以下为代码示例:

<!DOCTYPE HTML><html>
<head> <meta charset="UTF-8"> <meta name="viewport" content="maximum-scale=1.0, minimum-scale=1.0, user-scalable=0, initial-scale=1.0, width=device-width" /> <meta name="format-detection" content="telephone=no, email=no, date=no, address=no"> <title>权限管理</title> <link rel="stylesheet" type="text/css" href="./css/api.css" /> <link rel="stylesheet" type="text/css" href="./css/box.css" /> <script type="text/javascript" src="./script/public.js"></script> <style> .marg { margin: 3px 15px; font-size: 18px; } </style> <script type="text/javascript">
apiready = function () {
}
function hasPermission(one_per) { var perms = new Array(); if (one_per) { perms.push(one_per); } else { var prs = document.getElementsByName("p_list"); for (var i = 0; i < prs.length; i++) { if (prs[i].checked) { perms.push(prs[i].value); } } } var rets = api.hasPermission({ list: perms }); if (!one_per) { apialert('判断结果:' + JSON.stringify(rets)); return; } return rets; }
function reqPermission(one_per, callback) { var perms = new Array(); if (one_per) { perms.push(one_per); } else { var prs = document.getElementsByName("p_list_r"); for (var i = 0; i < prs.length; i++) { if (prs[i].checked) { perms.push(prs[i].value); } } } api.requestPermission({ list: perms, code: 100001 }, function (ret, err) { if (callback) { callback(ret); return; } var str = '请求结果:\n'; str += '请求码: ' + ret.code + '\n'; str += "是否勾选\"不再询问\"按钮: " + (ret.never ? '是' : '否') + '\n'; str += '请求结果: \n'; var list = ret.list; for (var i in list) { str += list[i].name + '=' + list[i].granted + '\n'; } apialert(str); console.log(JSON.stringify(ret)); }); }
function opWithPermission(perm) { if (!confirmPer(perm)) { return; } if ('calendar' == perm) { //操作日历 } else if ('camera' == perm) { api.getPicture({ sourceType: 'camera', mediaValue: 'pic', destinationType: 'url', }, function (ret, err) { if (ret) { apialert(JSON.stringify(ret)); } else { apialert(JSON.stringify(err)); } }); } else if ('contacts' == perm) { api.openContacts({ test: true }, function (ret, err) { if (ret && ret.status) { apialert(JSON.stringify(ret)); } else { apialert(JSON.stringify(err)); } }); } else if ('location' == perm) { api.getLocation(function (ret, err) { if (ret && ret.status) { apialert(JSON.stringify(ret)); } else { apialert(JSON.stringify(err)); } }); } else if ('microphone' == perm) { api.startRecord({ path: 'fs://perm-test.amr' }); } else if ('phone' == perm) { api.call({ type: 'tel', number: '10086' }); } else if ('sensor' == perm) { //操作身体传感器 } else if ('sms' == perm) { api.sms({ numbers: ['10086'], text: '余额', silent: true }); } else if ('storage' == perm) { api.readFile({ path: 'fs://test.txt' }, function (ret, err) { if (ret.status) { console.log('readFile: ' + ret.data); } else { apialert(err.msg + ": \n" + api.fsDir); } }); } }
function confirmPer(perm) { var has = hasPermission(perm); if (!has || !has[0] || !has[0].granted) { api.confirm({ title: '提醒', msg: '没有获得 ' + perm + " 权限\n是否前往设置?", buttons: ['去设置', '取消'] }, function (ret, err) { if (1 == ret.buttonIndex) { reqPermission(perm); } }); return false; } return true; }
</script></head>
<body> <div> <div id="wrap"> <div id='header'> <div class="back" tapmode="back-aconclick="api.closeWin()">返回</div> <h1>权限管理测试</h1> <div class="adpt"></div> </div> <div class='itemtitle'>一、判断权限</div> <div class='marg'>请选择一个或者多个权限进行判断:</div> <div class='marg'>日历&emsp;&emsp;&emsp;<input type="checkbox" name="p_list" value="calendar" /></div> <div class='marg'>相机&emsp;&emsp;&emsp;<input type="checkbox" name="p_list" value="camera" /></div> <div class='marg'>通讯录&emsp;&emsp;<input type="checkbox" name="p_list" value="contacts" /></div> <div class='marg'>位置信息&emsp;<input type="checkbox" name="p_list" value="location" /></div> <div class='marg'>麦克风&emsp;&emsp;<input type="checkbox" name="p_list" value="microphone" /></div> <div class='marg'>电话&emsp;&emsp;&emsp;<input type="checkbox" name="p_list" value="phone" /></div> <div class='marg'>身体传感器<input type="checkbox" name="p_list" value="sensor" /></div> <div class='marg'>短信&emsp;&emsp;&emsp;<input type="checkbox" name="p_list" value="sms" /></div> <div class='marg'>存储空间&emsp;<input type="checkbox" name="p_list" value="storage" /></div> <div class="clickbtn" tapmode="active" onclick="hasPermission()">点击开始判断</div> <div class='itemtitle'>二、请求权限</div> <div class='marg'>请选择一个或者多个权限进行请求:</div> <div class='marg'>日历&emsp;&emsp;&emsp;<input type="checkbox" name="p_list_r" value="calendar" /></div> <div class='marg'>相机&emsp;&emsp;&emsp;<input type="checkbox" name="p_list_r" value="camera" /></div> <div class='marg'>通讯录&emsp;&emsp;<input type="checkbox" name="p_list_r" value="contacts" /></div> <div class='marg'>位置信息&emsp;<input type="checkbox" name="p_list_r" value="location" /></div> <div class='marg'>麦克风&emsp;&emsp;<input type="checkbox" name="p_list_r" value="microphone" /></div> <div class='marg'>电话&emsp;&emsp;&emsp;<input type="checkbox" name="p_list_r" value="phone" /></div> <div class='marg'>身体传感器<input type="checkbox" name="p_list_r" value="sensor" /></div> <div class='marg'>短信&emsp;&emsp;&emsp;<input type="checkbox" name="p_list_r" value="sms" /></div> <div class='marg'>存储空间&emsp;<input type="checkbox" name="p_list_r" value="storage" /></div> <div class="clickbtn" tapmode="active" onclick="reqPermission()">点击开始请求</div> <div class='itemtitle'>三、需要权限的API操作</div> <div class='marg'>1、日历</div> <div class="clickbtn" tapmode="active" onclick="opWithPermission('calendar')">点击操作日历</div> <div class='marg'>2、相机</div> <div class="clickbtn" tapmode="active" onclick="opWithPermission('camera')">点击操作照相机</div> <div class='marg'>3、通讯录</div> <div class="clickbtn" tapmode="active" onclick="opWithPermission('contacts')">点击操作通讯录</div> <div class='marg'>4、位置信息</div> <div class="clickbtn" tapmode="active" onclick="opWithPermission('location')">点击操作位置信息</div> <div class='marg'>5、麦克风</div> <div class="clickbtn" tapmode="active" onclick="opWithPermission('microphone')">点击操作麦克风</div> <div class='marg'>6、电话</div> <div class="clickbtn" tapmode="active" onclick="opWithPermission('phone')">点击操作电话</div> <div class='marg'>7、身体传感器</div> <div class="clickbtn" tapmode="active" onclick="opWithPermission('sensor')">点击操作身体传感器</div> <div class='marg'>8、短信</div> <div class="clickbtn" tapmode="active" onclick="opWithPermission('sms')">点击操作短信</div> <div class='marg'>9、存储空间</div> <div class="clickbtn" tapmode="active" onclick="opWithPermission('storage')">点击操作存储空间</div> <br> </div> </div></body>
</html>
复制代码


发布于: 刚刚阅读数: 3
用户头像

APICloud

关注

一次编码多端运行,移动应用低代码开发平台 2020.12.22 加入

用友YonBuilder移动端低代码开发平台,快速构建高性能多端应用

评论

发布
暂无评论
使用APICloud开发app的动态权限及Android平台targetSdkVersion设置教程_android_APICloud_InfoQ写作社区