Android——Permission 6.0权限管理

Android6.0运行时权限描述

在旧的权限管理系统中,权限仅仅在App安装时询问用户一次,用户同意了这些权限App才能被安装。

在Android6.0开始,App可以直接安装,App在运行时一个一个询问用户授予权限,系统会弹出一个对话框让用户选择是否授权某个权限给App(这个Dialog不能由开发者定制),当App需要用户授予不恰当的权限的时候,用户可以拒绝,用户也可以在设置页面对每个App的权限进行管理。

特别注意:这个对话框不是开发者调用某个权限的功能时由系统自动弹出,而是需要开发者手动调用,如果你直接调用而没有去申请权限的话,将会导致App崩溃。

需要动态申请的权限

所有危险的Android系统权限属于权限组,如果APP运行在Android 6.0 (API level 23)或者更高级别的设备中,而且targetSdkVersion>=23时,系统将会自动采用动态权限管理策略,如果你在涉及到特殊权限操作时没有申请权限权限而直接调用了相关代码,你的App可能就崩溃了。

1、此类权限也必须在 AndroidManifest 中申明,否则申请时不提示用户,直接回调开发者权限被拒绝。
2、同一个权限组的任何一个权限被授权了,这个权限组的其他权限也自动被授权。例如一旦WRITE_CONTACTS被授权了,App也有READ_CONTACTS和GET_ACCOUNTS了。
3、申请某一个权限的时候系统弹出的Dialog是对整个权限组的说明,而不是单个权限。例如我申请READ_EXTERNAL_STORAGE,系统会提示"允许xxx访问设备上的照片、媒体内容和文件吗?"。

    共分为9组,每组只要有一个权限申请成功了,就默认整组权限都可以使用了。

          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
            permission:com.android.voicemail.permission.ADD_VOICEMAIL

          group:android.permission-group.CALENDAR
            permission:android.permission.READ_CALENDAR
            permission:android.permission.WRITE_CALENDAR

          group:android.permission-group.CAMERA
            permission:android.permission.CAMERA

          group:android.permission-group.SENSORS
            permission:android.permission.BODY_SENSORS

          group:android.permission-group.LOCATION
            permission:android.permission.ACCESS_FINE_LOCATION
            permission:android.permission.ACCESS_COARSE_LOCATION

          group:android.permission-group.STORAGE
            permission:android.permission.READ_EXTERNAL_STORAGE
            permission:android.permission.WRITE_EXTERNAL_STORAGE

          group:android.permission-group.MICROPHONE
            permission:android.permission.RECORD_AUDIO

          group:android.permission-group.SMS
            permission:android.permission.READ_SMS
            permission:android.permission.RECEIVE_WAP_PUSH
            permission:android.permission.RECEIVE_MMS
            permission:android.permission.RECEIVE_SMS
            permission:android.permission.SEND_SMS
            permission:android.permission.READ_CELL_BROADCASTS

三、普通权限
此类权限都是正常保护的权限,只需要在AndroidManifest.xml中简单声明这些权限即可,安装即授权,不需要每次使用时都检查权限,而且用户不能取消以上授权,除非用户卸载App。

          android.permission.ACCESS_LOCATION_EXTRA_COMMANDS
          android.permission.ACCESS_NETWORK_STATE
          android.permission.ACCESS_NOTIFICATION_POLICY
          android.permission.ACCESS_WIFI_STATE
          android.permission.ACCESS_WIMAX_STATE
          android.permission.BLUETOOTH
          android.permission.BLUETOOTH_ADMIN
          android.permission.BROADCAST_STICKY
          android.permission.CHANGE_NETWORK_STATE
          android.permission.CHANGE_WIFI_MULTICAST_STATE
          android.permission.CHANGE_WIFI_STATE
          android.permission.CHANGE_WIMAX_STATE
          android.permission.DISABLE_KEYGUARD
          android.permission.EXPAND_STATUS_BAR
          android.permission.FLASHLIGHT
          android.permission.GET_ACCOUNTS
          android.permission.GET_PACKAGE_SIZE
          android.permission.INTERNET
          android.permission.KILL_BACKGROUND_PROCESSES
          android.permission.MODIFY_AUDIO_SETTINGS
          android.permission.NFC
          android.permission.READ_SYNC_SETTINGS
          android.permission.READ_SYNC_STATS
          android.permission.RECEIVE_BOOT_COMPLETED
          android.permission.REORDER_TASKS
          android.permission.REQUEST_INSTALL_PACKAGES
          android.permission.SET_TIME_ZONE
          android.permission.SET_WALLPAPER
          android.permission.SET_WALLPAPER_HINTS
          android.permission.SUBSCRIBED_FEEDS_READ
          android.permission.TRANSMIT_IR
          android.permission.USE_FINGERPRINT
          android.permission.VIBRATE
          android.permission.WAKE_LOCK
          android.permission.WRITE_SYNC_SETTINGS
          com.android.alarm.permission.SET_ALARM
          com.android.launcher.permission.INSTALL_SHORTCUT
          com.android.launcher.permission.UNINSTALL_SHORTCUT

