Android 11 运行时权限

Posted

技术标签:

【中文标题】Android 11 运行时权限【英文标题】:Android 11 Runtime Permissions 【发布时间】:2021-10-05 07:45:29 【问题描述】:

几天前,代码运行良好,没有权限问题。

我无法在运行时授予权限,在从设置授予权限时也遇到问题。 (应用权限详情页面)。

var permissions = if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.Q) 
        arrayOf(Manifest.permission.BLUETOOTH, Manifest.permission.BLUETOOTH_ADMIN, Manifest.permission.ACCESS_FINE_LOCATION, Manifest.permission.ACCESS_COARSE_LOCATION, Manifest.permission.ACCESS_BACKGROUND_LOCATION)
     else 
        arrayOf(Manifest.permission.BLUETOOTH, Manifest.permission.BLUETOOTH_ADMIN, Manifest.permission.ACCESS_FINE_LOCATION, Manifest.permission.ACCESS_COARSE_LOCATION)
    

在相应的按钮单击时请求权限。

permissionResultLauncher =
        registerForActivityResult(
                ActivityResultContracts.RequestMultiplePermissions()
        )  permissions ->
            var allPermissionGranted = true
            permissions.entries.forEach 
                if (!it.value) 
                    Log.e(TAG, "Permission $it.key granted : $it.value")
                    allPermissionGranted = false
                
            
            if (!permissionDeniedDialog && !allPermissionGranted) 
                showDialog(
                        "Required",
                        "App needs Bluetooth and Location permissions to scan bluetooth devices.",
                        "Later",
                        "Allow",
                        object : DialogView.ButtonListener 
                            override fun onNegativeButtonClick(dialog: AlertDialog) 
                                dialog.dismiss()
                                val intent = Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS,
                                        Uri.fromParts("package", requireActivity().applicationContext.packageName, null))
                                intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
                                startActivity(intent)
                            

                            override fun onPositiveButtonClick(dialog: AlertDialog) 
                                dialog.dismiss()
                                requestRequirdPermissions()
                            
                        )
             else 
                val intent = Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS,
                        Uri.fromParts("package", requireActivity().applicationContext.packageName, null))
                intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
                startActivity(intent)
            
            permissionDeniedDialog = true
        
permissionResultLauncher.launch(permissions)

权限检查功能

private fun checkPermission(): Boolean 
        for (permission in permissions) 
            if (ContextCompat.checkSelfPermission(requireContext(), permission) != PackageManager.PERMISSION_GRANTED) return false
        
        return true
    

有人可以告诉我为什么上述方法不起作用吗?系统直接在结果上,应用程序正在将应用程序重定向到设置页面,但我也无法从设置页面授予权限。

这是特定于操作系统的吗?有人遇到与 ColorOS 11 相同的问题吗? 如果我身边缺少任何东西,请指导我。

设备:OPPO F17 Pro 操作系统:Color OS 11,基于 Android 11

注意:

以上代码,使用三星设备,基于 Android 11(OneUI 3.1),应用程序不要求运行时,但在设置页面上重定向后,我授予位置权限并且应用程序工作正常,关于 OPPO 我无法授予权限从设置页面。

尝试了什么:

  var permissions = if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.Q) 
    arrayOf(Manifest.permission.BLUETOOTH, Manifest.permission.BLUETOOTH_ADMIN, Manifest.permission.ACCESS_BACKGROUND_LOCATION)
 else 
    arrayOf(Manifest.permission.BLUETOOTH, Manifest.permission.BLUETOOTH_ADMIN, Manifest.permission.ACCESS_COARSE_LOCATION)



private fun requestRequirdPermissions() 
        if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.Q) 
            permissionResultLauncher.launch(arrayOf(Manifest.permission.ACCESS_BACKGROUND_LOCATION))
        
        else
        
            permissionResultLauncher.launch(arrayOf(Manifest.permission.ACCESS_COARSE_LOCATION ))
        
    

【问题讨论】:

