从打电话权限报错看Android6.0权限变化

来源:互联网 发布:魔方相册源码 编辑:程序博客网 时间:2024/06/05 01:53

来自:http://blog.csdn.net/leejizhou/article/details/51511630

引言:去年Android 6.0发布后,其新引入的(Requesting Permissions at Run Time)运行时权限就备受开发者关注,随着今年国内手机厂商对6.0系统的普及,觉得大家有必要了解下这个新特性,因为在TargetSDK23+进行开发不注意这些会造成APP运行在6.0+手机上崩溃,这篇博文将对这个新特性进行探索。

在之前的SDK开发中,如果需要用到一些权限例如打电话,发短信只在AndroidManifest中配置一下就可以了,但是SDK23+以上用到一些危险敏感(Dangerous Permissions)权限就不仅仅是在AndroidManifest配置一下就可以了,需要在操作发生前需要让用户进行授予权限才能进行下一步的操作,跟iOS的权限处理很像,如果没让用户授予权限或用户拒绝了此权限再进行操作例如打电话就会造成软件崩溃。

*如果你的app TargetSDK设置在了23以下那么在6.0+系统中运行是不会崩溃的,当然你也可以TargetSDK一直设置在23以下,那么这篇文章你就没必要继续看了:)

那么到底哪些权限需要进行在运行时进行授权呢?看下官方的说明

Dangerous permissions

这里写图片描述

以上这些权限不仅仅需要在AndroidManifest配置,还需要在运行时让用户进行授予权限才能使用这些功能。

可以看到打电话是需要进行运行时授权的,我们就做个点击按钮打电话的小demo,看看这个运行时权限到底怎么回事。

1:需要将APP的targetSdkVersion设置到23以上并且在Android6.0以上系统运行

这里写图片描述

2:在AndroidManifest中配置拨打电话的权限

<uses-permission android:name="android.permission.CALL_PHONE"></uses-permission>
  • 1
  • 1

3:在布局layout中我们就简简单单放个按钮,点击进行拨打电话(略)

