Android6.0运行时权限处理架构搭建
介绍
对于6.0以下的权限及在安装的时候,根据权限声明产生一个权限列表,用户只有在同意之后才能完成app的安装,造成了我们想要使用某个app,就要默默忍受其一些不必要的权限(比如是个app都要访问通讯录、短信等)。而在6.0以后,我们可以直接安装,当app需要我们授予不恰当的权限的时候,我们可以予以拒绝(比如:单机的象棋对战,请求访问任何权限,我都是不同意的)。当然你也可以在设置界面对每个app的权限进行查看,以及对单个权限进行授权或者解除授权。
新的权限机制更好的保护了用户的隐私,Google将权限分为两类,一类是Normal Permissions,这类权限一般不涉及用户隐私,是不需要用户进行授权的,比如手机震动、访问网络等;另一类是Dangerous Permission,一般是涉及到用户隐私的,需要用户进行授权,比如读取sdcard、访问通讯录等。
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
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
我们发现危险的权限全是一组一组的,如果你申请某个危险的权限,假设你的app早已被用户授权了同一组的某个危险权限,那么系统会立即授权,而不需要用户去点击授权。此外,对于申请时弹出的dialog上面的文本说明也是对整个权限组的说明,而不是单个权限。
申请权限的方式
1.对于6.0版本以下和6.0以上的Normal Permissions,还和以前一样
<uses-permission android:name="android.permission.INTERNET"></uses-permission>
2.对于6.0以上的Dangerous Permission,就需要运行时申请权限
private void callPhone(){
//需要申请的权限
String[] usePermissions=new String[]{Manifest.permission.CALL_PHONE};
//检测是否已经授权码
int permission = ContextCompat.checkSelfPermission(this,
Manifest.permission.CALL_PHONE);
if(permission!= PackageManager.PERMISSION_GRANTED){
//没有授权,申请权限
ActivityCompat.requestPermissions(this,usePermissions,CALL_REQUEST_CODE);
}else {
//已经授权,拨打电话
Intent intent = new Intent(Intent.ACTION_CALL);
Uri data = Uri.parse("tel:" + "10086");
intent.setData(data);
startActivity(intent);
}
}
申请权限时,用户可能授权,也可能拒绝,那我们怎么知道用户的选择呢?Activity提供了申请权限后的回调onRequestPermissionsResult,我们来看下它的使用:
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
//判断请求码
if(requestCode==CALL_REQUEST_CODE){
//用户是否同意授权
if(grantResults[0] == PackageManager.PERMISSION_GRANTED){
callPhone();
}else {
Toast.makeText(MainActivity.this, "Call Phone Permission Denied", Toast.LENGTH_SHORT).show();
}
}
}
原理很简单吧!但是每个需要权限的地方都这样调用,是不是有点麻烦?接下来我们就自己手写一个处理运行时权限的框架。
封装框架
我们先来看下libray的结构:
我们先看下注解:
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface PermissionSuccess {
/**
* 请求码
* @return
*/
int requestCode() ;
}
基于运行时注解,下面我们来看下StonePermission:
public class StonePermission {
//参数
private Object mObject;//activity or fragment
private String[] mPermissions;
private int mRequestCode;//请求码
private StonePermission(Object object){
this.mObject=object;
}
/**
* 直接请求权限
* @param activity
* @param requestCode
* @param permissions
*/
public static void requestPermission(Activity activity,int requestCode,String... permissions){
realRequestPermission(activity,requestCode,permissions);
}
/**
* 直接请求权限
* @param fragment
* @param requestCode
* @param permissions
*/
public static void requestPermission(Fragment fragment, int requestCode, String... permissions){
realRequestPermission(fragment,requestCode,permissions);
}
/**
* 关联调用类
* @param activity
* @return
*/
public static StonePermission with(Activity activity){
return new StonePermission(activity);
}
public static StonePermission with(Fragment fragment){
return new StonePermission(fragment);
}
/**
* 添加请求码
* @param requestCode
* @return
*/
public StonePermission addRequestCode(int requestCode){
this.mRequestCode=requestCode;
return this;
}
/**
* 添加申请权限
* @param permissions
* @return
*/
public StonePermission permissions(String... permissions){
this.mPermissions=permissions;
return this;
}
/**
* 执行请求权限
*/
public void request(){
realRequestPermission(mObject,mRequestCode,mPermissions);
}
/**
* 正在处理请求权限的地方
* @param object
* @param requestCode
* @param permissions
*/
@TargetApi(Build.VERSION_CODES.M)
private static void realRequestPermission(Object object, int requestCode, String... permissions){
//1.如果小于6.0,直接调用成功方法
if(!PermissionUtil.isOverMarshmallow()){
PermissionUtil.executeSuccessMethod(object,requestCode);
return;
}
//2.0 检查没有申请的权限
List<String> deniedPermissions= PermissionUtil.checkAndObtainDeniedPermissions(object,permissions);
if(deniedPermissions.size()>0){
//3.0 申请权限
ActivityCompat.requestPermissions(PermissionUtil.getActivity(object),deniedPermissions.toArray(new String[deniedPermissions.size()]),requestCode);
}else {
PermissionUtil.executeSuccessMethod(object,requestCode);
}
}
/**
* 在 onRequestPermissionsResult 中调用,处理权限申请结果
* @param activity
* @param requestCode
* @param permissions
* @param grantResults
*/
public static void onRequestPermissionsResult(Activity activity,int requestCode,String[] permissions,int[] grantResults){
requestPermissionsResult(activity,requestCode,permissions,grantResults);
}
/**
* 在 onRequestPermissionsResult 中调用,处理权限申请结果
* @param fragment
* @param requestCode
* @param permissions
* @param grantResults
*/
public static void onRequestPermissionsResult(Fragment fragment, int requestCode, String[] permissions, int[] grantResults){
requestPermissionsResult(fragment,requestCode,permissions,grantResults);
}
/**
* 真正回调的地方
* @param object
* @param requestCode
* @param permissions
* @param grantResults
*/
private static void requestPermissionsResult(Object object,int requestCode,String[] permissions,int[] grantResults){
List<String> deniedPermissions=new ArrayList<>();
int length = grantResults.length;
for (int i = 0; i < length; i++) {
if(grantResults[i] != PackageManager.PERMISSION_GRANTED){
deniedPermissions.add(permissions[i]);
}
}
if(deniedPermissions.size() > 0){
PermissionUtil.executeFaildMethod(object,requestCode);
}else {
PermissionUtil.executeSuccessMethod(object,requestCode);
}
}
}
上面就是提供给使用者的方法,是不是很简单?
接下来让我们一起来看下具体的使用:
private void callPhone(){
//申请权限的方式:
//第一种方式
//StonePermission.requestPermission(this,CALL_REQUEST_CODE,Manifest.permission.CALL_PHONE);
//第二种方式
StonePermission.with(this)
.addRequestCode(CALL_REQUEST_CODE)
.permissions(Manifest.permission.CALL_PHONE)
.request();
}
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
//将申请权限处理交给StonePermission
StonePermission.onRequestPermissionsResult(this,requestCode,permissions,grantResults);
}
//成功授权后调用
@PermissionSuccess(requestCode = CALL_REQUEST_CODE)
public void success() {
Intent intent = new Intent(Intent.ACTION_CALL);
Uri data = Uri.parse("tel:" + "10086");
intent.setData(data);
startActivity(intent);
}
//申请权限失败后调用
@PermissionFail(requestCode = CALL_REQUEST_CODE)
private void faild(){
Toast.makeText(MainActivity.this,"申请权限失败",Toast.LENGTH_SHORT).show();
}
看到没,使用很方便呀!