Android 6.0以上 需要运行时申请的权限

http://www.cnblogs.com/tangs/articles/6377347.html

自从Android6.0发布以来,在权限上做出了很大的变动,不再是之前的只要在manifest设置就可以任意获取权限,而是更加的注重用户的隐私和体验,不会再强迫用户因拒绝不该拥有的权限而导致的无法安装的事情,也不会再不征求用户授权的情况下,就可以任意的访问用户隐私,而且即使在授权之后也可以及时的更改权限。这就是6.0版本做出的更拥护和注重用户的一大体现。

一、认知

今天我们就来学习下Android6.0的权限管理。

Android6.0系统把权限分为两个级别:

一个是Normal Permissions,即普通权限,这类权限不会潜藏有侵害用户隐私和安全的问题,比如,访问网络的权限,访问WIFI的权限等;

另一类是Dangerous Permissions,即危险权限,这类权限会直接的威胁到用户的安全和隐私问题,比如说访问短信,相册等权限。

但是到底哪些是普通权限和危险权限呢,这里给出分类,大家在使用时以便参考。

1、Normal Permissions (普通权限)

ACCESS_LOCATION_EXTRA_COMMANDS

ACCESS_NETWORK_STATE

ACCESS_NOTIFICATION_POLICY

ACCESS_WIFI_STATE

BLUETOOTH

BLUETOOTH_ADMIN

BROADCAST_STICKY

CHANGE_NETWORK_STATE

CHANGE_WIFI_MULTICAST_STATE

CHANGE_WIFI_STATE

DISABLE_KEYGUARD

EXPAND_STATUS_BAR

GET_PACKAGE_SIZE

INSTALL_SHORTCUT

INTERNET

KILL_BACKGROUND_PROCESSES

MODIFY_AUDIO_SETTINGS

NFC

READ_SYNC_SETTINGS

READ_SYNC_STATS

RECEIVE_BOOT_COMPLETED

REORDER_TASKS

REQUEST_IGNORE_BATTERY_OPTIMIZATIONS

REQUEST_INSTALL_PACKAGES

SET_ALARM

SET_TIME_ZONE

SET_WALLPAPER

SET_WALLPAPER_HINTS

TRANSMIT_IR

UNINSTALL_SHORTCUT

USE_FINGERPRINT

VIBRATE

WAKE_LOCK

WRITE_SYNC_SETTINGS

使用以上权限是不会威胁到用户安全的,所以这类权限是可以直接的在manifest里面直接的使用,而且在安装后也会直接的生效了。

2、Dangerous Permissions (危险权限)

SMS(短信)

SEND_SMS

RECEIVE_SMS

READ_SMS

RECEIVE_WAP_PUSH

RECEIVE_MMS

STORAGE(存储卡)

READ_EXTERNAL_STORAGE

WRITE_EXTERNAL_STORAGE

CONTACTS(联系人)

READ_CONTACTS

WRITE_CONTACTS

GET_ACCOUNTS

PHONE(手机)

READ_PHONE_STATE

CALL_PHONE

READ_CALL_LOG

WRITE_CALL_LOG

ADD_VOICEMAIL

USE_SIP

PROCESS_OUTGOING_CALLS

CALENDAR(日历)

READ_CALENDAR

WRITE_CALENDAR

CAMERA(相机)

CAMERA

LOCATION(位置)

ACCESS_FINE_LOCATION

ACCESS_COARSE_LOCATION

SENSORS(传感器)

BODY_SENSORS

MICROPHONE(麦克风)

RECORD_AUDIO

危险权限和普通权限也有区别,普通权限是单条的权限,而危险权限是以组展示的,也就是说,当你接受一个危险权限时,不但但接受的是界面上展示的这一个权限,而是它所在这个组里面的其他所有访问权限也将会被自动获取权限,比如,一旦WRITE_CONTACTS被授权了,App也有READ_CONTACTS和GET_ACCOUNTS的权限了。

值得注意的是,这类权限也是需要在manifest中注册的。

ok,光说不练不是咱的风格,咱写东西都是基于自己遇到的问题,然后认真的学习后才记录下来的。一方面巩固自己的知识,另一方面也希望能帮助他人提供一点解决方案。

二、实战

