Android6.0动态权限

Android6.0动态权限

在6.0以后,Google将权限分为两类,一类是Normal Permissions,这类权限一般不涉及用户隐私,是不需要用户进行授权的,比如手机震动、访问网络等;另一类是Dangerous Permission,一般是涉及到用户隐私的,需要用户进行授权,比如读取sdcard、访问通讯录等。

普通权限

  • Normal Permissions(普通权限):
--
ACCESS_LOCATION_EXTRA_COMMANDS定位权限
ACCESS_NETWORK_STATE网络状态权限
ACCESS_NOTIFICATION_POLICY通知 APP通知显示在状态栏
ACCESS_WIFI_STATEWiFi状态权限
BLUETOOTH使用蓝牙权限
BLUETOOTH_ADMIN控制蓝牙开关
BROADCAST_STICKY粘性广播
CHANGE_NETWORK_STATE改变网络状态
CHANGE_WIFI_MULTICAST_STATE改变WiFi多播状态,应该是控制手机热点(猜测)
CHANGE_WIFI_STATE控制WiFi开关,改变WiFi状态
DISABLE_KEYGUARD改变键盘为不可用
EXPAND_STATUS_BAR扩展bar的状态
GET_PACKAGE_SIZE获取应用安装包大小
INTERNET网络权限
KILL_BACKGROUND_PROCESSES杀死后台进程
MODIFY_AUDIO_SETTINGS改变音频输出设置
NFC支付
READ_SYNC_SETTINGS获取手机设置信息
READ_SYNC_STATS数据统计
RECEIVE_BOOT_COMPLETED监听启动广播
REORDER_TASKS创建新栈
REQUEST_INSTALL_PACKAGES安装应用程序
SET_TIME_ZONE允许应用程序设置系统时间区域
SET_WALLPAPER设置壁纸
SET_WALLPAPER_HINTS设置壁纸上的提示信息,个性化语言
TRANSMIT_IR红外发射
USE_FINGERPRINT指纹识别
VIBRATE震动
WAKE_LOCK锁屏
WRITE_SYNC_SETTINGS改变设置
SET_ALARM设置警告提示
INSTALL_SHORTCUT创建快捷方式
UNINSTALL_SHORTCUT删除快捷方式

危险权限

  • 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
  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

可以通过adb shell pm list permissions -d -g进行查看。

这类权限需要在需要的时候,需要我们动态申请,比如:当我们需要打开相机拍摄照片的时候需要我们通过代码的方式在需要的地方去申请权限。Android6.0中权限问题中我们需要注意的是:

  • 1:由于权限API的问题,我们的Actiivty最好是AppCompatActivity类型的,也就是说在你的BaseActivity需要继承AppCompatActivity

  • 2:权限是分组的,同一组的权限申请其中一个,同组的权限就全部都申请了

相关API

使用的API:

6.0的运行时权限,我们最终都是要支持的,通常我们需要使用如下的API

  • int checkSelfPermission(String permission) 用来检测应用是否已经具有权限,这个方法是在API23中才有的,为了兼容低版本,建议使用v4包中的ContextCompat.checkSelfPermission,在下面的注意事项中有解释,这里就不在赘述了

  • void requestPermissions(String[] permissions, int requestCode) 进行请求单个或多个权限,第一个参数是请求的权限集合,第二个参数是请求码,在回调监听中可以用来判断是哪个权限请求的结果

  • void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) 用户对请求作出响应后的回调,请求成功或者失败的监听

  • shouldShowRequestPermissionRationale这个API可以帮我们判断用户是否 已经拒绝过了授予该权限了。如果用户拒绝过,那么为true。此时你就可以解释为什么一定需要这么权限了,并且同时再次申请授予该权限。如果用户在再次申请该权限时,勾选了Never ask again,那么就一直为false,也就是说不会再展示该权限的任何申请了。参考鸿洋大神的解释:这个API主要用于给用户一个申请权限的解释,该方法只有在用户在上一次已经拒绝过你的这个权限申请。也就是说,用户已经拒绝一次了,你又弹个授权框,你需要给用户一个解释,为什么要授权,则使用该方法。