“几天前,代码运行良好,没有权限问题”。那么从那以后你有什么改变呢? 什么都没变,和以前一样,这就是我来这里的原因,因为我从2小时就开始检查了。 您不能一次请求所有不同的位置权限。阅读文档。 谢谢,但不确定,我已经尝试使用名为 ACCESS_BACKGROUND_LOCATION 的单一权限仍然无法正常工作。 @Ashvinsolanki 您必须逐步请求位置许可:请求前景 (ACCESS_FINE_LOCATION) 然后在获得许可后您可以请求背景 (ACCESS_BACKGROUND_LOCATION)。如果您在没有先授予前景的情况下要求背景或同时要求两者,则请求会立即被拒绝,甚至不会显示对话框。见***.com/questions/66321232/… 【参考方案1】:

试试这个方法可能对你有帮助

fun checkPermission() 
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) 
            var permissions: ArrayList<String> = ArrayList<String>()
            permissions.add(Manifest.permission.ACCESS_COARSE_LOCATION)
            val listPermissionsNeeded: ArrayList<String> = ArrayList()
            for (p in permissions) 
                var result = ContextCompat.checkSelfPermission(this, p!!)
                if (result != PackageManager.PERMISSION_GRANTED) 
                    listPermissionsNeeded.add(p)
                
            

            if (!listPermissionsNeeded.isEmpty()) 
                ActivityCompat.requestPermissions(
                        this,
                        listPermissionsNeeded.toTypedArray(),
                        CODE
                )
             else 
                next()
            
         else 
            next()
        
    

    override fun onRequestPermissionsResult(
            requestc: Int,
            permissions: Array<String>,
            grantRes: IntArray
    ) 
        super.onRequestPermissionsResult(requestc, permissions, grantRes);
        when (requestc) 
            CODE -> 
                var isGrant = true
                if (grantRes.size > 0) 
                    for (i in 0 until grantResults.size) 
                        if (grantResults[i] == PackageManager.PERMISSION_DENIED) 
                            isGrant = false
                        
                    
                
                if (isGrant) 
                    next()
                 else 
                    checkRationalePermission(permissions)
                
            
        
    


    var alertDialogRatinal: android.app.AlertDialog? = null
    fun checkRationale(permissions: Array<String>) 
        if (alertDialogRatinal != null && alertDialogRatinal!!.isShowing || permissions.size == 0) 
            return
        
        var someDenied = false
        for (permission in permissions) 
            if (ActivityCompat.shouldShowRequestPermissionRationale(
                    this@Activity,
            permission!!
                )
            ) 

             else 
                if (ActivityCompat.checkSelfPermission(
                        this@Activity,
                permission
                    ) == PackageManager.PERMISSION_GRANTED
                ) 
                 else 
                    someDenied = true
                
            
        
        if (someDenied) 
            val alertDialogBuilder =
                    android.app.AlertDialog.Builder(this@Activity)
            alertDialogRatinal = alertDialogBuilder.setTitle("Permissions Required")
                    .setMessage(
                            "Please open settings, go to permissions and allow them."
                    )
                    .setPositiveButton(
                            "Settings"
                    )  dialog, which ->
                    val intent = Intent(
                    Settings.ACTION_APPLICATION_DETAILS_SETTINGS,
                    Uri.fromParts(
                            "package",
                            this@Activity.getPackageName(),
                null
                        )
                    )
                intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
                startActivityForResult(intent, 100)
            
                .setNegativeButton(
                    "Cancel"
            )  dialog, which -> 
                .setCancelable(false)
                    .create()
            alertDialogRatinal!!.show()
        
    

【讨论】:

我建议你使用新的 API "ActivityResultContracts"【参考方案2】:

我的问题已通过系统重置得到解决,但根据 cmets 和文档,请按照以下指南获取位置权限。

感谢Pawelcmets,

参考:https://***.com/a/66321942/9909365

后台位置权限不像其他权限那样工作。这是一个将位置权限从仅前台提升到前台和后台的请求。

用户必须有意识地选择“始终允许”才能执行此操作并授予后台位置权限。否则,该权限将被视为被拒绝。

除非已授予前台位置,否则您甚至无法请求后台位置 - 当系统权限活动显示时,它应该已经选择了选项 2 或 3。

