Android 6.0 运行时权限处理解析

目录(?)[+]

  1. 概述
  2. 2运行时权限的检测 

1.概述


  不知道大家有没有遇到过这种情况,开发app的时候发现自己手机选择照片是正常的,测试那边的一台手机怎么搞都不行,然后查看版本之后才发现是6.0的手机。
  
  随着Android 6.0 7.0 我们开发者所要应对的主要就是新版本SDK带来的一些变化,既然是程序员那么我们肯定就特别关注开发部分的变化,其中之一就是权限处理。那么在6.0及以上版本我们的危险权限都需要在运行的时候去申请,之前都是在清单文件中配置即可,现在就不行了需要加代码申请。
  
这里写图片描述

  那么有人就想如果我还是用原来的那种方式开发,不在运行时申请而是只在清单文件中声明呢?最终导致的结果就是6.0以上的手机可能就会出Bug比如获取不到照片,打不了电话等等,我们可以选择将sdk的版本降低,我也试过可以绕过这个。但是我们的测试竟然拒绝了一次权限,竟然拒绝竟然拒绝,我真是…所以我们最终的解决方式就是用代码动态的申请权限。附视频讲解地址:http://pan.baidu.com/s/1bpqqkGn

2.运行时权限的检测 


  2.1. Android6.0之后的权限差别
  
  对于6.0以下的权限及在安装的时候,根据权限声明产生一个权限列表,用户只有在同意之后才能完成app的安装。而在6.0以后,我们可以直接安装,当app需要权限是会给予用户提示用户可以选择同意和拒绝。

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

看几个Normal Permissions:

INTERNET
GET_PACKAGE_SIZE
ACCESS_NETWORK_STATE
ACCESS_NOTIFICATION_POLICY
ACCESS_WIFI_STATE
CHANGE_WIFI_STATE
VIBRATE
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

看几组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
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19

  你会发现dangerous permissions,危险权限都是一组一组的,这是个什么概念呢?又或是有什么用呢?如果app运行在Android 6.x的机器上,对于授权机制是这样的。如果你申请某个危险的权限,假设你的app早已被用户授权了同一组的某个危险权限,那么系统会立即授权,而不需要用户去点击授权。比如你的app对READ_CONTACTS已经授权了,当你的app申请WRITE_CONTACTS时,系统会直接授权通过。此外,对于申请时弹出的dialog上面的文本说明也是对整个权限组的说明,而不是单个权限(ps:这个dialog是不能进行定制的)。
  
  
2.2. 代码变化

2.2.1.之前写都是老套路直接上代码(已电话拨打为例):

        Intent intent = new Intent(Intent.ACTION_CALL);
        Uri data = Uri.parse("tel:" + mPhoneNumber);
        intent.setData(data);
        startActivity(intent);
  • 1
  • 2
  • 3
  • 4
  • 1
  • 2
  • 3
  • 4

2.2.2.但是现在不行啦,我们需要去检测该权限有没有背用户授予过,如果没有则需要申请打电话权限,如果有授予过可以直接拨打电话。
ContextCompat.checkSelfPermission:检测权限
ActivityCompat.requestPermissions:申请权限

// ContextCompat.checkSelfPermission()
// 方法返回值为PackageManager.PERMISSION_DENIED或者PackageManager.PERMISSION_GRANTED。
// 当返回GRANTED表示有该权限,DENIED表示没有该权限。

if(ContextCompat.checkSelfPermission(this,Manifest.permission.CALL_PHONE)
    != PackageManager.PERMISSION_GRANTED){

            // 没有该权限 申请打电话权限
            //  三个参数 第一个参数是 Context , 第二个参数是用户需要申请的权限字符串数组,第三个参数是请求码 主要用来处理用户选择的返回结果

            ActivityCompat.requestPermissions(this,new String[]{"Manifest.permission.CALL_PHONE"},CALL_PHONE_REQUEST_CODE);
        }else {
            // 有该权限,直接打电话
            Intent intent = new Intent(Intent.ACTION_CALL);
            Uri data = Uri.parse("tel:" + 137XXXXXXXX);
            intent.setData(data);
            startActivity(intent);
        }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18

