Android专题

Android动态权限封装

2019-04-27  本文已影响23人  angeliur

在Android 6.0之前安装应用的时候会产生一个权限列表,用户只有在同意之后才能完成app的安装。而在6.0以后,我们可以直接安装,当app需要权限时会给予用户提示,用户可以选择同意和拒绝。那么在6.0及以上版本我们的危险权限都需要在运行的时候去申请,之前都是在清单文件中配置即可,现在就不行了需要加代码申请。

新的权限机制更好的保护了用户的隐私,Google将权限分为两类,一类是Normal Permissions,这类权限一般不涉及用户隐私,是不需要用户进行动态授权的,比如访问网络等;另一类是Dangerous Permission,一般是涉及到用户隐私的,需要用户进行授权,比如读取sdcard、打电话等等。
如下是一些普通权限和危险权限:

Normal Permission:

Dangerous Permissions:

危险权限都是一组一组的,如果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

上一篇 下一篇

猜你喜欢

热点阅读