相关API

1、权限是否开启的常量:
PackageManager.PERMISSION_DENIED:该权限是被拒绝的。
PackageManager.PERMISSION_GRANTED:该权限是被授权的。

2、v4包中的三个静态方法:
2.1、ContextCompat.checkSelfPermission(Context context, String permission)
2.2、ActivityCompat.requestPermissions(Activity activity,String[] permissions, int requestCode)
2.3、ActivityCompat.shouldShowRequestPermissionRationale(Activity activity,String permission)

2、Activity中或者Fragment中的几个方法:
2.1、 int checkSelfPermission(String permission)
2.2、void requestPermissions(String[] permissions, int requestCode)
2.3、boolean shouldShowRequestPermissionRationale(String permission)
2.4、void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults)

3、checkSelfPermission() 检查权限
3.1、检查某一个权限的当前状态,你应该在请求某个权限时检查这个权限是否已经被用户授权,已经授权的权限重复申请可能会让用户产生厌烦。
3.2、该方法有一个参数是权限名称,有一个int的返回值,用这个值与上面提到的两个常量做比较可判断检查的权限当前的状态。


if (ContextCompat.checkSelfPermission(context, Manifest.permission.READ_CONTACTS)
    != PackageManager.PERMISSION_GRANTED) {
     // 没有权限,去申请权限。
} else {
     // 有权限了,去执行操作。
}

4、requestPermissions() 申请权限
请求用户授权几个权限,调用后系统会显示一个请求用户授权的提示对话框,App不能配置和修改这个对话框,如果需要提示用户这个权限相关的信息或说明,需要在调用 requestPermissions() 之前处理,该方法有两个参数:
1、String[] permissions,权限数组,你需要申请的的权限的数组。
2、int requestCode,会在回调onRequestPermissionsResult()时返回,用来判断是哪个授权申请的回调。由于该方法是异步的,所以无返回值,当用户处理完授权操作时,会回调 Activity 或者 Fragment 的onRequestPermissionsResult() 方法。

4.1、对于Activity我们直接调用requestPermissions(int, String[])即可,不过这个方法是在api leve 23以上,所以我们为了适配可以是使用兼容包提供的方法:
ActivityCompat.requestPermissions(activity, new String[]{Manifest.permission.READ_CONTACTS}, requestCode);
4.2、对于support包的Fragment就可以直接调用requestPermissions(int, String[]),对于app包的Fragment就需要做版本判断了,这样就显得比较麻烦。

5、onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) 处理权限结果回调:
该方法在Activity/Fragment中应该被重写,当用户处理完授权操作时,系统会自动回调该方法,该方法有三个参数:
1、int requestCode,在调用requestPermissions()时的第一个参数。
2、String[] permissions,权限数组,在调用requestPermissions()时的第二个参数。
3、int[] grantResults,授权结果数组,对应permissions,具体值和上方提到的PackageManager中的两个常量做比较。

6、shouldShowRequestPermissionRationale():是否应该显示请求权限的说明。
注意:如果用户拒绝了这个权限,返回true;
如果用户拒绝了这个权限,并且勾选了不再提醒”选项,返回false.

第一次请求权限时,用户拒绝了,调用shouldShowRequestPermissionRationale()后返回true,应该显示一些为什么需要这个权限的说明。

用户在第一次拒绝某个权限后,下次再次申请时,授权的dialog中将会出现“不再提醒”选项,一旦选中勾选了,那么下次申请将不会提示用户。

第二次请求权限时,用户拒绝了,并选择了“不再提醒”的选项,调用shouldShowRequestPermissionRationale()后返回false。设备的策略禁止当前应用获取这个权限的授权:shouldShowRequestPermissionRationale()返回false 。

