Android 12/13权限请求失效(compileSdkVersion升级至33后)
Android compileSdkVersion升级到33后权限请求弹窗不显示问题解决
问题描述
将Android项目的compileSdkVersion从31升级至33后,触发READ_EXTERNAL_STORAGE、WRITE_EXTERNAL_STORAGE或CAMERA权限请求时,权限弹窗无法弹出;回退到compileSdkVersion 31则恢复正常。相关原代码及配置如下:
原权限处理类代码
import android.Manifest import android.app.Activity import android.content.pm.PackageInfo import android.content.pm.PackageManager import androidx.core.app.ActivityCompat import androidx.core.content.ContextCompat import android.os.Build class Permissions(private val activity: Activity, private val permissionList: List<String>, val code: Int) { fun checkPermissions() { if (permissionsGranted() != PackageManager.PERMISSION_GRANTED) { requestPermissions() } } private fun permissionsGranted(): Int { var counter = 0 for (permission in permissionList) { counter += ContextCompat.checkSelfPermission(activity, permission) } return counter } private fun deniedPermission(): String { for (permission in permissionList) { if (ContextCompat.checkSelfPermission(activity, permission) == PackageManager.PERMISSION_DENIED) return permission } return "" } private fun requestPermissions() { val permission = deniedPermission() if (ActivityCompat.shouldShowRequestPermissionRationale(activity, permission)) { } else { ActivityCompat.requestPermissions(activity, permissionList.toTypedArray(), code) } } fun checkPermission(manifest: String): Boolean { val result = ContextCompat.checkSelfPermission(activity, manifest) return result == PackageManager.PERMISSION_GRANTED } fun checkPermissionActivity(state: Boolean, urlPath: String, kind : String){ val showRationale = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { this.activity.shouldShowRequestPermissionRationale(Manifest.permission.READ_EXTERNAL_STORAGE) && this.activity.shouldShowRequestPermissionRationale(Manifest.permission.WRITE_EXTERNAL_STORAGE) } else { true } if (!showRationale) { return } else { return } } fun getAllPermissions(): List<String> { val granted = ArrayList<String>() val pi = activity.packageManager.getPackageInfo(activity.packageName, PackageManager.GET_PERMISSIONS) for (i in pi.requestedPermissions.indices) { if (pi.requestedPermissionsFlags[i] and PackageInfo.REQUESTED_PERMISSION_GRANTED != 0) { granted.add(pi.requestedPermissions[i]) } } return granted } }
原Manifest配置
<?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.co.retrofit.app"> <uses-permission android:name="android.permission.POST_NOTIFICATIONS" /> <uses-permission android:name="android.permission.INTERNET" /> <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" /> <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" /> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /> <uses-permission android:name="android.permission.CAMERA" /> <application android:name=".feature.RetrofitApplication" android:allowBackup="true" android:icon="@mipmap/ic_launcher" android:label="@string/app_name" android:networkSecurityConfig="@xml/network_security_config" android:roundIcon="@mipmap/ic_launcher_round" android:supportsRtl="true" android:theme="@style/AppTheme" android:usesCleartextTraffic="true"> <activity android:exported="true" android:name=".feature.view.activities.Maintenance" /> <activity android:name=".feature.view.activities.SplashActivity" android:exported="true"> <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity> <activity android:name=".feature.view.activities.MainActivity" android:exported="true"/> </application> </manifest>
原调用逻辑
@Suppress("UNUSED_PARAMETER") private fun addAlbum(view: View){ val permissions = Permissions(this, arrayListOf( Manifest.permission.READ_EXTERNAL_STORAGE, Manifest.permission.WRITE_EXTERNAL_STORAGE), 23) permissions.checkPermissions() }
或
@Suppress("UNUSED_PARAMETER") private fun addAlbum(view: View){ val permissions = Permissions(this, arrayListOf( Manifest.permission.CAMERA), 23) permissions.checkPermissions() }
问题根源分析
- 存储权限适配缺失:Android 13(API 33)废弃了
READ_EXTERNAL_STORAGE和WRITE_EXTERNAL_STORAGE,改用READ_MEDIA_IMAGES、READ_MEDIA_VIDEO等细分媒体权限。继续请求旧权限时,系统会自动判定为已授予,不会弹出权限弹窗。 - 权限检查逻辑错误:原
permissionsGranted()方法通过累加权限状态值判断结果,PERMISSION_GRANTED为0,PERMISSION_DENIED为-1,多个权限累加后结果永远不等于单个PERMISSION_GRANTED,导致错误跳过权限请求。 - 请求逻辑存在漏洞:
deniedPermission()仅返回第一个被拒绝的权限,若用户之前对该权限勾选了"不再询问",shouldShowRequestPermissionRationale会返回false,且原逻辑为空实现,直接跳过请求流程。
修复方案
1. 更新Manifest权限配置
针对Android版本适配存储权限,保留旧版本兼容:
<?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.co.retrofit.app"> <uses-permission android:name="android.permission.POST_NOTIFICATIONS" /> <uses-permission android:name="android.permission.INTERNET" /> <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" /> <!-- Android 13+ 使用媒体权限 --> <uses-permission android:name="android.permission.READ_MEDIA_IMAGES" android:minSdkVersion="33"/> <!-- Android 12及以下保留旧存储权限 --> <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" android:maxSdkVersion="32"/> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" android:maxSdkVersion="32"/> <uses-permission android:name="android.permission.CAMERA" /> <application android:name=".feature.RetrofitApplication" android:allowBackup="true" android:icon="@mipmap/ic_launcher" android:label="@string/app_name" android:networkSecurityConfig="@xml/network_security_config" android:roundIcon="@mipmap/ic_launcher_round" android:supportsRtl="true" android:theme="@style/AppTheme" android:usesCleartextTraffic="true"> <activity android:exported="true" android:name=".feature.view.activities.Maintenance" /> <activity android:name=".feature.view.activities.SplashActivity" android:exported="true"> <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity> <activity android:name=".feature.view.activities.MainActivity" android:exported="true"/> </application> </manifest>
2. 修正权限处理类逻辑
修复权限检查和请求逻辑,适配Android 13权限变更:
import android.Manifest import android.app.Activity import android.content.pm.PackageInfo import android.content.pm.PackageManager import androidx.core.app.ActivityCompat import androidx.core.content.ContextCompat import android.os.Build class Permissions(private val activity: Activity, private val permissionList: List<String>, val code: Int) { fun checkPermissions() { if (!allPermissionsGranted()) { requestPermissions() } } // 正确判断所有权限是否都已授予 private fun allPermissionsGranted(): Boolean { return permissionList.all { ContextCompat.checkSelfPermission(activity, it) == PackageManager.PERMISSION_GRANTED } } // 获取所有未授予的权限 private fun getDeniedPermissions(): List<String> { return permissionList.filter { ContextCompat.checkSelfPermission(activity, it) == PackageManager.PERMISSION_DENIED } } private fun requestPermissions() { val deniedPermissions = getDeniedPermissions() if (deniedPermissions.isNotEmpty()) { ActivityCompat.requestPermissions(activity, deniedPermissions.toTypedArray(), code) } } fun checkPermission(manifest: String): Boolean { return ContextCompat.checkSelfPermission(activity, manifest) == PackageManager.PERMISSION_GRANTED } // 优化存储权限的 rationale 判断(可选用于引导用户) fun checkStoragePermissionRationale(): Boolean { return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { val targetPermission = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { Manifest.permission.READ_MEDIA_IMAGES } else { Manifest.permission.READ_EXTERNAL_STORAGE } activity.shouldShowRequestPermissionRationale(targetPermission) } else { true } } fun getAllPermissions(): List<String> { val granted = ArrayList<String>() val pi = activity.packageManager.getPackageInfo(activity.packageName, PackageManager.GET_PERMISSIONS) for (i in pi.requestedPermissions.indices) { if (pi.requestedPermissionsFlags[i] and PackageInfo.REQUESTED_PERMISSION_GRANTED != 0) { granted.add(pi.requestedPermissions[i]) } } return granted } }
3. 适配调用逻辑
根据系统版本动态请求对应权限:
@Suppress("UNUSED_PARAMETER") private fun addAlbum(view: View){ val permissionList = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { // Android 13+ 请求媒体图片权限 arrayListOf(Manifest.permission.READ_MEDIA_IMAGES) } else { // Android 12及以下请求旧存储权限 arrayListOf(Manifest.permission.READ_EXTERNAL_STORAGE, Manifest.permission.WRITE_EXTERNAL_STORAGE) } val permissions = Permissions(this, permissionList, 23) permissions.checkPermissions() } // 相机权限调用无需修改 @Suppress("UNUSED_PARAMETER") private fun openCamera(view: View){ val permissions = Permissions(this, arrayListOf(Manifest.permission.CAMERA), 24) permissions.checkPermissions() }
额外说明
- 相机权限在Android 13中无需变更,仍使用
Manifest.permission.CAMERA。 - 若需处理用户拒绝权限并勾选"不再询问"的场景,可在
Activity的onRequestPermissionsResult方法中判断shouldShowRequestPermissionRationale为false时,引导用户跳转到应用设置页面开启权限。
内容的提问来源于stack exchange,提问作者Alejandro Gonzalez
相关产品推荐
相关产品推荐

