美文网首页Android技术知识Android知识
Android 6.0&运行时权限 笔记

Android 6.0&运行时权限 笔记

作者: chauI | 来源:发表于2017-06-01 17:16 被阅读302次
    • 原生 API 的使用
    • 实践中问题

    首先介绍一下,Android 6.0 的权限机制的变化大家都知道,从原来安装时一次性全部授权,变为在运行时才向用户申请授权。

    当然在 6.0 也不是全部的权限都需要运行时授权,现在的权限分为两类:

    • Normal Permissions,一般不涉及用户隐私,比如手机震动、访问网络等。

    • Dangerous Permission,涉及到用户隐私的,比如访问通讯录等,这一类的权限就需要运行时授权。
      还有比较特别的一点是,这些危险权限是分组的,比如第一组的权限和联系人有关,当我们获取到用户授予的permission:android.permission.WRITE_CONTACTS 权限时,同组下的权限就默认一起获得,不需要再次申请。
      下面是危险权限一览,不在这里的权限的申请和 6.0 以前是一样的。

    //联系人
    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
    

    然后是最后的效果图:


    Gif 演示

    因为系统原生的 API 中,如果用户选择了「不再提示」则不会再弹出申请权限的对话框。
    但是假设我的 APP 依靠蓝牙向设备发送信息,但用户未授予蓝牙权限,则这个 APP 就无作为了,所以要捕获到这种情况并且弹出对话框。

    还有一点比较重要的,下面一部分代码在魅族、小米等系统上是无效的,猜测是因为它们有自己的权限机制,关于这些情况的处理后面再说。

    原生 API 的实践

    参考官网中文教程:在运行时请求权限

    • 检查权限
    if(ContextCompat.checkSelfPermission(this,
             Manifest.permission.ACCESS_COARSE_LOCATION)
            != PackageManager.PERMISSION_GRANTED) {
    
    }
    

    checkSelfPermission() 方法传入 context 和需要申请的权限,返回值:

    public static final int PERMISSION_GRANTED = 0;
    public static final int PERMISSION_DENIED = -1;
    

    当为 PERMISSION_GRANTED 时则已经获取到权限。

    • 申请权限
    ActivityCompat.requestPermissions(this,
                   arrayOf(Manifest.permission.ACCESS_COARSE_LOCATION), 10)
    

    requestPermissions() 方法传入 context,申请的权限的字符串数组,回调返回的参数。
    所以该方法可以一次申请多个权限,并且申请结果会回调到 Activity 的 onRequestPermissionsResult() 方法中。

    @Override
    public void onRequestPermissionsResult(int requestCode,
            String permissions[], int[] grantResults) {
        //requstCode 是 requestPermissions 传入的第三个参数
        //permissions[] 是申请的权限的数组
        //grantResults[] 是申请的结果
    }
    
    • 特殊的方法:
    ActivityCompat.shouldShowRequestPermissionRationale(this,
                        Manifest.permission.ACCESS_COARSE_LOCATION))
    

    方法传入 context 和 权限名,如果用户在申请权限对话框中拒绝了申请,则该方法返回 true。
    也就是当用户拒绝了一次申请后,APP 再次申请的时候应该解释为什么需要申请该权限。
    还有就是如果用户选择了「不再提示」,该方法返回 false。

    • 完整代码

    这里用上了 Kotlin 和 Anko,如果看不懂就该补习了。

    fun getPermission(){
      if (ContextCompat.checkSelfPermission(this,Manifest.permission.ACCESS_COARSE_LOCATION)
              != PackageManager.PERMISSION_GRANTED) {
      
          if (ActivityCompat.shouldShowRequestPermissionRationale(this,
                  Manifest.permission.ACCESS_COARSE_LOCATION)){
    
              //此时用户已经拒绝了一次申请,所以弹出对话框解释申请的原因
              alert("申请开启定位权限,如果不打开定位权限,将不能运行该 APP","申请权限"){
                 positiveButton("同意",{
                     ActivityCompat.requestPermissions(act,
                             arrayOf(Manifest.permission.ACCESS_COARSE_LOCATION), 10)
                 })
              }.show()
    
          }else {
              ActivityCompat.requestPermissions(this,
                      arrayOf(Manifest.permission.ACCESS_COARSE_LOCATION), 10)
          }
      }else{
          toast("已拥有权限")
      }
    }
    
    //申请方法的回调
    override fun onRequestPermissionsResult(requestCode: Int, permissions: Array<out String>, grantResults: IntArray) {
        super.onRequestPermissionsResult(requestCode, permissions, grantResults)
        if (requestCode == 10){
            //requestCode 为 10 时,才是对于申请方法的回调。
            if (grantResults[0] == PackageManager.PERMISSION_GRANTED){
                toast("申请权限成功")
            }else{
                if (!ActivityCompat.shouldShowRequestPermissionRationale(this,
                        Manifest.permission.ACCESS_COARSE_LOCATION)) {
                 
                    //在回调中如果发现用户拒绝了申请,则再一次弹出对话框
                    //即使用户选择了「不再提示」这个对话框也会弹出
                    alert("如果不打开定位权限,将不能使用蓝牙,是否跳转到应用详情页面", "申请权限") {
                        positiveButton("确定", {
                            //将会跳转到应用的详情页面
                            val localIntent = Intent()
                            localIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
                            localIntent.action = Settings.ACTION_APPLICATION_DETAILS_SETTINGS
                            localIntent.data = Uri.fromParts("package", packageName, null)
                            startActivity(localIntent)
                        })
                        negativeButton("取消", { dialog -> dialog.dismiss() })
                    }.show()
                }
            }
        }
    }
    

    遇到的问题

    就如上面说的,我在魅族和小米的手机上都试了一下,这里的代码也不能说完全无用,但是会出很多问题。

    比如说在魅族的手机上(魅蓝 note 5,Flyme 6.0.2.1A),无论授权与否,checkSelfPermission() 方法都只会返回 0,也就是已授权的返回值 。

    这种情况下,比如我需要打开蓝牙,那就直接执行打开蓝牙的方法,假如报错、或者隔了一段时间后还是没有打开,就弹出对话框提示用户手动打开。

    if (mBluetoothAdapter == null){
       val bluetoothManager = getSystemService(Context.BLUETOOTH_SERVICE) as BluetoothManager
       val bluetoothAdapter = bluetoothManager.adapter
    }
    var isEnable = bluetoothAdapter?.isEnabled ?: false
    if(!isEnable){
      //尝试申请权限
      bluetoothAdapter?.enable()
    }
    ...
    //延时五秒后再一次判断
    if (mBluetoothAdapter?.isEnabled == false){
      //跳转到应用详情页面
       val localIntent = Intent()
       localIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
       localIntent.action = Settings.ACTION_APPLICATION_DETAILS_SETTINGS
       localIntent.data = Uri.fromParts("package", packageName, null)
       startActivity(localIntent)
    }
    

    这应该是最笨的方法了,如果有更好的方法还请告知。

    相关文章

      网友评论

        本文标题:Android 6.0&运行时权限 笔记

        本文链接:https://www.haomeiwen.com/subject/tcaxfxtx.html