一个例子

注意:在AndroidManifest文件中添加需要的权限。这个步骤和我们之前的开发并没有什么变化,试图去申请一个没有声明的权限可能会导致程序崩溃。

        final String permission_call_phone = Manifest.permission.CALL_PHONE;
        final String permission_read_contacts = Manifest.permission.READ_CONTACTS;
        final String permission_camear = Manifest.permission.CAMERA;
        //版本判断
        if (Build.VERSION.SDK_INT >= 23) {
            Log.d("pepe", "Main"+"当前版本大于等于23");
            //检查是否拥有权限
            int checkCallPhonePermission = ContextCompat.checkSelfPermission(getApplicationContext(), permission_call_phone);
            int checkReadContactsPermission = ContextCompat.checkSelfPermission(getApplicationContext(), permission_read_contacts);
            int checkCamearPermission = ContextCompat.checkSelfPermission(getApplicationContext(), permission_camear);
            //如果其中有未授权的
            if (checkCallPhonePermission != PackageManager.PERMISSION_GRANTED||checkReadContactsPermission!= PackageManager.PERMISSION_GRANTED||checkCamearPermission!= PackageManager.PERMISSION_GRANTED) {
                // Should we show an explanation?
                if (ActivityCompat.shouldShowRequestPermissionRationale(MainActivity.this, permission_call_phone)) {

                    //TODO:之前申请被拒绝,需要弹出解释对话框,为什么一定需要这个权限
                    showMessageOKCancel("You need to allow access to Contacts",
                            new DialogInterface.OnClickListener() {
                                @Override
                                public void onClick(DialogInterface dialog, int which) {
                                    ActivityCompat.requestPermissions(MainActivity.this, new String[]{permission_call_phone}, 1);
                                }
                            });
                } else if(ActivityCompat.shouldShowRequestPermissionRationale(MainActivity.this, permission_read_contacts)){
                    showMessageOKCancel("You need to allow access to Contacts",
                            new DialogInterface.OnClickListener() {
                                @Override
                                public void onClick(DialogInterface dialog, int which) {
                                    ActivityCompat.requestPermissions(MainActivity.this, new String[]{permission_read_contacts}, 2);
                                }
                            });
                } else if(ActivityCompat.shouldShowRequestPermissionRationale(MainActivity.this, permission_camear)) {
                    showMessageOKCancel("You need to allow access to Contacts",
                            new DialogInterface.OnClickListener() {
                                @Override
                                public void onClick(DialogInterface dialog, int which) {
                                    ActivityCompat.requestPermissions(MainActivity.this, new String[]{permission_camear}, 3);
                                }
                            });
                }else{                 
                    //TODO:之前没有申请过,无需解释,直接申请授权
                    // No explanation needed, we can request the permission.
                    ActivityCompat.requestPermissions(MainActivity.this, new String[]{permission_call_phone,permission_read_contacts,permission_camear}, 0);
                    // MY_PERMISSIONS_REQUEST_READ_CONTACTS is an
                    // app-defined int constant. The callback method gets the
                    // result of the request.
                    return;
                }
            } else if (checkCallPhonePermission == PackageManager.PERMISSION_GRANTED) {
                //TODO:已授权,不用申请
                Log.d("pepe", "Main"+"已授权,不用申请");
//                callPhone();
            }
        } else {
            //TODO:当前版本低于23,直接使用
            Log.d("pepe", "Main"+"当前版本低于23,直接使用");
            callPhone();
        }