实战部分分为几种情况,因为根据我们的目标SDK版本和Android真机版本的不同会有不同的情景,针对普通权限大家都熟悉,就不介绍了,下面一一介绍危险权限的使用情景:

在介绍使用情景之前,先看下我的开发和真机的Android版本。

我们这里以读取短信息为例讲解整个权限的使用:

1、没有访问权限的情况下:

首先我们先来设计下布局,如下:

看下代码,很简单,就直接读取短息:

然后,点击界面上的“读取收件箱中的短信”,相信大家都会知道发生什么情况,果然不出意外的程序直接崩溃了,打下日志:

日志中很清晰的告诉我们,这个异常是因为没有权限而造成的,那么我们就直接给它加上读取短信的权限来看看吧。

2、在manifest中添加了权限:

在manifest中加了对读取短信的权限,你应该很高兴的等待着总共有多少条短信出现在我们的界面上,但是,事实很让人崩溃:

再次出现了没有权限的异常,这是为什么呢?

这里我们先不解决这个问题,先来想象一种实际的情况,假如你现有的APP里面有很多使用到了危险权限,有时候你并完全清楚到底在哪里使用了,但是你的目标版本又是像我的版本一样指向了6.0,而有可能用户的手机是6.0以上的版本,那么这时候你的APP就有可能会出现这种,那么在你还没查清楚有哪些地方使用了危险权限是,该怎么解决呢?

那么你可以这么解决:

修改你build.gradle 中的 targetSdkVersion 目标版本号:

然后手机版本还是6.0以上,来看看结果:

可以了,哈哈,你很高兴,确实是可以了。

那么聪明的你或许意识到什么了,是的,以版本23,也就是android6.0位分割线,我们可以得出一个小结论:

当targetSdkVersion >= 23,且真机版本 >= 23时,即使在manifest中添加了相应的危险权限,在没有做相应的处理时(至于怎么处理后面会讲),还时会出现限权的异常,这时manifest中的危险权限并没有起作用,但是还必须声明。

当targetSdkVersion < 23,且真机版本 >= 23时,我们并没有做任何的相关处理,就得到了想要的访问权限,这说明在manifest中申请的危险权限起作用了。

我们在来看另外一种情况,就是,假如我的手机比较旧,还没更新6.0的系统,这种情况下又该是什么情况呢?

这次我们用个4.4.4版本的模拟机

目标targetSdkVersion 为21 来看看结果:

也是可以的,0条信息是因为我的模拟机上没短信,这个数字多少和我们没有关系。假如targetSdkVersion 为23呢,来看看结果:

很清晰的看出,我们又得到了正确的结果。

由此我们也得到了一个小结论:

当我们的真机系统版本 < 23时,不管我们的targetSdkVersion 值是否大于23,都不会影响我们在manifest里面申请的权限,也就是说这时候真机的系统版本在起着主导作用。

由上面的几条结论,我们应该很清晰的知道了访问权限在真机中的使用状况,但是我们的手机在升级,版本也会越来越高,因此我们现在的应用不可能一直只支持低版本的使用也不考虑兼顾高版本。所以现在APP权限升级是必然的趋势。

那么现在回来解决上面遗留的问题,当真机和目标版本都大于6.0时出现的权限异常我们该怎么解决呢?

主要分为三个步骤:

1:检查是否拥有权限

2:假如没有权限,则申请权限

3:处理权限回调

下面我们分别来看看这几个步骤。

1:检查是否拥有权限

检查是否已拥有了权限,可以使用ContextCompat.checkSelfPermission(Context context, String permission);

checkSelfPermission方法中有两个参数,分别是上下文,以及所申请的权限。

如果有权限,请让它直接去读取短信信息。如果没有权限则去申请。

2:申请权限

申请权限则是使用:

public static void requestPermissions(final Activity activity,final String[] permissions, final int requestCode) {}

requestPermissions方法中需要三个参数,当前的activity,所申请的权限,可以是多个,最后就是请求码,既然有请求码说明它会有一个回调,也就是我们下面要讲的处理回调。

3:处理权限回调

处理权限回调,需要在Activity中重写onRequestPermissionsResult方法:

然后在方法内判断用户是授权了该权限组还是拒绝授权,如果授权则就去获取短信信息,否则,在这里我只是显示了一个toast提示框。

