Android运行时权限

来源:互联网 发布:贝多芬梅毒 知乎 编辑:程序博客网 时间:2024/05/19 23:05

Android运行时权限

简介:Android 6.0 以后,对于一些用户隐私权限总会在第一次提示用户是否授予权限。

优点:

  • 更好的保护了用户的隐私。
  • 给了程序向用户说明权限的作用。
  • 可以防止一些恶意程序盗取用户后者手机信息,增强了Android系统的安全性。

Android 6.0 将权限分为两类:

  • 普通权限:不需要单独申请,直接在Manifest里注册即可。
  • 危险权限&危险权限组:危险权限都是分组出现的,用户只要授权组内的一个权限,组内的其他权限就会立即授权。

危险权限列表:


申请方式一: 原生API

新增API:

  • ContextCompat.checkSelfPermission:检查危险权限&危险权限组是否授权。
  • ActivityCompat.requestPermissions:申请权限。
  • onRequestPermissionsResult:申请权限的回调(用户授权或拒绝授权)。
  • ActivityCompat.shouldShowRequestPermissionRationale:给用户解释,申请权限用户干什么。用户拒绝过权限后出现。

使用步骤:

  1. 在Manifest中添加需要的权限(不可省,因为要适配android 6.0 以下机型);
  2. 检查危险权限;
  3. 申请授权;
  4. 处理申请授权回调。

1.1 简单使用

示例:需要申请一个权限的功能(摄像头),需要申请多个(示例为2个:摄像头和手机状态)权限的功能。

