Android6.0以后的权限管理发生了很大的改变,不是直接在manifest中添加,而是让用户使用到的时候去动态的申请,Google把权限分了类,当涉及到一些危险的权限的时候必须使用动态申请。
- 权限分类
- 重要方法
- 使用示例
- 权限申请封装
- 其他权限
如下是危险权限,使用的时候都是需要动态申请的
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: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
// 涉及SMS卡的操作
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
如下申请动态权限的时候用到的函数:
(1)ContextCompat.checkSelfPermission
(2)ActivityCompat.requestPermissions
(3)ActivityCompat.shouldShowRequestPermissionRationale
(4)onRequestPermissionsResult
检查是否具有某个权限,如果应用具有此权限,方法将返回PackageManager.PERMISSION_GRANTED,并且应用可以继续操作。如果应用不具有此权限,方法将返回PackageManager.PERMISSION_DENIED,且应用必须明确向用户要求权限。
应用可以通过这个方法动态申请权限,调用后会弹出一个对话框提示用户授权所申请的权限。
如果应用之前请求过此权限但用户拒绝了请求,此方法将返回 true。如果用户在过去拒绝了权限请求,并在权限请求系统对话框中选择了 Don’t ask again 选项,此方法将返回 false。如果设备规范禁止应用具有该权限,此方法也会返回 false。
当应用请求权限时,系统将向用户显示一个对话框。当用户响应时,系统将调用应用的 onRequestPermissionsResult() 方法,这个是执行requestPermissions后的回调。
1、ContextCompat.checkSelfPermission使用
private void checkPermission() {
//动态请求的权限数组
String[] permissions =new String[]{
Manifest.permission.READ_EXTERNAL_STORAGE,
Manifest.permission.CAMERA,
Manifest.permission.READ_SMS,
Manifest.permission.INSTALL_SHORTCUT,
Manifest.permission.MODIFY_AUDIO_SETTINGS};
//测试
Log.i("checkSelfPermission", "--------------------------------"+permissions.length);
for (String permission : permissions) {
int isGranted = ContextCompat.checkSelfPermission(this, permission);
if (isGranted == PackageManager.PERMISSION_GRANTED) {
//已授权
Log.i("checkSelfPermission", permission.toString()+ ": 已授权");
}else if(isGranted == PackageManager.PERMISSION_DENIED){
//未授权的
Log.i("checkSelfPermission", permission.toString()+ ": 未授权");
}
}
Log.i("checkSelfPermission", "--------------------------------"+permissions.length);
}
2、ActivityCompat.requestPermissions使用,这里是先检查是否获得了这两个权限,只要有一个没有获得就去申请权限
private void requestPermissions() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
if (ContextCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE) != PERMISSION_GRANTED || ContextCompat.checkSelfPermission(this, Manifest.permission.CAMERA) != PERMISSION_GRANTED) {
ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE, Manifest.permission.CAMERA}, 1);
}else {
Toast.makeText(this,"您已经申请了权限!",Toast.LENGTH_SHORT).show();
}
}
}
3、ActivityCompat.shouldShowRequestPermissionRationale使用,拒绝且不再提醒、系统默认禁用的权限、用户未点过拒绝的权限均返回false, 只有用户点击拒绝后,且没有勾选不再提醒返回true
private void rationablePermission() {
String[] permissions =new String[]{
Manifest.permission.READ_EXTERNAL_STORAGE,
Manifest.permission.CAMERA,
Manifest.permission.READ_SMS,
Manifest.permission.INSTALL_SHORTCUT,
Manifest.permission.MODIFY_AUDIO_SETTINGS};
//测试
Log.i("checkSelfPermission", "--------------------------------"+permissions.length);
for (String permission : permissions) {
//拒绝且不再提醒、系统默认禁用的权限、用户未点过拒绝的权限均返回false, 只有用户点击拒绝后,且没有勾选不再提醒返回true
if(!ActivityCompat.shouldShowRequestPermissionRationale(this, permission)){
Log.i("checkSelfPermission @@", permission.toString()+ ": false");
}else{
Log.i("checkSelfPermission @@", permission.toString()+ ": true");
}
}
Log.i("checkSelfPermission", "--------------------------------"+permissions.length);
}
4、onRequestPermissionsResult使用,当应用请求权限时候,系统将向用户显示一个对话框。当用户相应的时候,系统将调用onRequstPermissionsResult() 方法。向其传递用户的相应。我们需要在我们自己Activity中复写该方法。并且对用户操作的反馈做处理。
@Override
public void onRequestPermissionsResult(int requestCode,
String permissions[], int[] grantResults) {
switch (requestCode) {
case MY_PERMISSIONS_REQUEST_READ_CONTACTS: {
// If request is cancelled, the result arrays are empty.
if (grantResults.length > 0
&& grantResults[0] == PackageManager.PERMISSION_GRANTED) {
// permission was granted, yay! Do the
} else {
// permission denied, boo! Disable the
}
return;
}
// other 'case' lines to check for other
}
}
首先建立一个权限申请成功和失败的回调接口:
public interface Callback {
//pendingPermissions需要申请的权限列表。
void onPendingGrant(Activity activity, List<String> pendingPermissions);
void onGrantAll();
}
然后建立一个PermissinoHelper类来检查是否有权限,如果没有的话直接申请:
/**
* 检查权限(仅仅做检查操作,不会主动申请权限)
*
* @param activity 当前活动
* @param callback 回调:1.onGrantAll 所检查权限已全部授权 2. onPendingGrant 待授权的权限集合
* @param permissions 待检测的权限
*/
public static void checkPermissions(Activity activity, Callback callback, String... permissions) {
checkPermissions(activity, callback, Arrays.asList(permissions));
}
public static void checkPermissions(Activity activity, Callback callback, List<String> permissions) {
if (activity == null || permissions == null || permissions.size() == 0) {
Log.w(TAG, "Illegal param , Activity or Permissions is NULL");
return;
}
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) {
Log.w(TAG, "The SDK API < Marshmallow");
return;
}
List<String> targetPermissions = permissions;
//检测 8.0 权限
if (targetPermissions.contains(Permission.REQUEST_INSTALL_PACKAGES) ||
targetPermissions.contains(Permission.ANSWER_PHONE_CALLS) ||
targetPermissions.contains(Permission.READ_PHONE_NUMBERS)) {
if (activity.getApplicationInfo().targetSdkVersion < Build.VERSION_CODES.O) {
Log.d(TAG, "Contain Permission over API 26");
}
}
//检测 6.0 运行时权限
List<String> noGrantedPermissions = checkPermissionState(activity, targetPermissions);
if (noGrantedPermissions == null) {
if (callback != null) {
callback.onGrantAll();
}
} else {
if (callback != null) {
callback.onPendingGrant(activity, noGrantedPermissions);
}
}
}
/**
* 获取Manifest声明的权限的集合
*
* @return
*/
private static List<String> getManifestPermissions(Activity activity) {
List<String> permissions = null;
try {
permissions = Arrays.asList(activity.getPackageManager().getPackageInfo(
activity.getPackageName(), PackageManager.GET_PERMISSIONS).requestedPermissions);
} catch (Exception e) {
e.printStackTrace();
}
return permissions;
}
/**
* 检测所申请权限的状态,并返回未授权的权限集合
*
* @param activity 当前活动
* @param permissions 需要申请的目标权限的集合
* @return 未授权的权限集合
*/
private static List<String> checkPermissionState(Activity activity, List<String> permissions) {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) {
return null;
}
List<String> results = null;
for (String permission : permissions) {
int state = ActivityCompat.checkSelfPermission(activity, permission);
if (state == PackageManager.PERMISSION_DENIED) {
if (results == null) {
results = new ArrayList<>();
}
results.add(permission);
}
}
return results;
}
然后是一个申请权限的方法,他会首先判断manifest文件中是否声明了该权限
/**
* 申请普通的6.0权限(不处理)
*
* @param activity 当前活动
* @param pendingPermissions 待申请的权限集合
*/
public static void requestPermissions(Activity activity, List<String> pendingPermissions, int requestCode) {
if (pendingPermissions == null || pendingPermissions.size() == 0) {
return;
}
List<String> manifestPermissions = getManifestPermissions(activity);
if (manifestPermissions == null) {
Log.w(TAG, "No permissions declared");
return;
} else if (manifestPermissions != null && !manifestPermissions.containsAll(pendingPermissions)) {
Log.w(TAG, "The manifestPermissions didn't include all of pendingPermissions");
return;
} else {
if (pendingPermissions.contains(Permission.SYSTEM_ALERT_WINDOW)) {
pendingPermissions.remove(Permission.SYSTEM_ALERT_WINDOW);
Log.w(TAG, "Call checkAndRequestAlertWindowPermission to request SYSTEM_ALERT_WINDOW permission");
}
if (pendingPermissions.contains(Permission.WRITE_SETTINGS)) {
pendingPermissions.remove(Permission.WRITE_SETTINGS);
Log.w(TAG, "Call checkAndRequestWriteSettingPermission to request WRITE_SETTINGS permission");
}
String[] permissions = new String[pendingPermissions.size()];
ActivityCompat.requestPermissions(activity, pendingPermissions.toArray(permissions), requestCode);
}
}
如下是在Fragment或者Activity中的使用方式,用来申请所需要的权限:
//申请必须的权限。
private void checkAndRequestPermission() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
String[] permissions;
if (getViewModel().getState().callType == AUDIO_TYPE) {
permissions = new String[]{Manifest.permission.RECORD_AUDIO,
Manifest.permission.READ_EXTERNAL_STORAGE, Manifest.permission.WRITE_EXTERNAL_STORAGE,
Manifest.permission.READ_PHONE_STATE};
} else {
permissions = new String[]{Manifest.permission.CAMERA, Manifest.permission.RECORD_AUDIO,
Manifest.permission.READ_EXTERNAL_STORAGE, Manifest.permission.WRITE_EXTERNAL_STORAGE,
Manifest.permission.READ_PHONE_STATE};
}
PermissionHelper.checkPermissions(this, new PermissionHelper.Callback() {
@Override
public void onPendingGrant(Activity activity, List<String> pendingPermissions) {
PermissionHelper.requestPermissions(activity, pendingPermissions, Constant.PERMISSION_REQUEST_CODE);
}
@Override
public void onGrantAll() {
ToastUtils.showToast("All Permission has Granted");
}
}, permissions);
}
}
最后是在onRequestPermissionsResult处理权限的申请结果
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
if (requestCode == Constant.PERMISSION_REQUEST_CODE) {
ArrayList<String> result = new ArrayList<>();
for (int i = 0; i < grantResults.length; i++) {
if (grantResults[i] == PackageManager.PERMISSION_DENIED) {
result.add(permissions[i]);
} else if (TextUtils.equals(permissions[i], Manifest.permission.CAMERA) && initCamera) {
cameraEnable();
}
}
}
ToastUtils.showToast("您尚未开启 : " + result.toString() + "权限");
//也可以在这里引导用户去设置界面开启权限
}
以上这些内容全部都是来自Google的官方Demo。国内真正的Android开发环境,Rom客制化太多了,Google 原生API被改的很严重。
以小米为例:以看到小米的授权模块中,对权限操作可以分为允许,询问,拒绝。当我们第一次打开应用的时候,默认是询问状态,在该状态下,我们调用requestPermission()方法会弹出系统询问框.在弹出系统授权框后,只要你操作了(拒绝或者允许),你永远也不要想着在以后能看到授权框了,除非你过来设置这边更改为“询问”模式。不然无论你再调用几次requestPermissions(),都是直接走回调OnRequestPermissionResult。总结为一句话,小米客制的授权模块是凌驾于Google的授权模块之上的。
另外小米手机还有特殊的权限,如后台弹出权限和锁屏显示权限,如果没有打开后台弹出权限的话是没有办法从后台正常弹出的,还有锁屏也是。后台弹出权限不同手机的控制机制也是不一样的,小米是直接限制不让弹出,而vivo手机只让弹出后再给他finish掉,360手机还不一样,适配起来还是比较头大的。
再有就是通知权限,这个权限还是比较好控制的,可以直接让用户跳转到设置界面去打开。悬浮窗权限的适配也是比较麻烦的,不同手机也是不太相同。
尊重作者,尊重原创,参考文章:
Demo:https://blog.csdn.net/losingcarryjie/article/details/80889154
封装:https://www.jianshu.com/p/8e37e9cf20a5
https://www.jianshu.com/p/2fe4fb3e8ce0
https://blog.csdn.net/yuguqinglei/article/details/80375702