Android6.0运行时权限处理
Android6.0的发布介绍了一种新的权限机制。用户可以在运行时直接管理应用程序的权限,这个功能提升了权限控制的可见性和可控性,同时简化了安装和自动升级过程,用户可以单独撤销或者授予应用程序某项权限,对应用拥有更多的控制权。
应用程序target是Android 6.0及以上(API level 23),要确保在运行时检查和请求权限,在6.0以后有些权限属于危险权限,清单中声明权限,不能保证权限授权通过,为了确定你的app是否授予某个权限,通过checkSelfPermission()方法判断,请求权限使用requestPermissions()方法。即使你的app不是target Android 6.0,你也应该在新的权限机制下测试你的应用。
本文参考博文:
Hongyang:Android 6.0 运行时权限处理完全解析
1. Android权限管理
Google将权限分为两类,一类是普通权限(Normal Permissions),这类权限一般不涉及用户隐私,也不需要用户进行授权,如网络访问,手机震动等,另外一类是危险权限(Dangerous Permission),涉及用户隐私,需要用户授权,如对sd卡读取、访问用户手机通讯录、拨打电话等。
关于Android的危险权限:Dangerous Permissions:
group:android.permission-group.CONTACTS
permission:android.permission.WRITE_CONTACTS
permission:android.permission.GET_ACCOUNTS
permission:android.permission.READ_CONTACTS
group:android.permission-group.PHONE
permission:android.permission.READ_CALL_LOG
permission:android.permission.READ_PHONE_STATE
permission:android.permission.CALL_PHONE
permission:android.permission.WRITE_CALL_LOG
permission:android.permission.USE_SIP
permission:android.permission.PROCESS_OUTGOING_CALLS
permission:com.android.voicemail.permission.ADD_VOICEMAIL
group:android.permission-group.CALENDAR
permission:android.permission.READ_CALENDAR
permission:android.permission.WRITE_CALENDAR
group:android.permission-group.CAMERA
permission:android.permission.CAMERA
group:android.permission-group.SENSORS
permission:android.permission.BODY_SENSORS
group:android.permission-group.LOCATION
permission:android.permission.ACCESS_FINE_LOCATION
permission:com.google.android.gms.permission.CAR_SPEED
permission:android.permission.ACCESS_COARSE_LOCATION
group:android.permission-group.STORAGE
permission:android.permission.READ_EXTERNAL_STORAGE
permission:android.permission.WRITE_EXTERNAL_STORAGE
group:android.permission-group.MICROPHONE
permission:android.permission.RECORD_AUDIO
group:android.permission-group.SMS
permission:android.permission.READ_SMS
permission:android.permission.RECEIVE_WAP_PUSH
permission:android.permission.RECEIVE_MMS
permission:android.permission.RECEIVE_SMS
permission:android.permission.SEND_SMS
permission:android.permission.READ_CELL_BROADCASTS
危险权限我们可以通过adb命令查看:
adb shell pm list permissions -d -g
查看权限以及详细信息:
adb shell pm list permissions -d -g -f
查看到危险的权限是一组一组的,如果某一组里面的某个危险权限已被用户授权,那么系统会立即授权其他的权限,而不需要再向用户授权,比如你的app已经授权READ_SMS,那么当你的app需要SEND_SMS,系统会自动授权。弹出的dialog的文本是对权限组的说明,而非单个权限说明。且dialog不可以自定义。
2. 权限相关处理
2.1 添加权限
在AndroidManifest.xml中正常添加权限。
2.2 检查权限
if (ContextCompat.checkSelfPermission(this,
Manifest.permission.CALL_PHONE)
!= PackageManager.PERMISSION_GRANTED) {
// 向用户申请权限
} else {
// 执行权限通过后的事件
}
这里涉及到一个API,ContextCompat.checkSelfPermission(),主要用于检测某个权限是否已经被授予,方法返回值为PackageManager.PERMISSION_DENIED或者PackageManager.PERMISSION_GRANTED。当返回DENIED就需要进行申请授权了。查看源码:
源码:
/**
* Determine whether <em>you</em> have been granted a particular permission.
* 确定你是否获得了一个特定的权限
*
* @param permission The name of the permission being checked.
* 参数permission:你要检测的权限
*
* @return {@link android.content.pm.PackageManager#PERMISSION_GRANTED} if you have the
* permission, or {@link android.content.pm.PackageManager#PERMISSION_DENIED} if not.
* 返回值PackageManager.PERMISSION_GRANTED授权已成功
* PackageManager.PERMISSION_DENIED 授权被拒绝
*
* @see android.content.pm.PackageManager#checkPermission(String, String)
*/
public static int checkSelfPermission(@NonNull Context context, @NonNull String permission) {
if (permission == null) {
throw new IllegalArgumentException("permission is null");
}
return context.checkPermission(permission, android.os.Process.myPid(), Process.myUid());
}
关于返回值,同样到源码里面可以查看:
/**
* Permission check result: this is returned by {@link #checkPermission}
* if the permission has been granted to the given package.
*/
public static final int PERMISSION_GRANTED = 0;
/**
* Permission check result: this is returned by {@link #checkPermission}
* if the permission has not been granted to the given package.
*/
public static final int PERMISSION_DENIED = -1;
检查完权限之后,如果系统没有授权成功,需要向用户申请权限。
2.3 申请权限
ActivityCompat.requestPermissions(this,
new String[]{Manifest.permission.CALL_PHONE},
MY_PERMISSIONS_REQUEST_CALL_PHONE);
该方法是异步的,第一个参数是Context;第二个参数是需要申请的权限的字符串数组;第三个参数为requestCode,主要用于回调的时候检测。
2.4 处理权限申请回调
@Override
public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
if (requestCode == MY_PERMISSIONS_REQUEST_CALL_PHONE) {
if (grantResults[0] == PackageManager.PERMISSION_GRANTED) {
// 权限申请成功,可以执行事件了
} else {
// Permission Denied 权限被拒绝
// 提示对话框等
}
return;
}
}
对于权限的申请结果,首先验证requestCode定位到你的申请,然后验证grantResults对应于申请的结果,这里的数组对应于申请时的第二个权限字符串数组。如果你同时申请两个权限,那么grantResults的length就为2,分别记录你两个权限的申请结果。
2.5 权限彻底禁止
// Should we show an explanation?
if (ActivityCompat.shouldShowRequestPermissionRationale(thisActivity,
Manifest.permission.CALL_PHONE))
}
当第一次向用户询问权限时,用户拒绝,而后再申请的弹窗会提示以后不再提示选项,如果用户勾选并拒绝权限,表示用户彻底拒绝权限,那么ActivityCompat.shouldShowRequestPermissionRationale将返回false,一般我们进行判断用户是彻底拒绝,那么想要再申请权限,可以提示用户到设置去修改。
3. 案例实践
public class MainActivity extends AppCompatActivity {
private static final int CALL_PHONE_RESQUESTCODE = 1;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Button btnCall = (Button) findViewById(R.id.btnCall);
btnCall.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
testCall();
}
});
}
public void testCall() {
// 判断权限是否未被系统授权
if (ContextCompat.checkSelfPermission(this,
Manifest.permission.CALL_PHONE)
!= PackageManager.PERMISSION_GRANTED) {
Log.i("TAG", "is" + ActivityCompat.shouldShowRequestPermissionRationale(this, Manifest.permission.CALL_PHONE));
// 重新请求权限
ActivityCompat.requestPermissions(this,
new String[]{Manifest.permission.CALL_PHONE},
CALL_PHONE_RESQUESTCODE);
} else {
// 否则直接执行事件了
callPhone();
}
}
public void callPhone() {
Intent intent = new Intent(Intent.ACTION_CALL);
Uri data = Uri.parse("tel:" + "10010");
intent.setData(data);
startActivity(intent);
}
@Override
public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
if (requestCode == CALL_PHONE_RESQUESTCODE) {
if (grantResults[0] == PackageManager.PERMISSION_GRANTED) {
callPhone();
} else {
// Permission Denied,权限被拒绝
// Toast.makeText(MainActivity.this, "Permission Denied", Toast.LENGTH_SHORT).show();
// 完全拒绝,可以提示用户跳到设置页面
if (!ActivityCompat.shouldShowRequestPermissionRationale(this, Manifest.permission.CALL_PHONE)) {
showDialog();
}
}
return;
}
}
public void showDialog() {
new AlertDialog.Builder(this)
.setMessage("权限被拒绝,请到权限管理里面设置!")
.setPositiveButton("去设置", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
Intent intent = new Intent();
intent.setAction(Settings.ACTION_APPLICATION_DETAILS_SETTINGS);
Uri uri = Uri.fromParts("package", MainActivity.this.getPackageName(), null);
intent.setData(uri);
MainActivity.this.startActivity(intent);
}
})
.setNegativeButton("取消", null)
.create().show();
}
}
基本的处理过程到此结束。