Android 13 Tiramisu 权限请求循环触发异常问题排查修复
问题产生原因
- Android 13(API 级别33)对存储权限模型做了强制调整:代码中调用的
READ_EXTERNAL_STORAGE、WRITE_EXTERNAL_STORAGE两个权限在API33及以上版本已被正式废弃,系统不会针对这两个权限弹出授权申请弹窗,调用ActivityCompat.requestPermissions申请这两个权限时,系统会直接默认返回拒绝结果,不存在用户交互等待过程。 - 权限申请逻辑放在了
onResume()生命周期中,而权限申请的系统弹窗本身是独立Activity,不管是弹窗弹出还是关闭(包括系统直接拒绝不弹窗的场景),都会重新触发当前页面的onResume回调。在Android13上申请旧存储权限时,系统瞬间返回拒绝结果回到页面,onResume立刻再次执行,再次调用权限申请方法,就形成了无弹窗的无限循环,这也是日志里反复打印方法入口日志的核心原因。 - 代码本身存在一处笔误:遍历权限时把未授权的权限存入了名为
list的ArrayList,但是发起申请时传入的是未定义的permissionsNeeded变量,这段代码正常情况下无法通过编译,属于贴代码时的书写错误。
修复方案
- 做存储权限的版本适配,针对不同系统版本申请对应合法权限:
- 系统版本低于API33时,正常申请
READ_EXTERNAL_STORAGE、WRITE_EXTERNAL_STORAGE旧存储权限 - 系统版本大于等于API33时,完全不要申请旧存储权限,根据自身业务需求申请
READ_MEDIA_IMAGES、READ_MEDIA_VIDEO、READ_MEDIA_AUDIO三个细分媒体读取权限;如果需要访问公共目录下的非媒体文件,直接使用SAF存储访问框架实现,不要依赖全局存储权限。
- 系统版本低于API33时,正常申请
- 修复代码逻辑错误,把未授权权限列表正确传入申请方法,不要重复申请已经获得授权的权限,修正后的申请逻辑参考:
static boolean requestPermission(int id, String... permissions) { System.out.println("REQUEST PERMISSION METHOD"); boolean allGranted = true; ArrayList<String> needRequestList = new ArrayList<>(); for (String permission : permissions) { int checkResult = ContextCompat.checkSelfPermission(this, permission); boolean hasPermission = (checkResult == PackageManager.PERMISSION_GRANTED); allGranted &= hasPermission; if (!hasPermission) { needRequestList.add(permission); } } if (allGranted) { return true; } else { // 传入实际需要申请的未授权权限列表,不要传全量权限 ActivityCompat.requestPermissions(this, needRequestList.toArray(new String[0]), id); return false; } }
- 增加防重入标记避免生命周期触发重复调用:不要在
onResume里无脑触发权限申请,增加布尔类型标记位isRequestingPermission,发起权限申请前先判断如果标记位为true(正在申请中)就直接返回,发起申请时将标记位置为true,在onRequestPermissionsResult权限结果回调中再将标记位置为false,从根源上避免生命周期重入导致的重复调用。
内容的提问来源于stack exchange,提问作者AndreaF
相关产品推荐
相关产品推荐