这里再次说明下,权限组内只要有一个被授权,其他的权限也就有了权限,这也是为什么直接使用grantResults[0] == PackageManager.PERMISSION_GRANTED的原因。

ok,下面来具体的界面显示:

我们可以看到,当我们第一次点击读取短信时,它会先检查该应用是否有权限,如果没有,就去申请,这里在界面上对应的就是显示一个授权的对话框,第一次我们选择了拒绝授权,然后在回调里面就会对应先打印了我们的一个toast消失提醒我们拒绝了授权,但是当我们再次需要读取短信时,它还会去申请授权,这时我们允许授权,然后我们就看到了,在显示短信条数的TextView显示了短信的条数。(这里0条是因为的用的模拟器没有短信,这不是重点。)

值得提醒的事,当我们第一次选择拒绝授权时,当再次点击读取短信时,这时在授权对话框中会多一个“不再提醒”的提示,当我们在拒绝了授权,并选择不再提醒时,那么会出现什么情况呢?请看演示:

当多次拒绝并选择不提提醒,那么下次再去读取就不会在去申请授权,而是直接在回调中说明用户已拒绝授权。

那么这时候假如用户出于某种需要必须得给应用授权该怎么做呢,其实很简单,在回调中,提醒用户去“设置”里面手动给应用授权,或是发个广播打开设置界面等等都可,这里和我显示的提醒“权限已被拒绝”基本一样,只需在稍微优化即可,这里不在演示。

其实到这里已经差不多讲完,但是,有一个方法我们可以留一下,那就是shouldShowRequestPermissionRationale,这个方法默认返回false,但当用户在上一次已经拒绝过这个权限申请时,再次需要申请该权限时,就会返回ture,它的寓意是你已经拒绝了一次,结果又弹出个授权框,你需要给我一个解释,为什么要授权,也就是说对多次授权这个权限做出解释,以便用户知道为什么必须授权了才能够完成他操作。

下面,来看看它的使用:

我这里就简单的弹出个对话框,说明下为什么要用这个权限,然后再次去调用这个申请的权限的方法了,大家可以同回调的方法一起封装下,可以更好的应用。

看下界面操作:

讲到这里基本差不地讲完了,这里只是讲了单个申请权限,多个一起也是可以的,大家可以自己试试,基本是一样的操作,另外在说明一点,可能我们一个应用里,需要多出的使用到危险权限,这样就造成我们需要多次重写一样的代码,很不便利,所以网上也就出现了很多关于权限框架的开源代码,大家可以自行的使用。

关于打电话的一个完整代码:

callPhoneIntent =newIntent(Intent.ACTION_DIAL);

callPhoneIntent.setData(uri);//如果有权限(实际这个方法只是检测你的APP是否使用了某个权限,但是不能检测是否被限制了)if(ContextCompat.checkSelfPermission(SkipWebActivity.this, Manifest.permission.CALL_PHONE) ==PackageManager.PERMISSION_GRANTED) {

