You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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()
}

问题根源分析

  1. 存储权限适配缺失:Android 13(API 33)废弃了READ_EXTERNAL_STORAGE和WRITE_EXTERNAL_STORAGE,改用READ_MEDIA_IMAGES、READ_MEDIA_VIDEO等细分媒体权限。继续请求旧权限时,系统会自动判定为已授予,不会弹出权限弹窗。
  2. 权限检查逻辑错误:原permissionsGranted()方法通过累加权限状态值判断结果,PERMISSION_GRANTED为0,PERMISSION_DENIED为-1,多个权限累加后结果永远不等于单个PERMISSION_GRANTED,导致错误跳过权限请求。
  3. 请求逻辑存在漏洞: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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.10 19:01:17