再来看回调:

    @Override
    public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
        super.onRequestPermissionsResult(requestCode, permissions, grantResults);
        switch (requestCode) {
            case 0: {
                // If request is cancelled, the result arrays are empty.
                if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {

                    //TODO:授权成功,做你想做的吧!
                    callPhone();

                } else {

                    //TODO:用户拒绝授权!
                    startActivity(new Intent(MainActivity.this,BActivity.class));
                    Toast.makeText(MainActivity.this,"你已拒绝授予该权限,如需更改请于设置中设置!",Toast.LENGTH_LONG).show();
                }
                return;
            }
        }
    }

封装

介绍两个库:

注意事项

API问题

由于checkSelfPermission和requestPermissions从API 23才加入,低于23版本,需要在运行时判断 或者使用Support Library v4中提供的方法

  • ContextCompat.checkSelfPermission

  • ActivityCompat.requestPermissions

  • ActivityCompat.shouldShowRequestPermissionRationale

多系统问题

当我们支持了6.0必须也要支持4.4,5.0这些系统,所以需要在很多情况下,需要有两套处理。比如Camera权限

if (isMarshmallow()) {  
    requestPermission();//然后在回调中处理  
} else {  
    useCamera();//低于6.0直接使用Camera  
}  

两个特殊权限

特殊权限,顾名思义,就是一些特别敏感的权限,在Android系统中,主要由两个

  • SYSTEM_ALERT_WINDOW,设置悬浮窗,进行一些黑科技

  • WRITE_SETTINGS 修改系统设置

关于上面两个特殊权限的授权,做法是使用startActivityForResult启动授权界面来完成。

请求SYSTEM_ALERT_WINDOW

private static final int REQUEST_CODE = 1;  
private  void requestAlertWindowPermission() {  
    Intent intent = new Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION);  
    intent.setData(Uri.parse("package:" + getPackageName()));  
    startActivityForResult(intent, REQUEST_CODE);  
}  
@Override  
protected void onActivityResult(int requestCode, int resultCode, Intent data) {  
    super.onActivityResult(requestCode, resultCode, data);  
    if (requestCode == REQUEST_CODE) {  
        if (Settings.canDrawOverlays(this)) {  
          Log.i(LOGTAG, "onActivityResult granted");  
        }  
    }  
}  

上述代码需要注意的是

  • 使用Action Settings.ACTION_MANAGE_OVERLAY_PERMISSION启动隐式Intent

  • 使用”package:” + getPackageName()携带App的包名信息

  • 使用Settings.canDrawOverlays方法判断授权结果

请求WRITE_SETTINGS

private static final int REQUEST_CODE_WRITE_SETTINGS = 2;  
private void requestWriteSettings() {  
    Intent intent = new Intent(Settings.ACTION_MANAGE_WRITE_SETTINGS);  
    intent.setData(Uri.parse("package:" + getPackageName()));  
    startActivityForResult(intent, REQUEST_CODE_WRITE_SETTINGS );  
}  
@Override  
protected void onActivityResult(int requestCode, int resultCode, Intent data) {  
    super.onActivityResult(requestCode, resultCode, data);  
    if (requestCode == REQUEST_CODE_WRITE_SETTINGS) {  
        if (Settings.System.canWrite(this)) {  
            Log.i(LOGTAG, "onActivityResult write settings granted" );  
        }  
    }  
}  

上述代码需要注意的是

  • 使用Action Settings.ACTION_MANAGE_WRITE_SETTINGS 启动隐式Intent

  • 使用”package:” + getPackageName()携带App的包名信息

  • 使用Settings.System.canWrite方法检测授权结果

注意:关于这两个特殊权限,一般不建议应用申请。

项目源码


引用:
Android 6.0 动态权限申请注意事项 - uana_777的博客 - 博客频道 - CSDN.NET
Android 6.0 运行时权限处理完全解析 - Hongyang - 博客频道 - CSDN.NET
Android_动态权限管理的解决方案
GitHub - shiraji/permissions-dispatcher-plugin:一个自动生成权限相关代码的AS插件
谈谈Android 6.0运行时权限理解 – 码农网

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值