startActivity(callPhoneIntent);

}else{//当多次拒绝并选择不提提醒,那么下次再去读取就不会在去申请授权,而是直接在回调中说明用户已拒绝授权if(ActivityCompat.shouldShowRequestPermissionRationale(SkipWebActivity.this,Manifest.permission.CALL_PHONE)){

showRequestPermission();

}else{//只被拒绝过一次该权限的申请ActivityCompat.requestPermissions(SkipWebActivity.this,newString[]{Manifest.permission.CALL_PHONE},REQUEST_CALLPHONE_PERMISSION);

}

}/*** 申请打电话的权限得的对话框,之前被勾选不再提醒申请权限*/privatevoidshowRequestPermission(){newAlertDialog.Builder(SkipWebActivity.this)

.setTitle("申请权限")

.setMessage("请求权限拨打电话")

.setPositiveButton("同意",newDialogInterface.OnClickListener() {

@OverridepublicvoidonClick(DialogInterface dialogInterface,inti) {//申请权限ActivityCompat.requestPermissions(SkipWebActivity.this,newString[]{Manifest.permission.CALL_PHONE},REQUEST_CALLPHONE_PERMISSION);

}

})

.setNegativeButton("拒绝",newDialogInterface.OnClickListener() {

@OverridepublicvoidonClick(DialogInterface dialogInterface,inti) {//拒绝授权}

})

.show();

}

@OverridepublicvoidonRequestPermissionsResult(intrequestCode, @NonNull String[] permissions, @NonNullint[] grantResults) {if(requestCode ==REQUEST_CALLPHONE_PERMISSION){if(grantResults[0] ==PackageManager.PERMISSION_GRANTED){

startActivity(callPhoneIntent);

}else{

T.showS("拒绝了打电话权限");

}

}super.onRequestPermissionsResult(requestCode, permissions, grantResults);

}

工具类的使用:https://github.com/googlesamples/easypermissions

例子,添加的依赖如下:

//6.0运行时权限工具类compile 'pub.devrel:easypermissions:0.4.2'

publicclassTestActivityextendsAppCompatActivityimplementsEasyPermissions.PermissionCallbacks{/*** 请求相机与存取文件权限的请求码*/privatestaticfinalintRP_CAMERA_AND_STORAGE = 1;

@OverrideprotectedvoidonCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);

setContentView(R.layout.activity_test);

methodRequiresTwoPermission();

}/*** 请求两个权限*/privatevoidmethodRequiresTwoPermission() {

String[] perms={Manifest.permission.CAMERA, Manifest.permission.WRITE_EXTERNAL_STORAGE};if(EasyPermissions.hasPermissions(this, perms)) {

Toast.makeText(this,"已经有这些权限了,该干嘛干嘛吧",Toast.LENGTH_SHORT).show();

}else{//缺少某些权限,请求权限EasyPermissions.requestPermissions(this, "请求相机与存取文件的权限",RP_CAMERA_AND_STORAGE, perms);

}

}

@OverridepublicvoidonRequestPermissionsResult(intrequestCode, @NonNull String[] permissions, @NonNullint[] grantResults) {super.onRequestPermissionsResult(requestCode, permissions, grantResults);//结果转发给EasyPermissions来处理EasyPermissions.onRequestPermissionsResult(requestCode, permissions, grantResults,this);

}/*** 权限同意的回调

*@paramrequestCode

*@paramperms*/@OverridepublicvoidonPermissionsGranted(intrequestCode, Listperms) {

Log.e("tag", "授权了:" + requestCode + ":" +perms.size());if(requestCode ==RP_CAMERA_AND_STORAGE){

Toast.makeText(this,"用户已经同意些权限了,该干嘛干嘛吧",Toast.LENGTH_SHORT).show();

}

}/*** 权限被拒绝的回调

*@paramrequestCode

*@paramperms 代表拒绝的权限*/@OverridepublicvoidonPermissionsDenied(intrequestCode, Listperms) {

Log.e("tag", "拒绝了:" + requestCode + ":" +perms.size());if(requestCode ==RP_CAMERA_AND_STORAGE){

Toast.makeText(this,"用户拒绝了某些权限了",Toast.LENGTH_SHORT).show();//检查是否有永久的权限列表中至少有一个权限是永久的被拒绝(用户点击“永不再问”)。if(EasyPermissions.somePermissionPermanentlyDenied(this, perms)) {newAppSettingsDialog.Builder(this, "为了您能正常使用,请开启权限!")

.setTitle("提示")

.setPositiveButton("去设置")

.setNegativeButton("取消",null)

.setRequestCode(RP_CAMERA_AND_STORAGE)

.build()

.show();

}

}

}

@OverridepublicvoidonActivityResult(intrequestCode,intresultCode, Intent data) {super.onActivityResult(requestCode, resultCode, data);if(requestCode ==RP_CAMERA_AND_STORAGE) {//在用户从应用程序设置界面返回后做一些事情Toast.makeText(this, "用户设置了什么", Toast.LENGTH_SHORT) .show();

}

}

}

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 214,717评论 6 496
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,501评论 3 389
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 160,311评论 0 350
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,417评论 1 288
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,500评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,538评论 1 293
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,557评论 3 414
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,310评论 0 270
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,759评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,065评论 2 330
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,233评论 1 343
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,909评论 5 338
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,548评论 3 322
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,172评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,420评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,103评论 2 365
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,098评论 2 352

推荐阅读更多精彩内容