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

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

先介绍一下关于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>



目录
相关文章
|
1月前
|
XML Java 数据库
安卓项目:app注册/登录界面设计
本文介绍了如何设计一个Android应用的注册/登录界面,包括布局文件的创建、登录和注册逻辑的实现,以及运行效果的展示。
139 0
安卓项目:app注册/登录界面设计
|
3天前
|
缓存 容器 Perl
【Azure Container App】Container Apps 设置延迟删除 (terminationGracePeriodSeconds) 的解释
terminationGracePeriodSeconds : 这个参数的定义是从pod收到terminated signal到最终shutdown的最大时间,这段时间是给pod中的application 缓冲时间用来处理链接关闭,应用清理缓存的;并不是从idel 到 pod被shutdown之间的时间;且是最大时间,意味着如果application 已经gracefully shutdown,POD可能被提前terminated.
|
2月前
|
Java 数据库 Android开发
一个Android App最少有几个线程?实现多线程的方式有哪些?
本文介绍了Android多线程编程的重要性及其实现方法,涵盖了基本概念、常见线程类型(如主线程、工作线程)以及多种多线程实现方式(如`Thread`、`HandlerThread`、`Executors`、Kotlin协程等)。通过合理的多线程管理,可大幅提升应用性能和用户体验。
120 15
一个Android App最少有几个线程?实现多线程的方式有哪些?
|
2月前
|
存储 开发工具 Android开发
使用.NET MAUI开发第一个安卓APP
【9月更文挑战第24天】使用.NET MAUI开发首个安卓APP需完成以下步骤:首先,安装Visual Studio 2022并勾选“.NET Multi-platform App UI development”工作负载;接着,安装Android SDK。然后,创建新项目时选择“.NET Multi-platform App (MAUI)”模板,并仅针对Android平台进行配置。了解项目结构,包括`.csproj`配置文件、`Properties`配置文件夹、平台特定代码及共享代码等。
156 2
|
2月前
|
XML Android开发 数据格式
🌐Android国际化与本地化全攻略!让你的App走遍全球无障碍!🌍
在全球化背景下,实现Android应用的国际化与本地化至关重要。本文以一款旅游指南App为例,详细介绍如何通过资源文件拆分与命名、适配布局与方向、处理日期时间及货币格式、考虑文化习俗等步骤,完成多语言支持和本地化调整。通过邀请用户测试并收集反馈,确保应用能无缝融入不同市场,提升用户体验与满意度。
102 3
|
2月前
|
Java 数据库 Android开发
一个Android App最少有几个线程?实现多线程的方式有哪些?
本文介绍了Android应用开发中的多线程编程,涵盖基本概念、常见实现方式及最佳实践。主要内容包括主线程与工作线程的作用、多线程的多种实现方法(如 `Thread`、`HandlerThread`、`Executors` 和 Kotlin 协程),以及如何避免内存泄漏和合理使用线程池。通过有效的多线程管理,可以显著提升应用性能和用户体验。
69 10
|
1月前
|
安全 网络安全 Android开发
深度解析:利用Universal Links与Android App Links实现无缝网页至应用跳转的安全考量
【10月更文挑战第2天】在移动互联网时代,用户经常需要从网页无缝跳转到移动应用中。这种跳转不仅需要提供流畅的用户体验,还要确保安全性。本文将深入探讨如何利用Universal Links(仅限于iOS)和Android App Links技术实现这一目标,并分析其安全性。
216 0
|
2月前
|
Android开发
Android经典实战之Textview文字设置不同颜色、下划线、加粗、超链接等效果
本文介绍了 `SpannableString` 在 Android 开发中的强大功能,包括如何在单个字符串中应用多种样式,如颜色、字体大小、风格等,并提供了详细代码示例,展示如何设置文本颜色、添加点击事件等,助你实现丰富文本效果。
245 3
|
2月前
|
XML 数据库 Android开发
10分钟手把手教你用Android手撸一个简易的个人记账App
该文章提供了使用Android Studio从零开始创建一个简单的个人记账应用的详细步骤,包括项目搭建、界面设计、数据库处理及各功能模块的实现方法。
|
2月前
|
图形学 iOS开发 Android开发
从Unity开发到移动平台制胜攻略:全面解析iOS与Android应用发布流程,助你轻松掌握跨平台发布技巧,打造爆款手游不是梦——性能优化、广告集成与内购设置全包含
【8月更文挑战第31天】本书详细介绍了如何在Unity中设置项目以适应移动设备,涵盖性能优化、集成广告及内购功能等关键步骤。通过具体示例和代码片段,指导读者完成iOS和Android应用的打包与发布,确保应用顺利上线并获得成功。无论是性能调整还是平台特定的操作,本书均提供了全面的解决方案。
150 0

热门文章

最新文章