权限问题一直是Android开发人员很头疼的问题,比如经常会碰到朋友抱怨,这个录音权限判断不了或者是判断了没效果,只能另辟蹊径。其实我们开发过程中很容易碰到这些问题,但是这些问题都很零碎,所以也引不起大的注意,但是却容易发生一些意外状况.当然今天的主题是M版本的权限问题,这个问题阻拦了很多应用SDK升级23之路。就我自己的研究来看,我有好几个朋友未升级的理由都是,M版本的权限检查太恶心了,我们里面有那么多地方用到,改的很费劲,那么究竟如何呢?大家如果可以翻墙的话,可以去看看http://developer.android.com/intl/zh-cn/about/versions/marshmallow/android-6.0-changes.html这篇文档,里面着重提到了权限检查的问题。
我是插入的:推荐权限检查第三方https://github.com/jsmeli/PermissionCheck ,望多多支持,感激不尽
一个Android应用默认情况下是不拥有任何权限的, 这即是说, 在默认情况下, 一个应用是没有权利去进行一些可能会造成不好影响的操作的. 这些不好的影响可能是对其它应用,操作系统,或者是用户。
如果应用需要一些额外的能力,则它需要在AndroidManifest.xml中静态地声明相应的权限。
如果应用没有在manifest中声明权限, 却使用了相应的功能, 在调用到相应功能的时候, 将会抛出异常。
比如程序要发送一个请求,却忘记加Internet权限, 那么在发送这个请求的时候程序就会抛出异常,一般不会catch这个异常,所以程序直接就崩溃了:
Caused by: java.lang.SecurityException: Permission denied (missing INTERNET permission?)
在Android 6.0 (API 23) 发布之前, 所有的权限都在安装应用的时候显示给用户,用户选择安装则表示全部接受这些权限, 之后无法撤销对这些权限的授权。
Android 6.0开始, 一部分比较危险的权限需要在程序运行时显式弹框,请求用户授权。
至于什么时候弹这个框,由应用程序自己决定。
对于其他权限,认为不是很危险,所以仍然保持原来的做法,在用户安装应用程序时就予以授权。
还需要注意的是,在设置中,对于应用的危险权限,用户可以选择性地进行授权或者关闭。当然,在我们开发过程中其实注意两类就够了,一种就是直接可以申请的,另一种就是需要手动检查的,也就说涉及到隐私的权限。
直接申请的权限列表(Normal权限):
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
INTERNET
KILL_BACKGROUND_PROCESSES
MODIFY_AUDIO_SETTINGS
NFC
READ_SYNC_SETTINGS
READ_SYNC_STATS
RECEIVE_BOOT_COMPLETED
REORDER_TASKS
REQUEST_INSTALL_PACKAGES
SET_TIME_ZONE
SET_WALLPAPER
SET_WALLPAPER_HINTS
TRANSMIT_IR
USE_FINGERPRINT
VIBRATE
WAKE_LOCK
WRITE_SYNC_SETTINGS
SET_ALARM
INSTALL_SHORTCUT
UNINSTALL_SHORTCUT
需要进行检查的组(Dangerous权限)
- CALENDAR
- CAMERA
- CONTACTS
- LOCATION
- MICROPHONE
- PHONE
- SENSORS
- SMS
- STORAGE
其实我们大部分开发过程中的需求都是需要使用到Dangerous权限,为啥呢,因为产品经理的需求,产品想要的数据基本或多或少都会涉及到用户的一些隐私信息,所以苦逼的还是我们这些开发人员.......
权限检查的代码网上其实都大同小异,基本都是那么写的(以通讯录权限为例):
//动态获取权限 if (checkSelfPermission(Manifest.permission.READ_CONTACTS) != PackageManager .PERMISSION_GRANTED) { if (!ActivityCompat.shouldShowRequestPermissionRationale(context, Manifest.permission.READ_CONTACTS)) { //这种情况是该用户已经选择了不再提示的选项,需要手动给该用户做提示操作 } else { ActivityCompat.requestPermissions(this, new String[]{Manifest.permission .READ_CONTACTS}, PERMISSION_REQUEST); } } else { //获取通讯录信息 Intent intent = new Intent(Intent.ACTION_PICK, ContactsContract.Contacts.CONTENT_URI); startActivityForResult(intent, CHOOSE_CONTACT); }
如果没有权限并且进行了申请,那么是会有回调结果的
权限申请后的回调:
@Override public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) { super.onRequestPermissionsResult(requestCode, permissions, grantResults); switch (requestCode) { case PERMISSION_REQUEST: if (grantResults.length > 0 && grantResults[0] == PackageManager .PERMISSION_GRANTED) { //获取通讯录信息 Intent intent = new Intent(Intent.ACTION_PICK, ContactsContract.Contacts .CONTENT_URI); startActivityForResult(intent, CHOOSE_CONTACT); } break; } }
这就是一套正常流程了,看着是没有毛病的,但是我试了其他的手机,就有了问题了,联想的ZUK Z2手机,无论权限禁止与否,
checkSelfPermission(Manifest.permission.READ_CONTACTS)
该方法返回值都是0,也就是说默认是一直都有权限,那么就有问题了
这样走的是有权限的流程,就直接打开通讯录了,其实这时候权限是禁止的
找了各种资料发现6.0以下的手机都会返回0,这个问题好像有点眉目了,我就检查了代码还真是少了这个判断,那么我就又开始各种修改,觉得找到了问题的关键了,修改后代码如下:
//动态获取权限 if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { if (checkSelfPermission(Manifest.permission.READ_CONTACTS) != PackageManager .PERMISSION_GRANTED) { if (!ActivityCompat.shouldShowRequestPermissionRationale(context, Manifest.permission.READ_CONTACTS)) { //这种情况是该用户已经选择了不再提示的选项,需要手动给该用户做提示操作 } else { ActivityCompat.requestPermissions(this, new String[]{Manifest.permission .READ_CONTACTS}, PERMISSION_REQUEST); } } else { //获取通讯录信息 Intent intent = new Intent(Intent.ACTION_PICK, ContactsContract.Contacts.CONTENT_URI); startActivityForResult(intent, CHOOSE_CONTACT); } }
运行后结果
这就不对了,怎么会酱紫,这就不科学了,完全没有道理。
手机相关权限:
手机型号:
一看也确实是6.0.1的系统,这就比较无解了,那么碰上这种手机如何处理呢?
个人处理方式,就通讯录来说的话,该手机的现象是即使权限禁止,也能打开通讯录,因为代码判断拦不住,居然也能调用成功,但是回传联系人的时候使用ContentProvider读取时候就报异常了
所以为了满足需求,这边做了try{}catch(Exception e){}的处理,如果捕获到异常,弹出自定义的提示框,让用户去授予权限。
ps:授予权限的快捷键,最好是跳到设置里面的应用程序列表(附上代码)
Intent intent = new Intent(Settings.ACTION_APPLICATION_SETTINGS); startActivity(intent);
如果有碰到这样问题的大神求指教。
个人邮箱: jsmeli@163.com