2.2.3. 处理回调
  如果用户同意或是拒绝那么会回调onRequestPermissionsResult(),别看错了不是onActivityResult()

    @Override
    public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
        if(requestCode == CALL_PHONE_REQUEST_CODE){
            if (grantResults !=null&&grantResults[0] == PackageManager.PERMISSION_GRANTED) {
                // Permission Granted  通过  打电话

                Intent intent = new Intent(Intent.ACTION_CALL);
                Uri data = Uri.parse("tel:" + 137XXXXXXXX);
                intent.setData(data);
                startActivity(intent);
            } else {
                // Permission Denied   被拒绝
                Toast.makeText(this,"权限被拒绝了",Toast.LENGTH_SHORT).show();
            }
        }
    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16

2.2.4. 简单的例子

public class MainActivity extends AppCompatActivity {
    // 打电话权限申请的请求码
    private static final int CALL_PHONE_REQUEST_CODE = 0x0011;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
    }

    public void phoneClick(View view){
        if(ContextCompat.checkSelfPermission(this, Manifest.permission.CALL_PHONE)
                != PackageManager.PERMISSION_GRANTED){
            Toast.makeText(this, "申请权限", Toast.LENGTH_SHORT).show();
            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 data = Uri.parse("tel:147****2514");
        intent.setData(data);
        startActivity(intent);
    }

    @Override
    public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
        super.onRequestPermissionsResult(requestCode, permissions, grantResults);
        if(requestCode == CALL_PHONE_REQUEST_CODE){
            if (grantResults !=null&&grantResults[0] == PackageManager.PERMISSION_GRANTED) {
                // Permission Granted
                callPhone();
            } else {
                // Permission Denied
                Toast.makeText(this,"权限被拒绝了",Toast.LENGTH_SHORT).show();
            }
        }
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46

  上面就是6.0以上的版本运行时权限处理,但是我们会发现一个问题,如果都得这么干那需要些多少代码量?下面我们就利用反射加注解的方式封装我们的权限处理框架,请看这里Android 6.0 运行时权限封装框架

这里写图片描述
  
  项目的代码不能够发给大家,里面涉及到后台接口以及数据加密,如果大家感兴趣可以看一下我录的视频:http://pan.baidu.com/s/1bpqqkGn

1.概述


  不知道大家有没有遇到过这种情况,开发app的时候发现自己手机选择照片是正常的,测试那边的一台手机怎么搞都不行,然后查看版本之后才发现是6.0的手机。
  
  随着Android 6.0 7.0 我们开发者所要应对的主要就是新版本SDK带来的一些变化,既然是程序员那么我们肯定就特别关注开发部分的变化,其中之一就是权限处理。那么在6.0及以上版本我们的危险权限都需要在运行的时候去申请,之前都是在清单文件中配置即可,现在就不行了需要加代码申请。
  
这里写图片描述

  那么有人就想如果我还是用原来的那种方式开发,不在运行时申请而是只在清单文件中声明呢?最终导致的结果就是6.0以上的手机可能就会出Bug比如获取不到照片,打不了电话等等,我们可以选择将sdk的版本降低,我也试过可以绕过这个。但是我们的测试竟然拒绝了一次权限,竟然拒绝竟然拒绝,我真是…所以我们最终的解决方式就是用代码动态的申请权限。附视频讲解地址:http://pan.baidu.com/s/1bpqqkGn

2.运行时权限的检测 


  2.1. Android6.0之后的权限差别
  
  对于6.0以下的权限及在安装的时候,根据权限声明产生一个权限列表,用户只有在同意之后才能完成app的安装。而在6.0以后,我们可以直接安装,当app需要权限是会给予用户提示用户可以选择同意和拒绝。

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

看几个Normal Permissions:

INTERNET
GET_PACKAGE_SIZE
ACCESS_NETWORK_STATE
ACCESS_NOTIFICATION_POLICY
ACCESS_WIFI_STATE
CHANGE_WIFI_STATE
VIBRATE
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

看几组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
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19

  你会发现dangerous permissions,危险权限都是一组一组的,这是个什么概念呢?又或是有什么用呢?如果app运行在Android 6.x的机器上,对于授权机制是这样的。如果你申请某个危险的权限,假设你的app早已被用户授权了同一组的某个危险权限,那么系统会立即授权,而不需要用户去点击授权。比如你的app对READ_CONTACTS已经授权了,当你的app申请WRITE_CONTACTS时,系统会直接授权通过。此外,对于申请时弹出的dialog上面的文本说明也是对整个权限组的说明,而不是单个权限(ps:这个dialog是不能进行定制的)。
  
  
2.2. 代码变化

2.2.1.之前写都是老套路直接上代码(已电话拨打为例):

        Intent intent = new Intent(Intent.ACTION_CALL);
        Uri data = Uri.parse("tel:" + mPhoneNumber);
        intent.setData(data);
        startActivity(intent);
  • 1
  • 2
  • 3
  • 4
  • 1
  • 2
  • 3
  • 4

2.2.2.但是现在不行啦,我们需要去检测该权限有没有背用户授予过,如果没有则需要申请打电话权限,如果有授予过可以直接拨打电话。
ContextCompat.checkSelfPermission:检测权限
ActivityCompat.requestPermissions:申请权限

// ContextCompat.checkSelfPermission()
// 方法返回值为PackageManager.PERMISSION_DENIED或者PackageManager.PERMISSION_GRANTED。
// 当返回GRANTED表示有该权限,DENIED表示没有该权限。

if(ContextCompat.checkSelfPermission(this,Manifest.permission.CALL_PHONE)
    != PackageManager.PERMISSION_GRANTED){

            // 没有该权限 申请打电话权限
            //  三个参数 第一个参数是 Context , 第二个参数是用户需要申请的权限字符串数组,第三个参数是请求码 主要用来处理用户选择的返回结果

            ActivityCompat.requestPermissions(this,new String[]{"Manifest.permission.CALL_PHONE"},CALL_PHONE_REQUEST_CODE);
        }else {
            // 有该权限,直接打电话
            Intent intent = new Intent(Intent.ACTION_CALL);
            Uri data = Uri.parse("tel:" + 137XXXXXXXX);
            intent.setData(data);
            startActivity(intent);
        }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18

2.2.3. 处理回调
  如果用户同意或是拒绝那么会回调onRequestPermissionsResult(),别看错了不是onActivityResult()

    @Override
    public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
        if(requestCode == CALL_PHONE_REQUEST_CODE){
            if (grantResults !=null&&grantResults[0] == PackageManager.PERMISSION_GRANTED) {
                // Permission Granted  通过  打电话

                Intent intent = new Intent(Intent.ACTION_CALL);
                Uri data = Uri.parse("tel:" + 137XXXXXXXX);
                intent.setData(data);
                startActivity(intent);
            } else {
                // Permission Denied   被拒绝
                Toast.makeText(this,"权限被拒绝了",Toast.LENGTH_SHORT).show();
            }
        }
    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16

2.2.4. 简单的例子

public class MainActivity extends AppCompatActivity {
    // 打电话权限申请的请求码
    private static final int CALL_PHONE_REQUEST_CODE = 0x0011;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
    }

    public void phoneClick(View view){
        if(ContextCompat.checkSelfPermission(this, Manifest.permission.CALL_PHONE)
                != PackageManager.PERMISSION_GRANTED){
            Toast.makeText(this, "申请权限", Toast.LENGTH_SHORT).show();
            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 data = Uri.parse("tel:147****2514");
        intent.setData(data);
        startActivity(intent);
    }

    @Override
    public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
        super.onRequestPermissionsResult(requestCode, permissions, grantResults);
        if(requestCode == CALL_PHONE_REQUEST_CODE){
            if (grantResults !=null&&grantResults[0] == PackageManager.PERMISSION_GRANTED) {
                // Permission Granted
                callPhone();
            } else {
                // Permission Denied
                Toast.makeText(this,"权限被拒绝了",Toast.LENGTH_SHORT).show();
            }
        }
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46

  上面就是6.0以上的版本运行时权限处理,但是我们会发现一个问题,如果都得这么干那需要些多少代码量?下面我们就利用反射加注解的方式封装我们的权限处理框架,请看这里Android 6.0 运行时权限封装框架

这里写图片描述
  
  项目的代码不能够发给大家,里面涉及到后台接口以及数据加密,如果大家感兴趣可以看一下我录的视频:http://pan.baidu.com/s/1bpqqkGn

猜你喜欢

转载自blog.csdn.net/qq_35114086/article/details/70053716