4.1:如果不做运行时权限处理会怎么样呢?我们直接进行点击按钮拨打电话

  public void CallPhone(View v) {        Intent intent = new Intent(Intent.ACTION_CALL);        Uri data = Uri.parse("tel:" + "10010");        intent.setData(data);        startActivity(intent);    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

运行效果:

这里写图片描述

可以看到在android 6.0系统上运行会崩溃

4.2:我们加上运行时权限的处理再运行

 private static final int REQUESTCODE = 8;    public void CallPhone(View v) {        //检查权限        if (ContextCompat.checkSelfPermission(this,                Manifest.permission.CALL_PHONE)                != PackageManager.PERMISSION_GRANTED) {            //没有权限,申请权限            ActivityCompat.requestPermissions(this,                    new String[]{Manifest.permission.CALL_PHONE},                    REQUESTCODE);        } else {            //已经拥有权限进行拨打            call();        }    }
  • 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

API: 
REQUESTCODE的作用是为了进行回调处理,因为申请权限是有回调结果的后面会说到。

ContextCompat.checkSelfPermission 主要用于检测某个权限是否已经被授予,方法参数为(context,需要检测的权限)方法返回值为PackageManager.PERMISSION_DENIED或者PackageManager.PERMISSION_GRANTED,当返回PackageManager.PERMISSION_DENIED时需要调用API进行权限申请。

ActivityCompat.requestPermissions 用于权限的申请,方法参数为(context,需要申请的权限数组,自定义的请求码),系统会弹出一个申请权限的对话框。

运行效果: 
这里写图片描述

可以看到程序已经成功进行了电话的拨打。

4.3 但是如果用户拒绝了此权限并且设置了不再提醒怎么办呢?点击按钮就会没响应了,是非常不友好的,如下。

这里写图片描述

稍稍改下代码

 public void CallPhone(View v) {        //检查权限        if (ContextCompat.checkSelfPermission(this,                Manifest.permission.CALL_PHONE)                != PackageManager.PERMISSION_GRANTED) {            if (ActivityCompat.shouldShowRequestPermissionRationale(this,                    Manifest.permission.CALL_PHONE)) {                new AlertDialog.Builder(MainActivity.this)                        .setMessage("app需要开启权限才能使用此功能")                        .setPositiveButton("设置", new DialogInterface.OnClickListener() {                            @Override                            public void onClick(DialogInterface dialogInterface, int i) {                                Intent intent = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS);                                intent.setData(Uri.parse("package:" + getPackageName()));                                startActivity(intent);                            }                        })                        .setNegativeButton("取消", null)                        .create()                        .show();            } else {                //申请权限                ActivityCompat.requestPermissions(this,                        new String[]{Manifest.permission.CALL_PHONE},                        REQUESTCODE);            }        } else {            //已经拥有权限进行拨打            call();        }    }
  • 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
  • 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

这里我们在申请权限前加了个判断ActivityCompat.shouldShowRequestPermissionRationale(this, 
Manifest.permission.CALL_PHONE)
方法参数为(context,需要检测的权限)如果返回true证明用户上次点击已经选了拒绝,所以我们进行一些友好的提示,这里做的是进行进行提示并让用户跳转到设置将权限打开。

运行效果: 
这里写图片描述

5:OK 最后介绍下,申请权限的回调方法处理

 @Override    public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {        switch (requestCode) {            case REQUESTCODE: {                if (grantResults.length > 0                        && grantResults[0] == PackageManager.PERMISSION_GRANTED) {                    //用户同意了授权                    call();                } else {                    //用户拒绝了授权                // Toast.makeText(MainActivity.this, "Permission Denied", Toast.LENGTH_SHORT).show();                }                return;            }        }    }
  • 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

这个方法跟onActivityResult比较类似,先判断requestCode,之后在判断用户的授权状态,grantResults数组代表了权限的结果数组,有点绕口,哈哈,之前申请权限是支持数组的,所以 onRequestPermissionsResult的返回结果也放到了一个数组里面,数组grantResults[n] == PackageManager.PERMISSION_GRANTED代表这个权限已经被用户授权了。

final:最后我们看下完整的代码

/** * blog:www.lijizhou.com */public class MainActivity extends AppCompatActivity {    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_main);    }    private static final int REQUESTCODE = 8;    public void CallPhone(View v) {        //检查权限        if (ContextCompat.checkSelfPermission(this,                Manifest.permission.CALL_PHONE)                != PackageManager.PERMISSION_GRANTED) {            if (ActivityCompat.shouldShowRequestPermissionRationale(this,                    Manifest.permission.CALL_PHONE)) {                new AlertDialog.Builder(MainActivity.this)                        .setMessage("app需要开启权限才能使用此功能")                        .setPositiveButton("设置", new DialogInterface.OnClickListener() {                            @Override                            public void onClick(DialogInterface dialogInterface, int i) {                                Intent intent = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS);                                intent.setData(Uri.parse("package:" + getPackageName()));                                startActivity(intent);                            }                        })                        .setNegativeButton("取消", null)                        .create()                        .show();            } else {                //申请权限                ActivityCompat.requestPermissions(this,                        new String[]{Manifest.permission.CALL_PHONE},                        REQUESTCODE);            }        } else {            //已经拥有权限进行拨打            call();        }    }    private void call() {        Intent intent = new Intent(Intent.ACTION_CALL);        Uri data = Uri.parse("tel:" + "10010");        intent.setData(data);        startActivity(intent);    }    @Override    public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {        switch (requestCode) {            case REQUESTCODE: {                if (grantResults.length > 0                        && grantResults[0] == PackageManager.PERMISSION_GRANTED) {                    //用户同意了授权                    call();                } else {                    //用户拒绝了授权                // Toast.makeText(MainActivity.this, "Permission Denied", Toast.LENGTH_SHORT).show();                }                return;            }        }    }}
  • 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
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 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
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81

OK,一个利用android原始API进行的运行时权限处理就介绍完了,当然你也可以对此进行封装方便使用,欢迎大家在下方留言,本篇源码下载地址 http://download.csdn.net/detail/leejizhou/9532629


最后如果只是调起打电话功能,其实谷歌是推荐采用
intent.setAction(Intent.ACTION_DIAL);intent.setData(Uri.parse("tel:" + phone));
这样将跳到电话拨打界面,而不是直接调起打电话。

0 0
原创粉丝点击