Android 运行权限那点事

对于6.0以下的权限及在安装的时候,根据权限声明产生一个权限列表,用户只有在同意之后才能完成app的安装,造成了我们想要使用某个app,就要默默忍受其一些不必要的权限(比如是个app都要访问通讯录、短信等)。而在6.0以后,我们可以直接安装,当app需要我们授予不恰当的权限的时候,我们可以予以拒绝(比如:单机的象棋对战,请求访问任何权限,我都是不同意的)。当然你也可以在设置界面对每个app的权限进行查看,以及对单个权限进行授权或者解除授权。
新的权限机制更好的保护了用户的隐私,Google将权限分为两类,一类是Normal Permissions,这类权限一般不涉及用户隐私,是不需要用户进行授权的,比如手机震动、访问网络等;另一类是Dangerous Permission,一般是涉及到用户隐私的,需要用户进行授权,比如读取sdcard、访问通讯录等。

  • Normal Permissions如下:

    ACCESS_LOCATION_EXTRA_COMMANDS
    ACCESS_NETWORK_STATE
    ACCESS_NOTIFICATION_POLICY
    ACCESS_WIFI_STATE
    BLUETOOTH
    BLUETOOTH_ADMIN
    BROADCAST_STICKY
    CHANGE_NETWORK_STATE
    CHANGE_WIFI_MULTICAST_STATE
    CHANGE_WIFI_STATE
    DISABLE_KEYGUARD
    EXPAND_STATUS_BAR
    GET_PACKAGE_SIZE
    INSTALL_SHORTCUT
    INTERNET
    KILL_BACKGROUND_PROCESSES
    MODIFY_AUDIO_SETTINGS
    NFC
    READ_SYNC_SETTINGS
    READ_SYNC_STATS
    RECEIVE_BOOT_COMPLETED
    REORDER_TASKS
    REQUEST_INSTALL_PACKAGES
    SET_ALARM
    SET_TIME_ZONE
    SET_WALLPAPER
    SET_WALLPAPER_HINTS
    TRANSMIT_IR
    UNINSTALL_SHORTCUT
    USE_FINGERPRINT
    VIBRATE
    WAKE_LOCK
    WRITE_SYNC_SETTINGS
    
  • 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
    

看到上面的dangerous permissions,会发现一个问题,好像危险权限都是一组一组的,恩,没错,的确是这样的,
那么有个问题:分组对我们的权限机制有什么影响吗?
的确是有影响的,如果app运行在Android 6.x的机器上,对于授权机制是这样的。如果你申请某个危险的权限,假设你的app早已被用户授权了同一组的某个危险权限,那么系统会立即授权,而不需要用户去点击授权。比如你的app对READ_CONTACTS已经授权了,当你的app申请WRITE_CONTACTS时,系统会直接授权通过。此外,对于申请时弹出的dialog上面的文本说明也是对整个权限组的说明,而不是单个权限(ps:这个dialog是不能进行定制的)。

不过需要注意的是,不要对权限组过多的依赖,尽可能对每个危险权限都进行正常流程的申请,因为在后期的版本中这个权限组可能会产生变化,就目前8.0,就已经做了这样的变化了。

在 Android 8.0 之前,如果应用在运行时请求权限并且被授予该权限,系统会错误地将属于同一权限组并且在清单中注册的其他权限也一起授予应用。

对于针对 Android 8.0 的应用,此行为已被纠正。系统只会授予应用明确请求的权限。然而,一旦用户为应用授予某个权限,则所有后续对该权限组中权限的请求都将被自动批准。

例如,假设某个应用在其清单中列出 READ_EXTERNAL_STORAGE 和 WRITE_EXTERNAL_STORAGE。应用请求 READ_EXTERNAL_STORAGE,并且用户授予了该权限。如果该应用针对的是 API 级别 24 或更低级别,系统还会同时授予 WRITE_EXTERNAL_STORAGE,因为该权限也属于同一 STORAGE 权限组并且也在清单中注册过。如果该应用针对的是 Android 8.0,则系统此时仅会授予 READ_EXTERNAL_STORAGE;不过,如果该应用后来又请求 WRITE_EXTERNAL_STORAGE,则系统会立即授予该权限,而不会提示用户。
所以不要对权限组过多的依赖,下面是我最近封装了一个权限申请:
1、检查权限是否已被授予;

     checkSelfPermission(String permission)

