系统将权限公为两种:normal与dangerous。只有在dangerous时才会需要用户在运行时确认权限。
如果系统版本低于5.1或者targetSdkVersion低于23,则权限与原来相同:都是在安装的时候需要用户允许,如果用户不允许就无法进行安装。
如果系统版本是6.0及以上,并且targetSdkVersion是23及以上,在运行时才会提示用户进行权限认证。
在需要用到dangerous权限时,每一次都应该检测自己的应用是否具有该权限。如下:
int permission = ContextCompat.checkSelfPermission(this, Manifest.permission.READ_PHONE_STATE); if(permission == PackageManager.PERMISSION_GRANTED){ //当前应用有该权限 }else{ //当前应用没有该权限 }
当本应用没有该权限,并且需要使用到该权限时,需要向用户进行申请。使用ActivityCompat.requestPermissions进行权限申请。第二个参数为所要申请的权限的String数组,第三个参数为识别码,类似于startActivityForResult()中的识别码。
该方法是异步的,当用户响应了系统弹出的权限申请对话框后,会立即进行回调Activity的onRequestPermissionsResult()。
当已经申请过,并且被用户拒绝,同时又勾选了"不再询问"。这种情况下调用requestPermissions()是没有任何反应的:系统不会再弹出向用户申请权限的dialog。
在某些时候,我们需要向用户解释自己为何需要该权限:比如很难理解为什么一个相机应用会需要读取联系人的权限。此时,我们就需要在一个合适的机会告诉用户原因。
可以调用ActivityCompat.shouldShowRequestPermissionRationale()进行判断是否需要向用户解释。只有当以前申请过权限,但被用户拒绝的情况下,该方法才会返回true。如果用户拒绝该权限,并且勾选了不再询问,或者系统禁止应用获取该权限,那么该方法就返回false。
当用户看完解释之后,应该再次申请该权限。并且应该在申请权限之前进行解释原因。第一次时该方法会返回false,所以不会影响第一次的权限申请。
使用ActivityCompat.requestPermissions()异步方法进行权限申请时,最终会回调onRequestPermissionsResult()方法。该方法的第一个参数就是requestPermissions()中传递的识别码。
系统将权限分成不同的组,同一组的权限中只有一个被用户同意过,那么在使用到其他权限时系统会自动同意该权限。如:用户同意使用READ_CONTACTS权限,那么在使用到WRITE_CONTACTS权限时,就不会再次提示用户,让用户进行确认,而是直接同意该权限的使用。
但上面的逻辑在后面的android版本中可能会进行修改,因此每当使用到dangerous权限时,都需要进行申请。
另外,当用户拒绝某个权限,并勾选了"不再询问"时,调用requestPermissions()方法,会立即执行onRequestPermissionResult(),就好像用户直接拒绝该权限一样。
使用dangerous权限时应该注意几点:
1,尽量使用intent的,而不是自己直接申请权限。比如需要拍照功能,可以申请CAMERA权限,然后自己调用相机功能进行拍照,也可以调用系统的进行。如果没有特殊要求,应尽量使用后者。
2,尽量减少申请权限的次数,并且只要在需要的时候才进行申请。
3,尽量避免一次性申请过多权限,在需要的时候才申请。
4,对应用必须的一个或多个权限,应该在应用启动的时候就进行申请。比如相机应用应该在应用启动的时候就申请相机权限,这样做不会使用户感到惊讶。
5,解释申请权限的原因。调用requestPermissions()申请权限时,并不会向用户解释需要该权限的原因,这样可能使用户感觉疑惑,因此最好在申请权限之前进行说明。
6,dangerous权限及分组。如下:
private void requestPermissions(){ int permission = ContextCompat.checkSelfPermission(this, Manifest.permission.READ_PHONE_STATE); if (permission == PackageManager.PERMISSION_GRANTED) { Toast.makeText(MainActivity.this, "你有该权限了", Toast.LENGTH_SHORT).show(); } else { if (ActivityCompat.shouldShowRequestPermissionRationale(this, Manifest.permission.READ_PHONE_STATE)) { AlertDialog.Builder builder = new AlertDialog.Builder(this);//使用v7包中的,界面比原生好 builder.setTitle("我要权限"); builder.setMessage("亲,给个权限呗!不给不让用!!!"); AlertDialog dialog = builder.create(); dialog.show(); dialog.setOnDismissListener(new DialogInterface.OnDismissListener() { @Override public void onDismiss(DialogInterface dialog) { ActivityCompat.requestPermissions(MainActivity.this, new String[]{Manifest.permission.READ_PHONE_STATE}, 100); } }); } else { Toast.makeText(MainActivity.this, "第一次啊", Toast.LENGTH_SHORT).show(); ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.READ_PHONE_STATE}, 100); } } } @Override public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) { if(requestCode == 100){ if(grantResults[0] == PackageManager.PERMISSION_GRANTED){ Toast.makeText(MainActivity.this, "我艹,不容易,你同意了?", Toast.LENGTH_SHORT).show(); }else{ Toast.makeText(MainActivity.this, "好吧,我还会再回来的!!", Toast.LENGTH_SHORT).show(); } } }首先判断有没有权限,如果有权限应该进行原本的操作。
如果没有该权限,就需要判断是不是向用户解释为何需要该权限了。
第一次启动时,shouldShowRequestPermissionRationale()返回false,此时可以申请权限。如果已经启动过,用户拒绝过,同时不允许再次询问的话,该方法也会返回false,但requestPermissions()是不会进行任何操作的,所以可以在shouldShowRequestPermissionRationale()返回false时去申请权限。
如果不是第一次启动,并且用户重新拒绝过,同时允许再次询问,那么shouldShowRequestPermissionRationale()会返回true,此时需要向用户进行解释,解释完毕之后再次申请权限。
申请权限时,系统会弹一个dialog(该dialog完全由系统控制),用户响应该dialog之后,就会回调onRequestPermissionsResult()
上述代码在低版本中不会产生任何效果。