以下是需要单独申请的权限,共分为9组,每组只要有一个权限申请成功了,就默认整组权限都可以使用了。
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: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
其他权限是普通权限,在Manifiest.xml中申请即可
危险权限则需要在代码中动态申请
运行时权限处理
Android6.0系统默认为targetSdkVersion小于23的应用默认授予了所申请的所有权限,所以如果你以前的APP设置的targetSdkVersion低于23,在运行时也不会崩溃,但这也只是一个临时的救急策略,用户还是可以在设置中取消授予的权限,当取消后权限对应的功能依旧无法使用(可能也会崩溃)。
为什么要动态申请权限
如果不动态申请权限,手机也会弹出权限申请窗口,但是一旦用户拒绝之后,程序会崩溃
故需要动态申请权限
如何处理安卓6.0权限
权限状态
有的手机权限分为三种状态(有的手机只有允许和禁止,如小米手机)
- 允许
当处于这个状态时,表示权限已经拿到 - 询问
可弹出系统的权限弹窗 - 禁止
此时无法弹出系统的权限弹窗,只能手动去设置界面修改
对于只有允许和禁止状态的手机,第一次请求权限会弹出权限弹窗,但是一旦拒绝之后就不会弹出了
关键方法解析
ContextCompat.checkSelfPermission(Context context,String permission)
检查传入的permission,有木有拿到
boolean shouldShowRequestPermissionRationale
该方法在ActivityCompat和FragmentCompat中都有
如果用户是第一次去请求此权限时,此方法返回false(所以应该先申请权限,在调用此方法)
如果应用之前请求过此权限但用户拒绝了请求,此方法将返回 true
如果用户在过去拒绝了权限请求,并在权限请求系统对话框中选择了 Don't ask again 选项,此方法将返回 false
如果设备规范禁止应用具有该权限,此方法也会返回 false(如小米手机)
一般情况下,我们会在该方法返回true时,展示一个UI,来告诉用户我们为什么需要此权限
requestPermissions
调用该方法,则会去申请权限
如果是权限是询问状态,则会弹出一个系统的权限弹窗
如果是权限是禁止状态,则直接返回权限被拒绝
onRequestPermissionsResult
申请权限时,权限的结果会从这里获取到
如果权限时禁止状态,则结果为拒绝
如果是询问状态,则结果取决于用户时允许还是拒绝
Activity的权限处理
点击按钮时
if (ContextCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) {
//申请权限
ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}, 100);
}else {
//权限是允许的,执行自己的逻辑
}
写一个回调
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
switch (requestCode) {
case 100:
if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
//用户同意授权,执行自己的逻辑
} else {
//用户拒绝授权,在这里区分一下两种拒绝的情况(shouldShowRequestPermissionRationale)
}
break;
}
}
Fragment的权限处理
参考网上的说法,没测试
在Fragment中申请权限,不要使用ActivityCompat.requestPermissions, 直接使用Fragment的requestPermissions方法,否则会回调到Activity的onRequestPermissionsResult
如果在Fragment中嵌套Fragment,在子Fragment中使用requestPermissions方法,onRequestPermissionsResult不会回调回来,建议使用getParentFragment().requestPermissions方法,这个方法会回调到父Fragment中的onRequestPermissionsResult,加入以下代码可以把回调透传到子Fragment
public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
List<Fragment> fragments = getChildFragmentManager().getFragments();
if (fragments != null) {
for (Fragment fragment : fragments) {
if (fragment != null) {
fragment.onRequestPermissionsResult(requestCode,permissions,grantResults);
}
}
}
}
关于权限的封装
推荐使用RxPermissions
https://github.com/tbruyelle/RxPermissions
/**
* 权限工具类
*/
public class PermissionUtil {
/**
* 申请单个权限
*/
public static Observable<Permission> requestPermission(FragmentActivity activity,
String permissionName) {
return requestPermission(activity, permissionName, "", "");
}
/**
* 申请单个权限
* refuseHint 权限被拒绝后的提示
* settingHint 权限被拒绝,并勾选不再询问的提示
*/
public static Observable<Permission> requestPermission(FragmentActivity activity,
String permissionName, String refuseHint, String settingHint) {
if (activity == null || TextUtils.isEmpty(permissionName)) {
return Observable.just(new Permission("", false));
}
return new RxPermissions(activity).requestEach(permissionName).doOnNext(permission -> {
if (permission.granted) {
// 权限允许
} else if (permission.shouldShowRequestPermissionRationale) {
// 权限拒绝,但未勾选不再提示
showRefuseHint(activity, refuseHint);
} else {
// 权限拒绝,并勾选不再提示,需要跳转至设置界面
showSettingDialog(activity, settingHint);
}
});
}
/**
* 申请多个权限,有一个拒绝则返回false
*/
public static Observable<Boolean> requestPermissions(FragmentActivity activity,
String[] permissions) {
return requestPermissions(activity, permissions, "", "");
}
/**
* 申请多个权限,有一个拒绝则返回false
*/
public static Observable<Boolean> requestPermissions(FragmentActivity activity,
String[] permissions, String refuseHint, String settingHint) {
if (activity == null || permissions.length == 0) {
return Observable.just(Boolean.FALSE);
}
return new RxPermissions(activity).request(permissions).doOnNext(aBoolean -> {
if (!aBoolean) {
// 权限被拒
List<String> denyPermissions = new ArrayList<>();
// 筛选出被拒绝的权限
for (int i = 0; i < permissions.length; i++) {
if (!hasPermission(activity, permissions[i])) {
denyPermissions.add(permissions[i]);
}
}
boolean shouldShowRequestPermissionRationale = false;
for (String permision : denyPermissions) {
// 有一个未勾选不再提示,则置为true
if (shouldShowRationale(activity, permision)) {
shouldShowRequestPermissionRationale = true;
}
}
if (shouldShowRequestPermissionRationale) {
showRefuseHint(activity, refuseHint);
} else {
showSettingDialog(activity, settingHint);
}
}
});
}
/**
* 拒绝权限,但未勾选不再提醒的提示
*/
private static void showRefuseHint(FragmentActivity activity, String hint) {
if (TextUtils.isEmpty(hint)) {
return;
}
ToastUtil.startShort(activity, hint);
}
/**
* 拒绝权限,并勾选不再提醒的弹窗
*/
private static void showSettingDialog(FragmentActivity activity, String hint) {
if (TextUtils.isEmpty(hint)) {
return;
}
AlertDialog.Builder builder = new AlertDialog.Builder(activity)
.setMessage(hint)
.setPositiveButton("去设置", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
startAppSettingActivity(activity);
}
})
.setNegativeButton("取消", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
dialog.dismiss();
}
});
builder.show();
}
/**
* 跳转至设置界面
* 此方法并不能适配所有手机
*/
public static void startAppSettingActivity(FragmentActivity activity) {
try {
Intent intent = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS);
intent.setData(Uri.fromParts("package", activity.getPackageName(), null));
activity.startActivity(intent);
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 判断是否用某个权限
*/
public static boolean hasPermission(Context context, String permission) {
if (context == null || TextUtils.isEmpty(permission)) {
return false;
}
return Build.VERSION.SDK_INT < Build.VERSION_CODES.M
|| ContextCompat.checkSelfPermission(context,
permission) == PackageManager.PERMISSION_GRANTED;
}
/**
* 返回true,表示权限被拒绝,但是未勾选不再提示
*/
public static boolean shouldShowRationale(FragmentActivity activity, String permission) {
if (activity == null || TextUtils.isEmpty(permission)) {
return false;
}
return ActivityCompat.shouldShowRequestPermissionRationale(activity, permission);
}
}