Android动态权限

和以前在安装 APP 的是就申请了权限不同,Google 在 API 23,也就 6.0 之后加入了动态权限。对于一些敏感的权限,决定权交还给了用户,不再是强制申请了。因为这个原因,如果 APP 需要支持 6.0 以上的系统,就需要进行一下适配,否则 APP 就会崩溃。

比如,下面拨打电话.。已经在在 AndroidManifest.xml 中申明权限了

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

这是拨打电话的代码实现

private void callPhone() {
    Intent intent = new Intent(Intent.ACTION_CALL);
    Uri data = Uri.parse("tel:" + "12312341234");
    intent.setData(data);
    startActivity(intent);
}

但是程序如果是在 Android 6.0 的设备中运行的话,一运行就崩溃了,报错信息如下

java.lang.SecurityException: Permission Denial: ... 后面的信息省略

那么要怎么才能适配呢?

动态权限有哪些?

首先要先知道动态权限有哪些?

Andriod 中的动态权限是按组来分的,下面的表格来自官网

runtime_permissions

每个组中的权限不用全部申请,申请了其中一个,组中其他权限也就一起申请到了。

怎么申请动态权限?

有个前提,我们的 Activity 比如 MainActivity 不再是继承于 Acticity 了,而是继承于 AppCompatActivity,因为动态权限的一些方法是只有 AppCompatActivity 才有的。

首先是检测是否已经赋予了权限,可以调用 ContextCompat.checkSelfPermission(Context, permissionName) 方法,通过方法的返回值来判断

if (ContextCompat.checkSelfPermission(this,
        Manifest.permission.CALL_PHONE)
        == PackageManager.PERMISSION_GRANTED) {
    // 已经赋予权限,直接调用拨打电话的代码
    callPhone();
} else {
    // 没有赋予权限,那就去申请权限
}

要申请权限可以使用 requestPermissions (Activity activity, String[] permissions, int requestCode) 进行申请

private static final int REQUEST_CALL_PHONE = 456;

ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.CALL_PHONE},
                                  REQUEST_CALL_PHONE);

于是在界面上就会弹出一个请求拨打电话权限的对话框(这个对话框是系统弹出来的,样式不可修改)

request_call_phone_permission

不管用户选择同意还是拒绝,都会在 onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) 方法中接收到信息

@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions,
                                       @NonNull int[] grantResults) {
    switch (requestCode) {
        case REQUEST_CALL_PHONE:
            if (grantResults[0] == PackageManager.PERMISSION_GRANTED) {
                // 权限请求成功
                callPhone();
            } else {
                // 用户拒绝了
                showTipDialog();
            }
            break;

        default:
            super.onRequestPermissionsResult(requestCode, permissions, grantResults);
    }
}

private void showTipDialog() {
    new AlertDialog.Builder(this)
            .setMessage("该程序需要电话权限,否则无法正常运行")
            .setPositiveButton(android.R.string.ok, null)
            .create()
            .show();
}

如果用户选择”同意“,就可以调用拨打电话的按钮,同时这个对话框之后都不会出现了。

如果用户拒绝了,那就无法调用拨打电话的代码了。为了用户体验,可以弹出一个对话框,告知用户我需要这个权限,没有这个权限程序无法正常运行

用户看到了这个信息,于是再次点击“拨打电话”的按钮,程序又进行了权限检查,弹出申请权限的对话框,这时候的对话框的和之前第一次进行权限申请时弹出的对话框的样式不同,多出了一个“不再提醒”的勾选项

permission_not_ask_agin

如果用户勾选“不再询问”,对话框是这样的

choose_not_ask_agin

意味着用户永远地拒绝拨打电话的权限,同时这个对话框不会再弹出来了,这就造成程序无法正常运行。还有办法可以补救一下,就是跳转到设置页面,让用户手动开启权限。Anroid 提供了一个方法 ActivityCompat.shouldShowRequestPermissionRationale(this, Manifest.permission.CALL_PHONE) 可以用来判断是否选择了“不再提示”

@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions,
                                       @NonNull int[] grantResults) {
    switch (requestCode) {
        case REQUEST_CALL_PHONE:
            if (grantResults[0] == PackageManager.PERMISSION_GRANTED) {
                // 权限请求成功
                callPhone();
            } else {
                if (ActivityCompat.shouldShowRequestPermissionRationale(this, Manifest.permission.CALL_PHONE)) {
                    // 用户选择了“拒绝”
                    showTipDialog();
                } else {
                    // 用户勾选了“不再提示”
                    goToSetting();
                }
            }
            break;

        default:
            super.onRequestPermissionsResult(requestCode, permissions, grantResults);
    }
}