public class PermissionActivity extends AppCompatActivity {    private Context mContext = this;    private final int PERMISSION_CAMERA = 100;//摄像头权限    private final int PERMISSION_PHONE = 101;//摄像头和手机状态权限    @Override    protected void onCreate(@Nullable Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_permission);        ButterKnife.bind(this);    }    @OnClick({R.id.per_btn_cc_cp, R.id.per_btn_cc_camera})    public void onViewClicked(View view) {        switch (view.getId()) {            case R.id.per_btn_cc_camera:                openCCCamera();//原生运行时权限--摄像头                break;            case R.id.per_btn_cc_cp:                openCCCP();//原生运行时权限--摄像头和手机状态                break;            }    }    /** 原生申请权限--摄像头 */    private void openCCCamera() {        //2.检查权限        if (ContextCompat.checkSelfPermission(this, Manifest.permission.CAMERA)        != PackageManager.PERMISSION_GRANTED) {            //进入到这里代表没有权限            if (ActivityCompat.shouldShowRequestPermissionRationale(this,   Manifest.permission.CAMERA)) {                //如果用户拒绝过该权限                new AlertDialog.Builder(this).setTitle("权限提示")                    .setMessage("之前拒绝授权,使用摄像头需要该权限")                    .setPositiveButton("知道了", new   DialogInterface.OnClickListener() {                        @Override                        public void onClick(DialogInterface dialog, int which) {                            ActivityCompat.requestPermissions((Activity) mContext, new String[]{Manifest.permission.CAMERA}, PERMISSION_CAMERA);                        }                    }).show();            } else {                //3. 申请权限                ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.CAMERA}, PERMISSION_CAMERA);            }        } else {            Toast.makeText(this, "之前已经授权,现在可以使用摄像头了", Toast.LENGTH_SHORT).show();        }    }    /** 原生申请权限--摄像头 和 手机状态 */    private void openCCCP() {        //2.检查权限        if (ContextCompat.checkSelfPermission(this, Manifest.permission.READ_PHONE_STATE)        != PackageManager.PERMISSION_GRANTED ||            ContextCompat.checkSelfPermission(this, Manifest.permission.CAMERA)            != PackageManager.PERMISSION_GRANTED) {            //3. 申请权限            ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.CAMERA, Manifest.permission.READ_PHONE_STATE}, PERMISSION_PHONE);        } else {            Toast.makeText(this, "之前已经授权,现在可以使用摄像头、读取手机状态了", Toast.LENGTH_SHORT).show();        }    }    @Override    public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {    switch (requestCode) {        case PERMISSION_CAMERA://使用摄像头功能申请处理            if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {//用户授予权限,执行业务逻辑                Toast.makeText(this, "用户同意授权,现在可以使用摄像头了", Toast.LENGTH_SHORT).show();            } else {//用户拒绝权限,提示用户                Toast.makeText(this, "用户没有授权--摄像头!!!", Toast.LENGTH_SHORT).show();            }            break;        case PERMISSION_PHONE://摄像头和读取手机状态申请处理            if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED &&            grantResults[1] == PackageManager.PERMISSION_GRANTED) {//用户授予权限,执行业务逻辑                Toast.makeText(this, "用户同意授权,现在可以使用摄像头和读取手机状态了", Toast.LENGTH_SHORT).show();            } else {//用户拒绝权限,提示用户                Toast.makeText(this, "用户没有授权--摄像头和读取手机状态!!!", Toast.LENGTH_SHORT).show();            }            break;        }    }}

效果:

1.2 封装

1.2.1 封装常量–权限请求code

public class Const {    public static class PERMISSION {        public static  final int CAMERA = 0x01;        public static  final int PHONE = 0x02;    }}

1.2.2 封装父类–BasePermissionActivity

public class BasePermissionActivity extends AppCompatActivity {    /**     * 检查是否拥有权限     * @param permissions     * @return     */    protected boolean hasPermission(String...permissions){        for(String permission : permissions){            if(ContextCompat.checkSelfPermission(this,permission) != PackageManager.PERMISSION_GRANTED){            return false;            }        }        return true;    }    /**     * 申请权限     * @param requestCode     * @param permissions     */    protected void requestPermission(int requestCode,String...permissions){        ActivityCompat.requestPermissions(this,permissions,requestCode);    }    /**     * 检查授权回调结果     * @param grantReslts     * @return     */    protected boolean checkGrantReslts(int[] grantReslts){        for(int grantReslt:grantReslts){            if( grantReslt != PackageManager.PERMISSION_GRANTED){            return false;            }        }        return true;    }    @Override    public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantReslts) {        switch (requestCode) {            case Const.PERMISSION.CAMERA://使用定义好的常量                if (grantReslts.length > 0 && checkGrantReslts(grantReslts)) {                    openCamera();                } else {                    Toast.makeText(this, "用户拒绝授权", Toast.LENGTH_SHORT).show();                }                break;            case Const.PERMISSION.PHONE://使用定义好的常量                if (grantReslts.length > 0 && checkGrantReslts(grantReslts)) {                    openCameraAndPhone();                } else {                    Toast.makeText(this, "用户拒绝授权", Toast.LENGTH_SHORT).show();                }                break;        }    }    /** 父类默认方法--打开相机和读取手机状态 */    protected void openCameraAndPhone() {}    /** 父类默认方法--打开相机 */    protected void openCamera() {}}

1.2.3 子类使用

public class PermissionTestActivity extends BasePermissionActivity {    private Context mContext = this;    @Override    protected void onCreate(@Nullable Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_permission);        ButterKnife.bind(this);    }    @OnClick({R.id.basePer_btn_camera, R.id.basePer_btn_phone})    public void onViewClicked(View view) {        switch (view.getId()) {            case R.id.basePer_btn_camera:                if (hasPermission(Manifest.permission.CAMERA)) {                    openCamera();                } else {                    requestPermission(Const.PERMISSION.CAMERA, Manifest.permission.CAMERA);                }                break;            case R.id.basePer_btn_phone:                if (hasPermission(Manifest.permission.CAMERA, Manifest.permission.READ_PHONE_STATE)) {                    openCameraAndPhone();                } else {                    requestPermission(Const.PERMISSION.CAMERA, Manifest.permission.CAMERA, Manifest.permission.READ_PHONE_STATE);                }                break;        }    }    // 重写父类方法--打开相机    @Override    protected void openCamera() {        Toast.makeText(mContext, "拥有授权,现在可以打开相机了", Toast.LENGTH_SHORT).show();    }    // 重写父类方法--打开相机和读取手机状态    @Override    protected void openCameraAndPhone() {        Toast.makeText(mContext, "拥有授权,现在可以打开相机和读取手机状态了", Toast.LENGTH_SHORT).show();    }}

申请方式二: EasyPermission

简介:EasyPermission是googlesamples提供的用以简化运行时权限逻辑的库。

GitHub地址,点击这里跳转

2.1 主要API

2.1.1 @AfterPermissionGranted()

@AfterPermissionGranted(int requestCode)

用途:可选项。如果请求码给定的所有请求权限被授予,将执行该方法。

2.1.2 hasPermissions()

hasPermissions(Context context, @NonNull String... perms):

用途:检查权限。

2.1.3 requestPermissions()

requestPermissions(@NonNull Activity host, @NonNull String rationale,@StringRes int positiveButton, @StringRes int negativeButton,int requestCode, @NonNull String... perms) {

用途:申请权限。

参数说明:

  • rationale:如果用户第一次拒绝请求,将显示为什么应用程序需要这组权限的消息。
  • positiveButton:确定按钮文本。
  • negativeButton:取消按钮文本。

2.1.4 PermissionCallbacks()

public interface PermissionCallbacks extends    ActivityCompat.OnRequestPermissionsResultCallback {    //Some permissions have been granted    void onPermissionsGranted(int requestCode, List<String> perms);    //Some permissions have been denied    void onPermissionsDenied(int requestCode, List<String> perms);}

用途:权限申请回调。

2.1.5 somePermissionPermanentlyDenied()

somePermissionPermanentlyDenied(@NonNull Activity host,@NonNull List<String> deniedPermissions)

用途:如果用户选择“不再询问”选项禁止这些权限,将无法从用户请求这些权限,之后必须在应用程序设置中更改这些权限。使用这个方法显示一个对话框,用户点击“确定按钮后”直接跳转至“系统的应用程序设置”界面。

2.2 简单使用

2.2.1 导包

compile 'pub.devrel:easypermissions:0.4.3'

2.2.2 示例

使用步骤:

  1. 在Activity/Fragment中重写onRequestPermissionsResult()方法;
  2. 检查权限;
  3. 申请权限;
  4. 实现接口,处理回调;

示例代码:

public class Permission3Activity extends AppCompatActivity implements   EasyPermissions.PermissionCallbacks {    private Context mContext = this;    private final int PERMISSION_PHONE = 101;//摄像头和手机状态权限    @Override    protected void onCreate(@Nullable Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_permission2);        ButterKnife.bind(this);    }    @OnClick({R.id.basePer_btn_phone})    public void onViewClicked(View view) {        switch (view.getId()) {            case R.id.basePer_btn_phone:                openCameraAndPhone();                break;        }    }    @AfterPermissionGranted(PERMISSION_PHONE)//请求权限回调结果中,如果所有权限全部授予,直接执行该方法    private void recordVideo(){        Toast.makeText(mContext, "开始录制视频", Toast.LENGTH_SHORT).show();    }    //点击录制视频按钮执行方法    private void openCameraAndPhone() {        String[] perms = {Manifest.permission.CAMERA, Manifest.permission.READ_PHONE_STATE};        //2.检查权限        if (EasyPermissions.hasPermissions(mContext, perms)) {            // 所有权限全部授权,进入这里            recordVideo();        } else {            //3.有未授权的权限,请求权限            EasyPermissions.requestPermissions((Activity) mContext, "录制视频需要摄像头权限和读取手机信息权限", PERMISSION_PHONE, perms);        }    }    //1.重写权限申请回调    @Override    public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {        super.onRequestPermissionsResult(requestCode, permissions, grantResults);        EasyPermissions.onRequestPermissionsResult(requestCode, permissions, grantResults, this);    }    /**     * 权限申请回调--某些同意     */    @Override    public void onPermissionsGranted(int requestCode, List<String> perms) {        //Some permissions have been granted    }    /**     * 权限申请回调--某些拒绝     */    @Override    public void onPermissionsDenied(int requestCode, List<String> perms) {        //Some permissions have been denied        if (EasyPermissions.somePermissionPermanentlyDenied(this, perms)) {//不再询问--是否跳转至app设置界面            new AppSettingsDialog.Builder(this)            .setRationale("缺少请求的权限倒是该功能无法使用,打开app设置界面开启相关权限").build().show();        } else {            Toast.makeText(mContext, "用户拒绝某些授权", Toast.LENGTH_SHORT).show();        }    }}

全部同意效果(直接执行指定方法):

拒绝效果(再次申请是解释为什么需要该权限):

不再询问效果:


申请方式三: PermissionsDispatcher

简介:使用注解申请运行时权限的开源框架,还有插件可以使用,很方便。

GitHub地址,点击这里跳转

注解说明:

  • @RuntimePermissions:必填。在需要权限的Activity/Fragment前添加该注解。
  • @NeedsPermission:必填。在需要权限的方法前添加该注解,并传入所需权限。
  • @OnShowRationale:可选。在向用户解释为什么需要该权限的方法前添加该注解,并传入权限。
  • @OnPermissionDenied:可选。在用户拒绝授权调用的方法前添加该注解。
  • @OnNeverAskAgain:可选。在用户选择“不在询问”调用的方法前添加该注解。

注意:添加注解的方法不能是private的。

3.1 导包

compile("com.github.hotchemi:permissionsdispatcher:${latest.version}") {    // if you don't use android.app.Fragment you can exclude support for them    exclude module: "support-v13"}annotationProcessor "com.github.hotchemi:permissionsdispatcher-processor:${latest.version}"

3.2 示例

使用步骤:

  1. 在需要权限的Activity/Fragment前添加@RuntimePermissions注解;
  2. 在需要权限的方法前添加@NeedsPermission()注解;
  3. 按需求添加@OnShowRationale()注解、@OnPermissionDenied()注解、@OnNeverAskAgain()注解;
  4. Make Project;
  5. 使用自动生成的检查权限方法;
  6. 重写onRequestPermissionsResult()方法。

示例:

//第一步@RuntimePermissionspublic class Permission4Activity extends AppCompatActivity {    @Override    protected void onCreate(@Nullable Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_permission2);        ButterKnife.bind(this);    }    @OnClick(R.id.basePer_btn_phone})    public void onViewClicked(View view) {        switch (view.getId()) {            case R.id.basePer_btn_phone:            break;        }    }    //第二步    @NeedsPermission({Manifest.permission.CAMERA,Manifest.permission.READ_PHONE_STATE})    void recordVideo() {        Toast.makeText(this,"开始录制视频",Toast.LENGTH_SHORT).show();    }    //第三步--可选    @OnShowRationale({Manifest.permission.CAMERA,Manifest.permission.READ_PHONE_STATE})    void showRationale(final PermissionRequest request) {        new AlertDialog.Builder(this)            .setMessage("录制视频需要摄像头权限和读取手机信息权限")            .setPositiveButton("确定", new DialogInterface.OnClickListener() {                @Override                public void onClick(DialogInterface dialog, int which) {                    request.proceed();                }            })            .setNegativeButton("取消", new DialogInterface.OnClickListener() {            @Override                public void onClick(DialogInterface dialog, int which) {                    request.cancel();                }            })            .show();    }    //第三步--可选    @OnPermissionDenied({Manifest.permission.CAMERA,Manifest.permission.READ_PHONE_STATE})        void  showDenied(){        Toast.makeText(this, "用户拒绝某些授权", Toast.LENGTH_SHORT).show();    }    //第三步--可选    @OnNeverAskAgain({Manifest.permission.CAMERA,Manifest.permission.READ_PHONE_STATE})    void showNeverAskAgain(){        new AppSettingsDialog.Builder(this)        .setRationale("缺少请求的权限导致该功能无法使用,打开app设置界面开启相关权限").build().show();    }}

第四步:Make Project

完成后会自动生成[Activity Name] + PermissionsDispatcher类。

第五步:检查权限

第六步:重写onRequestPermissionsResult()方法

3.3 使用PermissionsDispatcher插件

3.3.1 安装插件

3.3.2 使用插件

3.3.2.1 动态导包

备注:执行一次即可。

3.3.2.2 添加权限

选择申请的权限和添加的方法:

完成后,在相应方法中添加逻辑。

检查权限

五、机型适配

5.1 小米手机

Manifest.permission.READ_PHONE_STATE权限要单独申请。

原创粉丝点击