Android权限完全解析
Posted 劲火星空
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Android权限完全解析相关的知识,希望对你有一定的参考价值。
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
1. ContextCompat.checkSelfPermission
检查是否具有某个权限,如果应用具有此权限,方法将返回PackageManager.PERMISSION_GRANTED,并且应用可以继续操作。如果应用不具有此权限,方法将返回PackageManager.PERMISSION_DENIED,且应用必须明确向用户要求权限。
2. ActivityCompat.requestPermissions
应用可以通过这个方法动态申请权限,调用后会弹出一个对话框提示用户授权所申请的权限。
3. ActivityCompat.shouldShowRequestPermissionRationale
如果应用之前请求过此权限但用户拒绝了请求,此方法将返回 true。如果用户在过去拒绝了权限请求,并在权限请求系统对话框中选择了 Don’t ask again 选项,此方法将返回 false。如果设备规范禁止应用具有该权限,此方法也会返回 false。
4. onRequestPermissionsResult
当应用请求权限时,系统将向用户显示一个对话框。当用户响应时,系统将调用应用的 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
以上是关于Android权限完全解析的主要内容,如果未能解决你的问题,请参考以下文章