private void goToSetting() {
    new AlertDialog.Builder(this)
            .setMessage("该程序需要电话权限,否则无法正常运行")
            .setPositiveButton("去打开权限", new DialogInterface.OnClickListener() {
                @Override
                public void onClick(DialogInterface dialog, int which) {
                    Intent intent = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS);
                    Uri uri = Uri.fromParts("package", getPackageName(), null);
                    intent.setData(uri);
                    startActivity(intent);
                }
            })
            .create()
            .show();
}

完整代码

public class MainActivity extends AppCompatActivity {

    private static final int REQUEST_CALL_PHONE = 456;

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

        findViewById(R.id.btn_capture).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                callPhoneWithCheck();
            }
        });
    }

    private void callPhone() {
        Intent intent = new Intent(Intent.ACTION_CALL);
        Uri data = Uri.parse("tel:" + "12312341234");
        intent.setData(data);
        startActivity(intent);
    }

    private void callPhoneWithCheck() {
        if (ContextCompat.checkSelfPermission(this,
                Manifest.permission.CALL_PHONE)
                == PackageManager.PERMISSION_GRANTED) {
            // 已经赋予权限
            callPhone();
        } else {
             ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.CALL_PHONE},
                        REQUEST_CALL_PHONE);
        }
    }

    @Override
    public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions,
                                           @NonNull int[] grantResults) {
        switch (requestCode) {
            case REQUEST_CALL_PHONE:
                if (grantResults[0] == PackageManager.PERMISSION_GRANTED) {
                    // 权限请求成功
                    callPhone();
                } else {
                    if (ActivityCompat.shouldShowRequestPermissionRationale(this, Manifest.permission.CALL_PHONE)) {
                        // 用户选择了“拒绝”
                        showTipDialog();
                    } else {
                        // 用户勾选了“不再提示”
                        goToSetting();
                    }
                }
                break;

            default:
                super.onRequestPermissionsResult(requestCode, permissions, grantResults);
        }
    }

    private void goToSetting() {
        new AlertDialog.Builder(this)
                .setMessage("该程序需要电话权限,否则无法正常运行")
                .setPositiveButton("去打开权限", new DialogInterface.OnClickListener() {
                    @Override
                    public void onClick(DialogInterface dialog, int which) {
                        Intent intent = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS);
                        Uri uri = Uri.fromParts("package", getPackageName(), null);
                        intent.setData(uri);
                        startActivity(intent);
                    }
                })
                .create()
                .show();
    }

    private void showTipDialog() {
        new AlertDialog.Builder(this)
                .setMessage("该程序需要电话权限,否则无法正常运行")
                .setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
                    @Override
                    public void onClick(DialogInterface dialog, int which) {
                        ActivityCompat.requestPermissions(MainActivity.this, new String[]{Manifest.permission.CALL_PHONE},
                                REQUEST_CALL_PHONE);
                    }
                })
                .create()
                .show();
    }
}

开源库

为了申请一个拨打电话的权限,要写好多代码。网上有个一个流行的开源库 PermissionsDispatcher,采用注解来快速实现以上的逻辑。用法很简单,就是需要注意到是,写完代码是需要编译一下 project,这样才能生成相应的代码。

参考来源

http://blog.csdn.net/zhangqinghuazhangzhe/article/details/52801202
http://www.jianshu.com/p/d6b3e16cc1d9
http://www.jcodecraeer.com/a/anzhuokaifa/androidkaifa/2015/1110/3670.html
http://www.jianshu.com/p/b4a8b3d4f587
http://blog.csdn.net/quan356270259/article/details/50876272
http://blog.csdn.net/u010483016/article/details/50401605
http://www.jianshu.com/p/e1ab1a179fbb#
http://blog.csdn.net/u011200604/article/details/52874599
http://blog.csdn.net/yangqingqo/article/details/48371123

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,445评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,889评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,047评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,760评论 1 276
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,745评论 5 367
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,638评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,011评论 3 398
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,669评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,923评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,655评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,740评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,406评论 4 320
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,995评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,961评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,197评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,023评论 2 350
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,483评论 2 342

推荐阅读更多精彩内容

  • Android 自定义View的各种姿势1 Activity的显示之ViewRootImpl详解 Activity...
    passiontim阅读 171,431评论 25 707
  • 最近APP里面要添加动态权限,网上找了不少例子并经过一定的测试,基本流程有了一定的认识,需要注意的地方记录下来以备...
    Tom老兄_baaa阅读 1,037评论 0 1
  • 6.0以后分出了危险权限,危险权限需要动态申请,他们分了组,如果申请了组内的某个权限,那么整个组的权限都会被授予 ...
    达峰a阅读 4,876评论 0 9
  • 21.3 兼容性问题 新的运行时权限仅当APP的targetSdkVersion 为23,且APP运行在安装了An...
    xjbclz阅读 368评论 0 1
  • 上班的路上,很经常会遇到一个女子。我走路的这个方向,她走路的那个方向。 刚开始留意她,是因为她的衣服。 淡蓝色牛仔...
    修女Maria阅读 229评论 0 0