见https://developer.android.com/training/location/permissions#request-background-location

注意:

当您的应用使用此后台位置时,请准备一段简短的视频,演示您的应用中基于位置的功能,该功能需要在后台访问位置(当应用未使用时)。

见https://support.google.com/googleplay/android-developer/answer/9799150

【讨论】:

【参考方案3】:
private val requestIdMultiplePermissions = 1
private val permissionsRequest: ArrayList<String> =
    arrayListOf(READ_CALENDAR
            , CAMERA)
findViewById<Button>(R.id.multiplePermissionBtn).setOnClickListener 
            if (checkMultipleRequestPermissions()) 
                doOperation()
            

 private fun doOperation() 
        Toast.makeText(this, "Successfully granted", Toast.LENGTH_LONG).show()
    
private fun checkMultipleRequestPermissions(): Boolean 
        val listPermissionsNeeded: MutableList<String> = ArrayList()

        for (p in permissionsRequest) 
            val result = ContextCompat.checkSelfPermission(this, p)
            if (result != PackageManager.PERMISSION_GRANTED) 
                listPermissionsNeeded.add(p)
            
        

        if (listPermissionsNeeded.isNotEmpty()) 
            ActivityCompat.requestPermissions(
                this,
                listPermissionsNeeded.toTypedArray(),
                requestIdMultiplePermissions
            )
            return false
        
        return true
    
override fun onRequestPermissionsResult(
        requestCode: Int,
        permissions: Array<out String>,
        grantResults: IntArray
    ) 
        super.onRequestPermissionsResult(requestCode, permissions, grantResults)
        if (requestCode == requestIdMultiplePermissions) 
            if (grantResults.isNotEmpty()) 
                var isGrant = true
                for (element in grantResults) 
                    if (element == PackageManager.PERMISSION_DENIED) 
                        isGrant = false
                    
                
                if (isGrant) 
                    doOperation()
                 else 
                    var someDenied = false
                    for (permission in permissions) 
                        if (!ActivityCompat.shouldShowRequestPermissionRationale(
                                this,
                                permission
                            )
                        )  
                            if (ActivityCompat.checkSelfPermission(
                                    this,
                                    permission
                                ) == PackageManager.PERMISSION_DENIED
                            ) 
                                someDenied = true
                            
                        
                    
                    if (someDenied) 
                        settingActivityOpen()
                    else
                        showDialogOK  _: DialogInterface?, which: Int ->
                            when (which) 
                                DialogInterface.BUTTON_POSITIVE -> checkMultipleRequestPermissions()
                                DialogInterface.BUTTON_NEGATIVE ->  
                            
                        
                    
                
            
        

 private fun settingActivityOpen() 
        Toast.makeText(
            this,
            "Go to settings and enable permissions",
            Toast.LENGTH_LONG
        )
            .show()
        val i = Intent()
        i.action = Settings.ACTION_APPLICATION_DETAILS_SETTINGS
        i.addCategory(Intent.CATEGORY_DEFAULT)
        i.data = Uri.parse("package:$packageName")
        i.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
        i.addFlags(Intent.FLAG_ACTIVITY_NO_HISTORY)
        i.addFlags(Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS)
        startActivity(i)

private fun showDialogOK(okListener: DialogInterface.OnClickListener) 
        MaterialAlertDialogBuilder(this)
            .setMessage("All Permission required for this app")
            .setPositiveButton("OK", okListener)
            .setNegativeButton("Cancel", okListener)
            .create()
            .show()

【讨论】:

以上是关于Android 11 运行时权限的主要内容,如果未能解决你的问题,请参考以下文章

运行BTDemo时如何避免Android始终获得蓝牙权限

已授予 Android 运行时权限,但仍被拒绝

可以在 Android Marshmallow (API 23) 的运行时权限模型中同步请求权限吗?

Android 运行时权限流程:我可以使用 ActivityCompat.requestPermissions 发送额外内容吗?

Android NotificationChannel 在运行时询问多个频道的权限?

我们如何区分 Android M 的运行时权限中的从不询问和停止询问?