简介
Marshmallow (API 23) 引进了Runtime Permissions 将权限大致划分为敏感权限和普通权限: PHONE,SMS ,LOCATION ,CONTACTS 之类的隐私属于敏感权限,而NFC,INTERNET,BLUETOOTH 之类的属于普通权限。如果app需要获取敏感权限时需要在运行时通过代码请求权限,让用户手动同意。
- 只有设备 && TargetSdkVersion >= 23 才生效,否则还是是采用安装时获取权限的方式。
- Manifest 中还是需要定义权限
例子
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
getPermissionToReadUserContacts();
}
private static final int READ_CONTACTS_PERMISSIONS_REQUEST = 1;
private void getPermissionToReadUserContacts() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
if (PackageManager.PERMISSION_GRANTED !=
ContextCompat.checkSelfPermission(this, Manifest.permission.READ_CONTACTS)) {
if (shouldShowRequestPermissionRationale(Manifest.permission.READ_CONTACTS)) {
Toast.makeText(this, "This permission is necessary", Toast.LENGTH_SHORT).show();
}
requestPermissions(new String[]{Manifest.permission.READ_CONTACTS}, READ_CONTACTS_PERMISSIONS_REQUEST);
}
}
}
@TargetApi(Build.VERSION_CODES.M)
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
if (requestCode == READ_CONTACTS_PERMISSIONS_REQUEST) {
if (grantResults.length == 1 &&
grantResults[0] == PackageManager.PERMISSION_GRANTED) {
Toast.makeText(this, "Read Contacts permission granted", Toast.LENGTH_SHORT).show();
} else {
// showRationale = false if user clicks Never Ask Again, otherwise true
boolean showRationale = shouldShowRequestPermissionRationale(Manifest.permission.READ_CONTACTS);
if (showRationale) {
// do something here to handle degraded mode
} else {
Toast.makeText(this, "permission denied", Toast.LENGTH_SHORT).show();
}
}
}
}
}
关键方法
ContextCompat.checkSelfPermission | 检查当前是否已有权限 |
shouldShowRequestPermissionRationale | 是否该提示用户需求权限的原因 |
requestPermissions | 请求权限 |
onRequestPermissionsResult | 请求权限call back |
shouldShowRequestPermissionRationale 是说用户点了拒绝赋予权限,但也没点‘不再提示’,这时候如果实在需要权限就可以给予用户适当的提示告诉原因。
权限的分组
权限是分组的,如果请求的是组内的一个权限,Android会自动询问用户请求整个组的权限来避免大量请求权限。
可以通过以下命令查看所有敏感权限的分组情况:
$adb shell pm list permissions -d -g
比如:请求READ_CONTACTS 权限,提示的给用户的是要获取CONTACTS组权限,用户同意后WRITE_CONTACTS 也被赋予了。
第三方权限管理库
PermissionsDispatcher
注解方式管理权限,清晰方便
@RuntimePermissions
public class MainActivity extends AppCompatActivity {
@NeedsPermission(Manifest.permission.CAMERA)
void showCamera() {
getSupportFragmentManager().beginTransaction()
.replace(R.id.sample_content_fragment, CameraPreviewFragment.newInstance())
.addToBackStack("camera")
.commitAllowingStateLoss();
}
@OnShowRationale(Manifest.permission.CAMERA)
void showRationaleForCamera(PermissionRequest request) {
new AlertDialog.Builder(this)
.setMessage(R.string.permission_camera_rationale)
.setPositiveButton(R.string.button_allow, (dialog, button) -> request.proceed())
.setNegativeButton(R.string.button_deny, (dialog, button) -> request.cancel())
.show();
}
@OnPermissionDenied(Manifest.permission.CAMERA)
void showDeniedForCamera() {
Toast.makeText(this, R.string.permission_camera_denied, Toast.LENGTH_SHORT).show();
}
@OnNeverAskAgain(Manifest.permission.CAMERA)
void showNeverAskForCamera() {
Toast.makeText(this, R.string.permission_camera_neverask, Toast.LENGTH_SHORT).show();
}
}
RxPermissions
rxjava 体系的权限管理,提供链式操作,最大好处不用再分离权限的请求与结果处理了。
//RxPermission
RxPermissions.getInstance(this)
.request(Manifest.permission.READ_CONTACTS)
.subscribe(new Action1<Boolean>() {
@Override
public void call(Boolean grant) {
if (grant) {
Toast.makeText(MainActivity.this, "this permission is necessary", Toast.LENGTH_SHORT).show();
} else {
Toast.makeText(MainActivity.this, "Read Contacts permission granted", Toast.LENGTH_SHORT).show();
}
}
});