加这个提醒的好处在于,用户拒绝过一次权限后我们再次申请时可以提醒该权限的重要性,免得再次申请时用户勾选“不再提醒”并决绝,导致下次申请权限直接失败。

申请权限的步骤

1、检查权限checkSelfPermission(),通过返回值判断权限是否已经开启,如果已开启,直接做自己的操作。

2、如果权限未开启,则判断是否需要向用户解释为何申请权限 shouldShowRequestPermissionRationale。
2.1、返回true,向用户给出 为什么需要此权限的 提示。
2.2、返回false,则直接调用 requestPermissions() 去申请权限。

3、调用 requestPermissions() 去申请权限
3.1、申请成功,直接做自己的操作。
3.2、申请失败,再去判断 shouldShowRequestPermissionRationale ,
如果返回true,提示权限申请失败;如果返回false,表明用户勾选了“不再提醒”,应该引导用户去设置界面开启权限。

举个栗子

public class PermissionActivity extends BaseActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_permission);
        
        //如果不申请权限,程序会直接崩溃
        findViewById(R.id.bt_phone).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                Intent intent = new Intent();
                intent.setAction(Intent.ACTION_CALL);
                intent.setData(Uri.parse("tel://123456"));
                startActivity(intent);

            }
        });

        findViewById(R.id.bt_camera).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                //判断权限
                if (PackageManager.PERMISSION_GRANTED != checkSelfPermission(
                        Manifest.permission.CAMERA)) {
                    // 无权限
                    if (shouldShowRequestPermissionRationale(Manifest.permission.CAMERA)) {
                        //是否需要给出需要此权限的提示
                        new AlertDialog.Builder(PermissionActivity.this).setTitle("标题")
                                .setMessage("需要权限")
                                .setNegativeButton("取消", new DialogInterface.OnClickListener() {
                                    @Override
                                    public void onClick(DialogInterface dialog, int which) {
                                        dialog.dismiss();
                                    }
                                }).setPositiveButton("确定", new DialogInterface.OnClickListener() {
                                    @Override
                                    public void onClick(DialogInterface dialog, int which) {
                                        //申请权限
                                        requestCameraPermission();
                                    }
                                }).show();

                    } else {
                        //申请权限
                        requestCameraPermission();
                    }
                } else {
                    //有权限
                    openCamera();
                }
            }
        });
    }

    //申请权限
    private void requestCameraPermission() {
        requestPermissions(new String[] {Manifest.permission.CAMERA}, 100);
    }

    //打开相机
    private void openCamera() {
        Intent intent = new Intent();
        intent.setAction("android.media.action.STILL_IMAGE_CAMERA");
        startActivity(intent);
    }

    //打开设置界面
    private void openSetting(int requestCode) {
        Intent intent = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS);
        Uri uri = Uri.fromParts("package", getPackageName(), null);
        intent.setData(uri);
        startActivityForResult(intent, requestCode);
    }


    @Override
    public void onRequestPermissionsResult(final int requestCode, String[] permissions,
            int[] grantResults) {
        super.onRequestPermissionsResult(requestCode, permissions, grantResults);
        if (requestCode == 100) {
            if (PackageManager.PERMISSION_GRANTED == grantResults[0]) {
                openCamera();
            } else {
                if (!shouldShowRequestPermissionRationale(Manifest.permission.CAMERA)) {

                    new AlertDialog.Builder(PermissionActivity.this).setTitle("权限申请失败")
                            .setMessage("我们需要的一些权限被您拒绝或者系统发生错误申请失败,请您到设置界面手动授权,否则功能无法正常使用!")
                            .setNegativeButton("取消", new DialogInterface.OnClickListener() {
                                @Override
                                public void onClick(DialogInterface dialog, int which) {
                                    dialog.dismiss();
                                }
                            }).setPositiveButton("确定", new DialogInterface.OnClickListener() {
                                @Override
                                public void onClick(DialogInterface dialog, int which) {
                                    openSetting(requestCode);
                                }
                            }).show();
                }
            }
        }
    }

}

关于Permission第三方库的使用。

1、AndPermission——https://github.com/yanzhenjie/AndPermission
2、RxPermissions——https://github.com/tbruyelle/RxPermissions
3、PermissionsDispatcher——https://github.com/permissions-dispatcher/PermissionsDispatcher

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

推荐阅读更多精彩内容