在Android 6.0之前安装应用的时候会产生一个权限列表,用户只有在同意之后才能完成app的安装。而在6.0以后,我们可以直接安装,当app需要权限时会给予用户提示,用户可以选择同意和拒绝。那么在6.0及以上版本我们的危险权限都需要在运行的时候去申请,之前都是在清单文件中配置即可,现在就不行了需要加代码申请。
新的权限机制更好的保护了用户的隐私,Google将权限分为两类,一类是Normal Permissions,这类权限一般不涉及用户隐私,是不需要用户进行动态授权的,比如访问网络等;另一类是Dangerous Permission,一般是涉及到用户隐私的,需要用户进行授权,比如读取sdcard、打电话等等。
如下是一些普通权限和危险权限:
Normal Permission:
- INTERNET
- GET_PACKAGE_SIZE
- ACCESS_NETWORK_STATE
- ACCESS_NOTIFICATION_POLICY
- ACCESS_WIFI_STATE
- CHANGE_WIFI_STATE
- VIBRATE
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 -
group:android.permission-group.CAMERA
permission:android.permission.CAMERA -
group:android.permission-group.STORAGE
permission:android.permission.READ_EXTERNAL_STORAGE
permission:android.permission.WRITE_EXTERNAL_STORAGE
危险权限都是一组一组的,如果app运行在Android 6.x的机器上,对于授权机制是这样的:如果你申请某个危险的权限,假设你的app早已被用户授权了同一组的某个危险权限,那么系统会立即授权,而不需要用户去点击授权。比如你的app对READ_CONTACTS已经授权了,当你的app申请WRITE_CONTACTS时,系统会直接授权通过。此外,对于申请时弹出的dialog上面的文本说明也是对整个权限组的说明,而不是单个权限(ps:这个dialog是不能进行定制的)。
之前我们需要打电话的时候,只要在清单文件中声明了<uses-permission android:name="android.permission.CALL_PHONE" />
就可以直接调用intent,如下:
Intent intent = new Intent(Intent.ACTION_CALL);
Uri uri = Uri.parse("tel:10086");
intent.setData(uri);
startActivity(intent);
Android 6.0之后我们需要动态申请,首先通过ActivityCompat.checkSelfPermission()方法检查是否被授予权限,如果已经有权限就直接打电话,没有则通过ActivityCompat.requestPermissions()申请权限,在重写的onRequestPermissionsResult()方法里进行权限申请是否成功的判断,代码如下:
public class MainActivity extends AppCompatActivity {
private static final int CALL_PHONE_REQUEST_CODE = 0x0001;
private Button btn;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
btn = findViewById(R.id.btn);
btn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
requestCallPhone();
}
});
}
private void requestCallPhone() {
if (ActivityCompat.checkSelfPermission(this, Manifest.permission.CALL_PHONE) != PackageManager.PERMISSION_GRANTED) {
//没有权限则动态申请
ActivityCompat.requestPermissions(this,new String[]{"Manifest.permission.CALL_PHONE"},CALL_PHONE_REQUEST_CODE);
}else {
//已经有权限直接打电话
callPhone();
}
}
private void callPhone() {
Intent intent = new Intent(Intent.ACTION_CALL);
Uri uri = Uri.parse("tel:10086");
intent.setData(uri);
startActivity(intent);
}
//获取用户的反馈 是否授予了权限
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
if (requestCode == CALL_PHONE_REQUEST_CODE){
if(grantResults != null && grantResults[0] == PackageManager.PERMISSION_GRANTED){
// 用户授权了
callPhone();
}else {
//没有授权提示用户
Toast.makeText(this,getString(R.string.permission_denied),Toast.LENGTH_SHORT).show();
}
}
}
}
动态权限申请的流程比较简单,但是如果有很多权限,没有都有写一遍代码的话还是很麻烦的。网上已经有很多的权限处理框架,例如RxPermission,我们也可以写一套自己的权限处理框架,在需要申请权限的地方直接调用就可以了。这里我们采用反射 + 注解 的方式来实现。
1.1 创建一个PermissionHelper类,里面传三个参数。第一个参数反射的类this,第二个参数请求码用于监听反馈处理,第三个参数传请求权限的数组 。传递的方式我们可以在工具类里面写一个静态的方法一把塞过去,但是在这里我们可以使用链式调用这种方式我们也经常使用,比如Okhttp,Gilde,甚至是Android的AlertDialog都是使用的这个方式。
public class PermissionHelper {
// 传的参数
private Object mObject; // Object: Fragment or Activity
private int mRequestCode; //int 请求码
private String[] mRequestPermission; //需要请求的权限 string[]
public PermissionHelper(Object mObject) {
this.mObject = mObject;
}
public static void requestPermission(Activity activity,int requestCode,String[] permission){
PermissionHelper.with(activity).requestCode(requestCode).requestPermission(permission).request();
}
public static void requestPermission(Fragment fragment, int requestCode, String[] permission){
PermissionHelper.with(fragment).requestCode(requestCode).requestPermission(permission).request();
}
//添加请求权限
private PermissionHelper requestPermission(String[] permission) {
this.mRequestPermission = permission;
return this;
}
//添加请求码
private PermissionHelper requestCode(int requestCode) {
this.mRequestCode = requestCode;
return this;
}
//链式的方式传Activity
public static PermissionHelper with(Activity activity) {
return new PermissionHelper(activity);
}
//链式的方式传Fragment
public static PermissionHelper with(Fragment fragment) {
return new PermissionHelper(fragment);
}
}
1.2 在request()方法中处理6.0以下和6.0以上的权限请求
//真正判断和发起请求权限的方法
private void request() {
//判断当前的版本是不是6.0 及以上
if (!PermissionUtils.isOverMarshmallow()){
//如果不是6.0以上 那么直接执行方法 反射获取执行方法
// 执行什么方法并不确定 那么我们只能采用注解的方式给方法打一个标记,
// 然后通过反射去执行。 注解 + 反射 执行Activity里面的callPhone
PermissionUtils.executeSucceedMethod(mObject,mRequestCode);
return;
}
//如果是6.0以上 那么首先需要判断权限是否授予
//需要申请的权限中 获取没有授予过得权限
ArrayList<String> deniedPermissions = PermissionUtils.getDeniedPermissions(mObject, mRequestPermission);
if (deniedPermissions.size() == 0){
//所有权限都授予了
PermissionUtils.executeSucceedMethod(mObject,mRequestCode);
}else {
//没有授权则申请权限
ActivityCompat.requestPermissions(PermissionUtils.getActivity(mObject),deniedPermissions.toArray(new String[deniedPermissions.size()]),mRequestCode);
}
}
1.3 处理回调:如果用户同意或是拒绝那么会回调onRequestPermissionsResult(),我们肯定也需要对它做处理。
//处理申请权限的回调
public static void requestPermissionsResult(Object object,int requestCode,String[] permissions){
//获取没有授予的权限
ArrayList<String> deniedPermissions = PermissionUtils.getDeniedPermissions(object, permissions);
if (deniedPermissions.size() == 0){
//所有权限都授予了
PermissionUtils.executeSucceedMethod(object,requestCode);
}else {
//申请的权限中 有用户拒绝的
PermissionUtils.executeFaildMethod(object,requestCode);
}
}
使用如下:点击按钮拨打电话的时候直接调用PermissionHelper.requestPermission()方法获取权限,然后再复写的onRequestPermissionsResult()方法里处理请求结果,请求成功和失败分别回调加了@PermissionSucceed注解的callPhone()方法和@PermissionFaild注解的callPhoneFail()方法。
public class MainActivity extends AppCompatActivity {
private static final int CALL_PHONE_REQUEST_CODE = 0x0001;
private Button btn;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
btn = findViewById(R.id.btn);
btn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
requestCallPhone();
}
});
}
private void requestCallPhone() {
PermissionHelper.requestPermission(this,CALL_PHONE_REQUEST_CODE,new String[]{Manifest.permission.CALL_PHONE});
}
@PermissionSucceed(requestCode = CALL_PHONE_REQUEST_CODE)
private void callPhone() {
Intent intent = new Intent(Intent.ACTION_CALL);
Uri uri = Uri.parse("tel:10086");
intent.setData(uri);
startActivity(intent);
}
@PermissionFaild(requestCode = CALL_PHONE_REQUEST_CODE)
private void callPhoneFail() {
//授权失败提示用户
Toast.makeText(this,getString(R.string.permission_denied),Toast.LENGTH_SHORT).show();
}
//获取用户的反馈 是否授予了权限
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
PermissionHelper.requestPermissionsResult(this,requestCode,permissions);
}
}
详细代码可以访问github仓库
参考:
https://www.jianshu.com/p/8e37e9cf20a5
https://blog.csdn.net/z240336124/article/details/53130207
网友评论