2、没有则申请权限;

    requestPermissions(@NonNull String[] permissions, int requestCode)

3、处理申请权限结果,成功和失败;

onRequestPermissionsResult(int requestCode, @NonNull String[] permissions,
       @NonNull int[] grantResults)

基本上就完事了,下面就是一些部分代码。

open class BasePermissionsActivity : RxAppCompatActivity(), PermissionsTransfer {
private val REQUEST_PERMISSIONS = 0x20
private var mPermissionPendingRunnable: Runnable? = null

/**
 * 请求权限:
 *
 */
override fun requestPermissions(permissions: Array<String>, pendingRunnable: Runnable?) {
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
        if (checkPermissions(permissions)) {
            executePendingRunnable(pendingRunnable)
            return
        }
        //Remind the pendingRunnable
        mPermissionPendingRunnable = pendingRunnable
        return
    }
    //否则直接执行
    executePendingRunnable(pendingRunnable)
}

override fun onDestroy() {
    super.onDestroy()
    mPermissionPendingRunnable = null
}

override fun requestPermissions(permissions: Array<String>, pendingRunnable: () -> Unit) {
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
        if (checkPermissions(permissions)) {
            pendingRunnable()
        }
        return
    }
    //否则直接执行
    pendingRunnable()
}

private fun executePendingRunnable(action: Runnable?) = action?.run()


@TargetApi(Build.VERSION_CODES.M)
private fun checkPermissions(permissions: Array<String>): Boolean {
    val needRequestPermissions = ArrayList<String>(1)

    permissions.forEach {
        if (checkSelfPermission(it) != PackageManager.PERMISSION_GRANTED) {
            needRequestPermissions.add(it)
        }
    }

    if (needRequestPermissions.size > 0) {
        val requestPermissions = arrayOfNulls<String>(needRequestPermissions.size)
        needRequestPermissions.toArray(requestPermissions)
        requestPermissions(requestPermissions, REQUEST_PERMISSIONS)
        return false
    }
    // 如果已经具有本次所需的权限,则返回True
    return true
}


override fun onRequestPermissionsResult(requestCode: Int, permissions: Array<out String>, grantResults: IntArray) {
    super.onRequestPermissionsResult(requestCode, permissions, grantResults)
    when (requestCode) {
        REQUEST_PERMISSIONS -> {
            handlePermissionsResult(permissions as Array<String>, grantResults)
        }
        else -> {
        }
    }
}

/**
 * 处理权限请求结果
 */
private fun handlePermissionsResult(permissions: Array<String>, grantResults: IntArray) {
    val requestPermissionLength = permissions.size
    val requestFailurePermissionsList = ArrayList<String>(1)
    for (index in 0 until requestPermissionLength) {
        val permission = permissions[index]
        val isPermissionGranted = grantResults[index] == PackageManager.PERMISSION_GRANTED
        KLogUtil.e("RequestPermission:$permission granted:$isPermissionGranted")
        if (!isPermissionGranted) {
            requestFailurePermissionsList.add(permission)
        }
    }
    if (requestFailurePermissionsList.size > 0) {
        val requestFailurePermissions = arrayOfNulls<String>(requestFailurePermissionsList.size)
        requestFailurePermissionsList.toArray(requestFailurePermissions)
        handleRequestPermissionsFailure(requestFailurePermissions)
    } else {
        handleRequestPermissionSuccess(permissions)
    }
}

private fun handleRequestPermissionSuccess(permissions: Array<String>) {
    onRequestPermissionsSuccess(permissions)
    executePendingRunnable(mPermissionPendingRunnable)
    mPermissionPendingRunnable = null
}

private fun handleRequestPermissionsFailure(requestFailurePermissions: Array<String?>) {
    onRequestPermissionsFailure(requestFailurePermissions)
    mPermissionPendingRunnable = null
    Build.VERSION_CODES.JELLY_BEAN
}

override fun onRequestPermissionsFailure(requestFailurePermissions: Array<String?>) {
}

override fun onRequestPermissionsSuccess(permissions: Array<String>) {
}
}

最近项目紧,好久没有更新了,把最近在项目中权限给总结一下,以前总是使用第三方库,不如直接自己封装,后面用起来方便一些。

    原文作者:wdroid
    原文地址: https://www.jianshu.com/p/4ec91e137244
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