运行时权限
简介
Android从6.0(API23)开始采用运行时权限机制,以往的版本是在安装应用的时候赋予权限,而6.0以后的权限是在应用运行时,需要此权限的时候去申请这个权限。
在6.0之前的版本调用像拨打电话、插入联系人等功能的时候,如果没有权限系统默认不做任何处理,而在6.0之后,如果调用了没有权限的功能,应用会异常退出。
用到的api
权限检查:
// Context类提供抽象方法 方法在ContextWrapper类中实现
public abstract int checkSelfPermission(@NonNull String permission);
// Fragment中存在同名方法
是否被拒绝过检查:
// Activity和Fragment都有此方法,都是调用了PackageManager的同名方法
public boolean shouldShowRequestPermissionRationale(@NonNull String permission)
权限请求:
// v4兼容包中ActivityCompat提供静态方法
public static void requestPermissions(final @NonNull Activity activity,
final @NonNull String[] permissions, final int requestCode)
// Activity类提供的方法
public final void requestPermissions(@NonNull String[] permissions, int requestCode)
// Fragment中存在同名方法
权限请求结果返回:
// Activity和Fragment都存在相同方法
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions,
@NonNull int[] grantResults)
以上方法在Activity和Fragment中都存在同名和相同方法签名的方法。下面以Activity为例,Fragment只需要修改为继承自Fragment即可使用
单个权限请求
单个权限请求较为简单,比较适合初学者来理顺思路逻辑。
- 检查是否拥有权限,如果没有此权限进入第二步,如果拥有权限直接进行业务处理
- 检查权限是否被拒绝过,如果没有被拒绝过,进入第三步,如果被拒绝过,做出处理,给用户一个提示
- 申请权限,权限申请会有两种结果,用户通过或者用户拒绝
/**
* Created by ztt on 2016/11/18.
*
* 权限检查回调接口,包含三个方法:
* 分别对应权限检查的成功,失败以及权限被拒绝之后再次点击的回调
*/
public interface IRunTimePermission {
/**
* 权限请求通过
* @param requestCode 权限请求码
*/
void onPermissionsGranted(int requestCode);
/**
* 权限请求拒绝
* @param requestCode 权限请求码
*/
void onPermissionsDenied(int requestCode);
/**
* 权限请求被拒绝过,显示请求权限的的原因
* @param requestCode 权限请求码
*/
void onShowRequestRationale(int requestCode);
}
/**
* Created by ztt on 2016/11/18.
*
* 带权限申请的Activity基类
*/
public abstract class BasePermissionActivityI extends Acticity implements
IRunTimePermission {
/**
* 检查权限
* @param requestCode 没有权限要去申请,权限请求码
* @param permission 需要检查的权限
* @return 是否有权限
*/
protected boolean checkPermission(int requestCode, String permission) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
if (checkSelfPermission(permission) == PackageManager.PERMISSION_GRANTED) {
return true;
} else {
// 此处检查权限是否被拒绝过,拒绝过的不能再次申请,可以给用户提示信息
if (shouldShowRequestPermissionRationale(permission)) {
onShowRequestRationale(requestCode);
} else {
requestPermissions(new String[]{permission}, requestCode);
}
return false;
}
} else {
return true;
}
}
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions,
@NonNull int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
if (grantResults[0] == PackageManager.PERMISSION_GRANTED) {
onPermissionsGranted(requestCode);
} else {
onPermissionsDenied(requestCode);
}
}
}
多个权限检查请求
官方都拥有同时请求多个权限的方法,这个功能也要实现哈。
当同时有ABCD四个权限需要检查申请,A权限已经被授权,B权限已经被拒绝过一次,C权限在本次申请中被拒绝,D权限在本次申请中通过。而我们的业务需要同时拥有此四个权限才能实现。此时如果要对每一个权限进行详细的处理,逻辑会变得复杂,饭要一口一口吃,权限要一个一个申请。
(申请多个权限,当一个权限被拒绝过之后将不再继续申请其他权限简化逻辑,当申请权限时,有一个权限被拒绝,将进入权限拒绝回调,简化逻辑)
先修改接口,当权限通过,就直接业务逻辑就好了。如果权限异常,可能需要对异常的权限进行处理,所以添加权限数组参数
为了更好的复用,将接口拿到Activity类里面,然后让项目原有的BaseActivity继承此类,为项目所有的Activity添加此功能。
修改权限检查申请和权限申请结果处理逻辑
/**
* Created by ztt on 2016/11/18.
* <p>
* 带权限申请的Activity基类
*/
public abstract class BasePermissionActivity extends FragmentActivity {
private OnRunTimePermissionListener onRunTimePermissionListener;
public void setOnRunTimePermissionListener(OnRunTimePermissionListener
onRunTimePermissionListener) {
this.onRunTimePermissionListener = onRunTimePermissionListener;
}
/**
* 检查权限
*
* @param requestCode 没有权限要去申请,权限请求码
* @param permissions 需要检查的权限
* @return 是否有所有权限
*/
protected boolean checkPermission(int requestCode, String... permissions) {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) {
return true;
}
// 存放被拒绝过的权限
List<String> rationalePermissions = new ArrayList<>();
// 存放未申请的权限
List<String> noPermissions = new ArrayList<>();
for (String permission : permissions) {
if (checkSelfPermission(permission) != PackageManager.PERMISSION_GRANTED) {
if (shouldShowRequestPermissionRationale(permission)) {
rationalePermissions.add(permission);
} else {
noPermissions.add(permission);
}
}
}
if (noPermissions.isEmpty() && rationalePermissions.isEmpty()) {
return true;
} else {
// 当未申请的权限不为空并且被拒绝权限为空时(所有未通过权限都处于询问状态)申请权限
if (!noPermissions.isEmpty() && rationalePermissions.isEmpty()) {
requestPermissions(noPermissions.toArray(new String[0]), requestCode);
} else {
// 存在被拒绝权限
if (onRunTimePermissionListener != null) {
onRunTimePermissionListener.onShowRequestRationale(requestCode,
rationalePermissions.toArray(new String[0]));
}
}
return false;
}
}
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions,
@NonNull int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
// 被拒绝权限
List<String> deniedPermissions = new ArrayList<>();
for (int i = 0; i < permissions.length; i++) {
if (grantResults[i] != PackageManager.PERMISSION_GRANTED) {
deniedPermissions.add(permissions[i]);
}
}
// 如果被拒绝权限为空,权限全部获取
if (deniedPermissions.isEmpty()) {
if (onRunTimePermissionListener != null) {
onRunTimePermissionListener.onPermissionsGranted(requestCode);
}
} else {
// 如果存在被拒绝权限
if (onRunTimePermissionListener != null) {
onRunTimePermissionListener.onPermissionsDenied(requestCode, deniedPermissions
.toArray(new String[0]));
}
}
}
/**
* 权限检查回调接口
* 包含三个方法,分别对应权限检查的成功,失败以及权限被拒绝之后再次点击
*/
public interface OnRunTimePermissionListener {
/**
* 权限请求通过
*
* @param requestCode 权限请求码
*/
void onPermissionsGranted(int requestCode);
/**
* 权限请求拒绝
*
* @param requestCode 权限请求码
* @param permissions 被拒绝的权限
*/
void onPermissionsDenied(int requestCode, String[] permissions);
/**
* 权限请求被拒绝过,显示请求权限的的原因
*
* @param requestCode 权限请求码
* @param permissions 被拒绝过的权限
*/
void onShowRequestRationale(int requestCode, String[] permissions);
}
}