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

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

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

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

 

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

 

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

 

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

 

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

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

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

image.gifimage.gif

将其中的targetSdkVersion更新为目标值,例如30
2、将manifest.xml置于你的/项目代码/res/目录下(widget/res/manifest.xml);
3、将你的app代码中所有涉及到需要动态权限的操作,参照示例中的代码,改造一遍(例如进行拍照录制视频等需要使用摄像头,以前的缺省处理中不需要申请摄像头权限,而开启动态权限后,必须在进行拍照之前,判断是否有摄像头权限,没有则进行申请,只有用户同意了摄像头权限才能进行接下来拍照的操作);
4、提交代码;
5、云编译界面勾选app所需的权限;
6、云编译app或自定义loader即可。

 

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

 

为保证动态权限尽可能适配更多厂商的手机以及顺利上线Google PlaytargetSdkVersion目前推荐设置为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>

image.gifimage.gif


目录
相关文章
|
30天前
|
缓存 搜索推荐 Android开发
安卓开发中的自定义控件实践
【10月更文挑战第4天】在安卓开发的海洋中,自定义控件是那片璀璨的星辰。它不仅让应用界面设计变得丰富多彩,还提升了用户体验。本文将带你探索自定义控件的核心概念、实现过程以及优化技巧,让你的应用在众多竞争者中脱颖而出。
|
30天前
|
Java Android开发 Swift
安卓与iOS开发对比:平台选择对项目成功的影响
【10月更文挑战第4天】在移动应用开发的世界中,选择合适的平台是至关重要的。本文将深入探讨安卓和iOS两大主流平台的开发环境、用户基础、市场份额和开发成本等方面的差异,并分析这些差异如何影响项目的最终成果。通过比较这两个平台的优势与挑战,开发者可以更好地决定哪个平台更适合他们的项目需求。
98 1
|
6天前
|
编解码 Java Android开发
通义灵码:在安卓开发中提升工作效率的真实应用案例
本文介绍了通义灵码在安卓开发中的应用。作为一名97年的聋人开发者,我在2024年Google Gemma竞赛中获得了冠军,拿下了很多项目竞赛奖励,通义灵码成为我的得力助手。文章详细展示了如何安装通义灵码插件,并通过多个实例说明其在适配国际语言、多种分辨率、业务逻辑开发和编程语言转换等方面的应用,显著提高了开发效率和准确性。
|
5天前
|
Android开发 开发者 UED
安卓开发中自定义View的实现与性能优化
【10月更文挑战第28天】在安卓开发领域,自定义View是提升应用界面独特性和用户体验的重要手段。本文将深入探讨如何高效地创建和管理自定义View,以及如何通过代码和性能调优来确保流畅的交互体验。我们将一起学习自定义View的生命周期、绘图基础和事件处理,进而探索内存和布局优化技巧,最终实现既美观又高效的安卓界面。
18 5
|
3天前
|
JSON Java Android开发
探索安卓开发之旅:打造你的第一个天气应用
【10月更文挑战第30天】在这个数字时代,掌握移动应用开发技能无疑是进入IT行业的敲门砖。本文将引导你开启安卓开发的奇妙之旅,通过构建一个简易的天气应用来实践你的编程技能。无论你是初学者还是有一定经验的开发者,这篇文章都将成为你宝贵的学习资源。我们将一步步地深入到安卓开发的世界中,从搭建开发环境到实现核心功能,每个环节都充满了发现和创造的乐趣。让我们开始吧,一起在代码的海洋中航行!
|
5天前
|
缓存 数据库 Android开发
安卓开发中的性能优化技巧
【10月更文挑战第29天】在移动应用的海洋中,性能是船只能否破浪前行的关键。本文将深入探讨安卓开发中的性能优化策略,从代码层面到系统层面,揭示如何让应用运行得更快、更流畅。我们将以实际案例和最佳实践为灯塔,引领开发者避开性能瓶颈的暗礁。
16 3
|
7天前
|
存储 IDE 开发工具
探索Android开发之旅:从新手到专家
【10月更文挑战第26天】在这篇文章中,我们将一起踏上一段激动人心的旅程,探索如何在Android平台上从零开始,最终成为一名熟练的开发者。通过简单易懂的语言和实际代码示例,本文将引导你了解Android开发的基础知识、关键概念以及如何实现一个基本的应用程序。无论你是编程新手还是希望扩展你的技术栈,这篇文章都将为你提供价值和启发。让我们开始吧!
|
1月前
|
Web App开发 安全 程序员
FFmpeg开发笔记(五十五)寒冬里的安卓程序员可进阶修炼的几种姿势
多年的互联网寒冬在今年尤为凛冽,坚守安卓开发愈发不易。面对是否转行或学习新技术的迷茫,安卓程序员可从三个方向进阶:1)钻研谷歌新技术,如Kotlin、Flutter、Jetpack等;2)拓展新功能应用,掌握Socket、OpenGL、WebRTC等专业领域技能;3)结合其他行业,如汽车、游戏、安全等,拓宽职业道路。这三个方向各有学习难度和保饭碗指数,助你在安卓开发领域持续成长。
58 1
FFmpeg开发笔记(五十五)寒冬里的安卓程序员可进阶修炼的几种姿势
|
13天前
|
Java API Android开发
安卓应用程序开发的新手指南:从零开始构建你的第一个应用
【10月更文挑战第20天】在这个数字技术不断进步的时代,掌握移动应用开发技能无疑打开了一扇通往创新世界的大门。对于初学者来说,了解并学习如何从无到有构建一个安卓应用是至关重要的第一步。本文将为你提供一份详尽的入门指南,帮助你理解安卓开发的基础知识,并通过实际示例引导你完成第一个简单的应用项目。无论你是编程新手还是希望扩展你的技能集,这份指南都将是你宝贵的资源。
42 5
|
12天前
|
设计模式 IDE Java
探索安卓开发:从新手到专家的旅程
【10月更文挑战第22天】 在数字时代的浪潮中,移动应用开发如同一座金矿,吸引着无数探险者。本文将作为你的指南针,指引你进入安卓开发的广阔天地。我们将一起揭开安卓平台的神秘面纱,从搭建开发环境到掌握核心概念,再到深入理解安卓架构。无论你是初涉编程的新手,还是渴望进阶的开发者,这段旅程都将为你带来宝贵的知识和经验的财富。让我们开始吧!