简介
自从android 6.0以来谷歌对用户隐私提供更加安全的机制。
6.0之前,系统会在用户安装应用时要求用户授予权限,也就是安装时授权,用户一旦安装应用,撤销权限的唯一方式是卸载应用。
6.0开始,应用在运行时向用户请求权限,也就是运行时授权,用户可随时调用权限,因此应用在每次运行时都要检查本身是否有所需的权限,并且用户可以随时进入应用的“设置”里开启或者关闭权限,用户拥有更多的控制权,更加安全。
正常权限和危险权限
系统权限分为几个保护级别。需要了解的两个最重要保护级别是正常权限和危险权限:
正常权限是对用户隐私或其他应用操作风险很小的区域。例如,设置时区的权限就是正常权限。如果应用声明其需要正常权限,系统会自动向应用授予该权限。如需当前正常权限的完整列表。
危险权限是涉及用户隐私信息的数据或资源,或者可能对用户存储的数据或其他应用的操作产生影响的区域。例如,能够读取用户的联系人属于危险权限。如果应用声明其需要危险权限,则用户必须明确向应用授予该权限。
权限组
所有危险的 Android 系统权限都属于权限组。如果设备运行的是 Android 6.0(API 级别 23),并且应用的 targetSdkVersion 是 23 或更高版本,则当用户请求危险权限时系统会发生以下行为:
如果应用请求其清单中列出的危险权限,而应用目前在权限组中没有任何权限,则系统会向用户显示一个对话框,描述应用要访问的权限组。对话框不描述该组内的具体权限。例如,如果应用请求 READ_CONTACTS 权限,系统对话框只说明该应用需要访问设备的联系信息。如果用户批准,系统将向应用授予其请求的权限。
如果应用请求其清单中列出的危险权限,而应用在同一权限组中已有另一项危险权限,则系统会立即授予该权限,而无需与用户进行任何交互。例如,如果某应用已经请求并且被授予了 READ_CONTACTS 权限,然后它又请求 WRITE_CONTACTS,系统将立即授予该权限。
任何权限都可属于一个权限组,包括正常权限和应用定义的权限。但权限组仅当权限危险时才影响用户体验。可以忽略正常权限的权限组。
如果设备运行的是 Android 5.1(API 级别 22)或更低版本,并且应用的 targetSdkVersion 是 22 或更低版本,则系统会在安装时要求用户授予权限。再次强调,系统只告诉用户应用需要的权限组,而不告知具体权限。
上面是简单的介绍一下,下面正式进入主题,本人使用的android studio 自带的模拟器,版本是6.0,
主要步骤分为两步:
1.在Manifest应用清单文件中声明权限
2.在运行时请求权限
1.在Manifest应用清单文件中声明权限
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.dingmouren.androiddemo">
<uses-permission android:name="android.permission.CALL_PHONE"/>
......
</application>
</manifest>
2.在运行时请求权限
打电话的代码
private void callPhone() {
Intent intent = new Intent(Intent.ACTION_CALL);
final Uri data = Uri.parse("tel:" + "10086");
intent.setData(data);
if (ContextCompat.checkSelfPermission(this, Manifest.permission.CALL_PHONE) == PackageManager.PERMISSION_GRANTED) {
startActivity(intent);
}
}
ActivityCompat.checkSelfPermission(this, Manifest.permission.CALL_PHONE)返回的值 表示权限的授权值,等于PackageManager.PERMISSION_GRANTED,表示权限已经得到授权,等于PackageManager.PERMISSION_DENIED,表示权限未授权。
shouldShowRequestPermissionRationale(Manifest.permission.CALL_PHONE)这个方法的返回值可以知道用户是否拒绝了权限的申请,当请求过权限但用户拒绝了请求,此方法返回true,如果设备规范禁止应用具有该权限或者在权限申请系统对话框中勾选了不再提醒,此方法会返回false.
@RequiresApi(api = Build.VERSION_CODES.M)
private void checkCallPhonePermission() {
Log.e(TAG, "检查权限:" + ContextCompat.checkSelfPermission(this, Manifest.permission.CALL_PHONE));
Log.e(TAG, "是否要给予解释:" + shouldShowRequestPermissionRationale(Manifest.permission.CALL_PHONE));
if (ActivityCompat.checkSelfPermission(this, Manifest.permission.CALL_PHONE) != PackageManager.PERMISSION_GRANTED) {//检查activity是否具有打电话的权限
//没有权限,第一次去申请权限
if (!ActivityCompat.shouldShowRequestPermissionRationale(this,Manifest.permission.CALL_PHONE))
ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.CALL_PHONE}, 101);
/*请求过权限但用户拒绝了请求,此方法返回true,如果设备规范禁止应用具有该权限或者在权限申请系统对话框中勾选了不再提醒,此方法会返回false,*/
if (ActivityCompat.shouldShowRequestPermissionRationale(this, Manifest.permission.CALL_PHONE)) {
Log.e(TAG, "请求过权限但用户拒绝了请求,要给用户一个需要这个权限的解释");
new AlertDialog.Builder(this)
.setMessage("打电话功能需要这个权限")
.setNegativeButton("取消", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
dialog.dismiss();
}
})
.setPositiveButton("去申请权限", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
ActivityCompat.requestPermissions(Demo1Activity.this, new String[]{Manifest.permission.CALL_PHONE}, 101);
}
}).show();
}
}else {
callPhone();
}
}
处理权限请求响应
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
switch (requestCode) {
case 101:
if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
Log.e(TAG, "授权成功,可以直接使用功能");
callPhone();
} else {
Log.e(TAG, "授权失败,不能使用此功能");
if (!ActivityCompat.shouldShowRequestPermissionRationale(this, Manifest.permission.CALL_PHONE)) {
new AlertDialog.Builder(this)
.setMessage("使用打电话功能的话,就需要这个权限哦,要使用此功能可以去设置里面哟")
.setNegativeButton("取消", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
dialog.dismiss();
}
})
.setPositiveButton("跳往设置界面", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
Intent intent = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS);
Uri uri = Uri.fromParts("package", Demo1Activity.this.getPackageName(), null);
intent.setData(uri);
Demo1Activity.this.startActivityForResult(intent, 102);
}
}).show();
}
}
return;
}
}
下面是全部代码
public class Demo1Activity extends AppCompatActivity {
private static final String TAG = Demo1Activity.class.getSimpleName();
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_demo1);
findViewById(R.id.btn_call).setOnClickListener(new View.OnClickListener() {
@RequiresApi(api = Build.VERSION_CODES.M)
@Override
public void onClick(View v) {
checkCallPhonePermission();
}
});
}
@RequiresApi(api = Build.VERSION_CODES.M)
private void checkCallPhonePermission() {
Log.e(TAG, "检查权限:" + ContextCompat.checkSelfPermission(this, Manifest.permission.CALL_PHONE));
Log.e(TAG, "是否要给予解释:" + shouldShowRequestPermissionRationale(Manifest.permission.CALL_PHONE));
if (ActivityCompat.checkSelfPermission(this, Manifest.permission.CALL_PHONE) != PackageManager.PERMISSION_GRANTED) {//检查activity是否具有打电话的权限
//没有权限,第一次去申请权限
if (!ActivityCompat.shouldShowRequestPermissionRationale(this, Manifest.permission.CALL_PHONE))
ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.CALL_PHONE}, 101);
/*请求过权限但用户拒绝了请求,此方法返回true,如果设备规范禁止应用具有该权限或者在权限申请系统对话框中勾选了不再提醒,此方法会返回false,*/
if (ActivityCompat.shouldShowRequestPermissionRationale(this, Manifest.permission.CALL_PHONE)) {
Log.e(TAG, "请求过权限但用户拒绝了请求,要给用户一个需要这个权限的解释");
new AlertDialog.Builder(this)
.setMessage("打电话功能需要这个权限")
.setNegativeButton("取消", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
dialog.dismiss();
}
})
.setPositiveButton("去申请权限", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
ActivityCompat.requestPermissions(Demo1Activity.this, new String[]{Manifest.permission.CALL_PHONE}, 101);
}
}).show();
}
} else {
callPhone();
}
}
private void callPhone() {
Intent intent = new Intent(Intent.ACTION_CALL);
final Uri data = Uri.parse("tel:" + "10086");
intent.setData(data);
if (ContextCompat.checkSelfPermission(this, Manifest.permission.CALL_PHONE) == PackageManager.PERMISSION_GRANTED) {
startActivity(intent);
}
}
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
switch (requestCode) {
case 101:
if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
Log.e(TAG, "授权成功,可以直接使用功能");
callPhone();
} else {
Log.e(TAG, "授权失败,不能使用此功能");
if (!ActivityCompat.shouldShowRequestPermissionRationale(this, Manifest.permission.CALL_PHONE)) {
new AlertDialog.Builder(this)
.setMessage("使用打电话功能的话,就需要这个权限哦,要使用此功能可以去设置里面哟")
.setNegativeButton("取消", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
dialog.dismiss();
}
})
.setPositiveButton("跳往设置界面", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
Intent intent = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS);
Uri uri = Uri.fromParts("package", Demo1Activity.this.getPackageName(), null);
intent.setData(uri);
Demo1Activity.this.startActivityForResult(intent, 102);
}
}).show();
}
}
return;
}
}
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
if (requestCode == 102) {
System.out.println("从设置界面回来");
}
}
}