Android 6.0开始,对敏感权限需要动态申请,首先我们需要引入android.support.v4的兼容包,然后使用ActivityCompat这个类来处理权限。
比如最常用的
ActivityCompat.checkSelfPermission(...)
ActivityCompat.requestPermissions(...)
ActivityCompat.shouldShowRequestPermissionRationale(...)
第一个是检查某个权限是否授权
第二个是申请权限
第三个是判断是否需要合理显示授权对话框。具体含义参考文末。
有两个原因
第一,如果应用的minSdkVersion小于23,那么IDE会提示你这些方法在6.0以下不存在,编译会不通过。
第二,ActivityCompat处理了一些兼容性判断,比如requestPermissions的实现里加了判断api 23。
一种方法是使用ActivityCompat或ContextCompat,调用checkSelfPermission方法。
另一种方法使用Activity提供的方法:checkPermission(String permission, int pid, int uid),需要传入当前进程id和uid。
checkSelfPermission方法其实也是调用checkPermission(String permission, int pid, int uid)。
动态权限示例代码:
package com.devnn.testdemo;
import android.Manifest;
import android.app.Activity;
import android.content.pm.PackageManager;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.v4.app.ActivityCompat;
import android.widget.Toast;
public class MainActivity extends Activity {
String permission = Manifest.permission.READ_EXTERNAL_STORAGE;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
if (ActivityCompat.checkSelfPermission(this, permission) != PackageManager.PERMISSION_GRANTED) {
ActivityCompat.requestPermissions(this, new String[]{permission}, 100);
}else{
Toast.makeText(this, "已有读SDK卡权限", Toast.LENGTH_SHORT).show();
}
}
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
if(requestCode==100){
if(grantResults[0]==PackageManager.PERMISSION_GRANTED){
Toast.makeText(this, "授权成功", Toast.LENGTH_SHORT).show();
}else if(ActivityCompat.shouldShowRequestPermissionRationale(this,permission)==false){
Toast.makeText(this, "拒绝并勾选了不再提醒,不会弹了,需要引导用户去设置页", Toast.LENGTH_SHORT).show();
}else{
Toast.makeText(this, "拒绝了授权,下次还会再弹", Toast.LENGTH_SHORT).show();
}
}
}
}
记得先要在AndroidManifest.xml里声明权限。
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
在弹出系统的权限对话框前,弹出自已的对话框,用来说明权限用途,会更加友好一点。直接使用Materia风格的Dialog。
public void showPermissionDialog() {
AlertDialog.Builder builder = new AlertDialog.Builder(this, android.R.style.Theme_DeviceDefault_Light_Dialog_NoActionBar_MinWidth);
builder.setMessage("需要读SD卡权限,用来获取照片\n需要获取手机状态权限,用来获取设备号\n需要获取联系人权限,用来...");
builder.setTitle("权限说明");
builder.setCancelable(true);
builder.setPositiveButton("申请", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
ActivityCompat.requestPermissions(MainActivity.this, new String[]{permission}, 100);
}
});
builder.show();
}
只有危险权限(或叫敏感权限)才需要申请,而且危险权限组中的某一个权限授权了,组内其它权限也被同时授权。
关于 ActivityCompat.shouldShowRequestPermissionRationale(...)
方法的含义,有必要说明。从字面意思理解,即是否需要合理显示申请权限对话框,那么什么时候显示才是合理的呢?我们发现以下规律。
在第一次申请权限时,它返回了false
用户选择拒绝并没有勾选不再提醒时,它返回true
而用户选择拒绝并勾选不再提醒(永久拒绝),它返回了false
因此这个方法并不能用来判断是否需要显示自已的权限说明对话框。它只能在权限回调里用来判断是否要引导用户到设置页打开权限。