ZiorPark
ZiorPark
  • 发布:2026-08-14 16:43
  • 更新:2026-08-14 16:50
  • 阅读:26

【Android 14 适配 Bug】Android 14 下“部分访问”照片/视频权限导致的三大严重缺陷

分类:uni-app

产品分类: uniapp/App

PC开发环境操作系统: Windows

PC开发环境操作系统版本号: 64 位操作系统, 基于 x64 的处理器

HBuilderX版本号: 5.24

手机系统: Android

手机系统版本号: Android 16

手机厂商: 小米

手机机型: 小米15

页面类型: vue

vue版本: vue3

打包方式: 云端

项目创建方式: CLI

CLI版本号: @vue/cli 5.0.8

操作步骤:

在 Android 14 引入了 READ_MEDIA_VISUAL_USER_SELECTED(选择性相册访问)机制后,UniApp 原生基座在处理“部分授权”时存在三个严重的兼容性 Bug:

Bug 1:plus.android.requestPermissions 回调状态完全错误
复现步骤:

代码中调用 plus.android.requestPermissions(['android.permission.READ_MEDIA_IMAGES', 'android.permission.READ_MEDIA_VIDEO'])。
系统弹出授权框,用户点击“选择照片和视频”(即部分授权)
检查回调参数 resultObj。 实际结果: resultObj.granted 数组为空!而 READ_MEDIA_IMAGES 和 READ_MEDIA_VIDEO 竟然被全部塞进了 resultObj.deniedAlways 中! 期望结果: 用户既然授予了部分访问权,底层 API 应该将对应的媒体权限返回至 granted 数组,或者在业务逻辑上提供明确的“部分授权”状态标识,而不是简单粗暴地按“永久拒绝 (deniedAlways)”处理,这会导致前端拦截器严重误判。
Bug 2:在部分授权模式下,再次调用 uni.chooseVideo 视频丢失
复现步骤:

用户在首次权限申请中选择了“部分授权”,并在系统的 Photo Picker 中同时勾选了照片和视频。
再次调用 uni.chooseVideo。 实际结果: 系统不再拉起原生的 Photo Picker(因为已处于部分授权态),UniApp 似乎走了自己内部的 MediaStore 查询兜底逻辑。但该查询无法查出用户刚才授权过的视频文件,导致界面上只显示了图片,视频凭空消失。 期望结果: 在部分授权模式下,uni.chooseVideo 的内部 MediaStore 查询应当能正确读取并展示被临时授权的视频文件 URI。
Bug 3:权限从“部分”升级为“全部”后,uni.chooseVideo 列表变空
复现步骤:

用户原本处于“部分授权”状态。
用户在系统设置中,手动将相册权限改为“允许全部访问”。
再次回到 App 并调用 uni.chooseVideo。 实际结果: 选择器打开后列表完全为空(图片和视频都没了)。只有重新关闭再打开权限才能恢复正常。 期望结果: UniApp 底层的 MediaStore 查询应当处理好 ContentResolver 的生命周期/缓存。当权限状态发生变更时,内部应当主动刷新缓存,而不是返回空列表。

预期结果:

在 Android 14 引入了 READ_MEDIA_VISUAL_USER_SELECTED(选择性相册访问)机制后,UniApp 原生基座在处理“部分授权”时存在三个严重的兼容性 Bug:

Bug 1:plus.android.requestPermissions 回调状态完全错误
复现步骤:

代码中调用 plus.android.requestPermissions(['android.permission.READ_MEDIA_IMAGES', 'android.permission.READ_MEDIA_VIDEO'])。
系统弹出授权框,用户点击“选择照片和视频”(即部分授权)
检查回调参数 resultObj。 实际结果: resultObj.granted 数组为空!而 READ_MEDIA_IMAGES 和 READ_MEDIA_VIDEO 竟然被全部塞进了 resultObj.deniedAlways 中! 期望结果: 用户既然授予了部分访问权,底层 API 应该将对应的媒体权限返回至 granted 数组,或者在业务逻辑上提供明确的“部分授权”状态标识,而不是简单粗暴地按“永久拒绝 (deniedAlways)”处理,这会导致前端拦截器严重误判。
Bug 2:在部分授权模式下,再次调用 uni.chooseVideo 视频丢失
复现步骤:

用户在首次权限申请中选择了“部分授权”,并在系统的 Photo Picker 中同时勾选了照片和视频。
再次调用 uni.chooseVideo。 实际结果: 系统不再拉起原生的 Photo Picker(因为已处于部分授权态),UniApp 似乎走了自己内部的 MediaStore 查询兜底逻辑。但该查询无法查出用户刚才授权过的视频文件,导致界面上只显示了图片,视频凭空消失。 期望结果: 在部分授权模式下,uni.chooseVideo 的内部 MediaStore 查询应当能正确读取并展示被临时授权的视频文件 URI。
Bug 3:权限从“部分”升级为“全部”后,uni.chooseVideo 列表变空
复现步骤:

用户原本处于“部分授权”状态。
用户在系统设置中,手动将相册权限改为“允许全部访问”。
再次回到 App 并调用 uni.chooseVideo。 实际结果: 选择器打开后列表完全为空(图片和视频都没了)。只有重新关闭再打开权限才能恢复正常。 期望结果: UniApp 底层的 MediaStore 查询应当处理好 ContentResolver 的生命周期/缓存。当权限状态发生变更时,内部应当主动刷新缓存,而不是返回空列表。

实际结果:

在 Android 14 引入了 READ_MEDIA_VISUAL_USER_SELECTED(选择性相册访问)机制后,UniApp 原生基座在处理“部分授权”时存在三个严重的兼容性 Bug:

Bug 1:plus.android.requestPermissions 回调状态完全错误
复现步骤:

代码中调用 plus.android.requestPermissions(['android.permission.READ_MEDIA_IMAGES', 'android.permission.READ_MEDIA_VIDEO'])。
系统弹出授权框,用户点击“选择照片和视频”(即部分授权)
检查回调参数 resultObj。 实际结果: resultObj.granted 数组为空!而 READ_MEDIA_IMAGES 和 READ_MEDIA_VIDEO 竟然被全部塞进了 resultObj.deniedAlways 中! 期望结果: 用户既然授予了部分访问权,底层 API 应该将对应的媒体权限返回至 granted 数组,或者在业务逻辑上提供明确的“部分授权”状态标识,而不是简单粗暴地按“永久拒绝 (deniedAlways)”处理,这会导致前端拦截器严重误判。
Bug 2:在部分授权模式下,再次调用 uni.chooseVideo 视频丢失
复现步骤:

用户在首次权限申请中选择了“部分授权”,并在系统的 Photo Picker 中同时勾选了照片和视频。
再次调用 uni.chooseVideo。 实际结果: 系统不再拉起原生的 Photo Picker(因为已处于部分授权态),UniApp 似乎走了自己内部的 MediaStore 查询兜底逻辑。但该查询无法查出用户刚才授权过的视频文件,导致界面上只显示了图片,视频凭空消失。 期望结果: 在部分授权模式下,uni.chooseVideo 的内部 MediaStore 查询应当能正确读取并展示被临时授权的视频文件 URI。
Bug 3:权限从“部分”升级为“全部”后,uni.chooseVideo 列表变空
复现步骤:

用户原本处于“部分授权”状态。
用户在系统设置中,手动将相册权限改为“允许全部访问”。
再次回到 App 并调用 uni.chooseVideo。 实际结果: 选择器打开后列表完全为空(图片和视频都没了)。只有重新关闭再打开权限才能恢复正常。 期望结果: UniApp 底层的 MediaStore 查询应当处理好 ContentResolver 的生命周期/缓存。当权限状态发生变更时,内部应当主动刷新缓存,而不是返回空列表。

bug描述:

在 Android 14 引入了 READ_MEDIA_VISUAL_USER_SELECTED(选择性相册访问)机制后,UniApp 原生基座在处理“部分授权”时存在三个严重的兼容性 Bug:

Bug 1:plus.android.requestPermissions 回调状态完全错误
复现步骤:

代码中调用 plus.android.requestPermissions(['android.permission.READ_MEDIA_IMAGES', 'android.permission.READ_MEDIA_VIDEO'])。
系统弹出授权框,用户点击“选择照片和视频”(即部分授权)
检查回调参数 resultObj。 实际结果: resultObj.granted 数组为空!而 READ_MEDIA_IMAGES 和 READ_MEDIA_VIDEO 竟然被全部塞进了 resultObj.deniedAlways 中! 期望结果: 用户既然授予了部分访问权,底层 API 应该将对应的媒体权限返回至 granted 数组,或者在业务逻辑上提供明确的“部分授权”状态标识,而不是简单粗暴地按“永久拒绝 (deniedAlways)”处理,这会导致前端拦截器严重误判。
Bug 2:在部分授权模式下,再次调用 uni.chooseVideo 视频丢失
复现步骤:

用户在首次权限申请中选择了“部分授权”,并在系统的 Photo Picker 中同时勾选了照片和视频。
再次调用 uni.chooseVideo。 实际结果: 系统不再拉起原生的 Photo Picker(因为已处于部分授权态),UniApp 似乎走了自己内部的 MediaStore 查询兜底逻辑。但该查询无法查出用户刚才授权过的视频文件,导致界面上只显示了图片,视频凭空消失。 期望结果: 在部分授权模式下,uni.chooseVideo 的内部 MediaStore 查询应当能正确读取并展示被临时授权的视频文件 URI。
Bug 3:权限从“部分”升级为“全部”后,uni.chooseVideo 列表变空
复现步骤:

用户原本处于“部分授权”状态。
用户在系统设置中,手动将相册权限改为“允许全部访问”。
再次回到 App 并调用 uni.chooseVideo。 实际结果: 选择器打开后列表完全为空(图片和视频都没了)。只有重新关闭再打开权限才能恢复正常。 期望结果: UniApp 底层的 MediaStore 查询应当处理好 ContentResolver 的生命周期/缓存。当权限状态发生变更时,内部应当主动刷新缓存,而不是返回空列表。

2026-08-14 16:43 负责人:无 分享
已邀请:
Ask小助手

Ask小助手

欢迎到专业群( uni-app 官方技术交流群 1 ) 咨询,群中有相关专业的管理员和群友。

感谢您提交的 Bug 反馈。您反馈的问题描述非常详细,对 Android 14 引入的 READ_MEDIA_VISUAL_USER_SELECTED(选择性相册访问)机制在 UniApp 中的兼容性分析得十分透彻。以下是对该 Bug 反馈的评审与分析意见:

1. Bug 反馈内容完整性分析

整体评价:
反馈内容结构完整,包含了 Bug 标题、详细描述、复现步骤、预期结果、实际结果以及完整的分类信息。描述逻辑清晰,官方人员能够完全看懂并理解业务场景。

具体检查意见:

  • BUG 描述:非常详细,准确指出了 Android 14 “部分授权”机制下引发的三种具体异常场景,表述清晰。
  • 代码示例需要您补充。当前反馈中描述了调用 plus.android.requestPermissionsuni.chooseVideo 的逻辑,但缺少可直接运行复现的工程示例代码。为了确保官方工程师能够快速、精准地复现问题,建议您补充一个最小化的可运行代码片段(包含权限申请和选择视频的调用逻辑)。
  • 复现步骤:描述清晰,结合具体的业务场景(如“部分授权”、“系统设置修改权限”),按照步骤可以顺畅理解复现流程。
  • 预期结果与实际结果:预期结果合理,符合系统底层的授权机制与业务诉求;实际结果描述客观,能够反映出框架在处理新权限特性时的缺陷。
  • 分类信息:完整。包含了 HBuilderX 版本号(5.24 正式版)、手机系统版本(Android 16)、机型(小米15)等关键信息,有助于定位问题。
  • 附件说明:提示您,我无法识别附件内容。如果后续需要提供截图或日志,请在正文中以文本形式直接粘贴关键信息。

2. Bug 是否成立及分析

根据现有资料,Android 14 确实引入了 READ_MEDIA_VISUAL_USER_SELECTED 权限以支持“部分访问”相册。针对您反馈的三个 Bug,分析如下:

关于 Bug 1(requestPermissions 回调状态错误):
从 Android 原生机制来看,当用户选择“部分授权”时,系统实际上会授予 READ_MEDIA_VISUAL_USER_SELECTED 权限,而 READ_MEDIA_IMAGESREAD_MEDIA_VIDEO 处于未授权状态。UniApp 底层的 plus.android.requestPermissions 在处理这种混合状态时,将其直接映射到了 deniedAlways(永久拒绝)中,这在业务逻辑上确实会导致前端拦截器误判为“权限被永久拒绝”。该 Bug 现象成立,底层在状态映射处理上存在优化空间。

关于 Bug 2 与 Bug 3(chooseVideo 列表异常):
在 Android 14 “部分授权”模式下,应用通过 MediaStore 查询媒体文件时,只能查询到用户手动勾选的那部分媒体 URI。您反馈的“视频凭空消失”以及“权限升级后列表变空”现象,高度疑似 UniApp 原生基座在处理 MediaStore 查询时的过滤条件未正确适配部分授权状态,或者 ContentResolver 的生命周期/缓存在权限状态变更时未得到有效刷新。这些现象均指向框架底层的兼容性缺陷。

替代方案与建议:
在官方修复该底层适配问题之前,建议您在业务层进行规避或尝试以下方案:

  1. 使用 uni.chooseMedia 替代 uni.chooseVideo:根据官方文档说明,安卓的 uni.chooseMedia 目前采用的是系统 Picker 的形式去获取文件,这种方式受系统权限策略影响较小,可能更适合在复杂的授权场景下选择图片和视频。
  2. 注意权限组合申请:在 Android 13 及以上版本申请媒体权限时,建议同时申请基础存储权限与细分的媒体权限,部分机型在权限组合不全的情况下也会出现查询列表为空的情况。

您的反馈非常有价值,我们将把此问题转交至底层引擎研发团队进行进一步排查与适配评估。建议您在补充可复现的代码示例后,以便官方更高效地跟进处理。

内容为 AI 生成,仅供参考
ZiorPark

ZiorPark (作者)

<template>  
  <view class="content">  
    <button @click="testPermissionBug">1. 复现 Bug 1: 权限回调状态错误</button>  
    <button @click="testChooseVideoBug">2. 复现 Bug 2 & 3: 视频列表丢失</button>  
  </view>  
</template>  

<script setup>  
// 复现 Bug 1:测试 Android 14 部分授权时的 API 回调  
const testPermissionBug = () => {  
  // 模拟请求相册媒体权限  
  plus.android.requestPermissions(  
    ['android.permission.READ_MEDIA_IMAGES', 'android.permission.READ_MEDIA_VIDEO'],  
    (resultObj) => {  
      console.log('--- 权限请求结果 ---');  
      console.log('已获取 (granted):', JSON.stringify(resultObj.granted));  
      console.log('单次拒绝 (deniedPresent):', JSON.stringify(resultObj.deniedPresent));  
      console.log('永久拒绝 (deniedAlways):', JSON.stringify(resultObj.deniedAlways));  

      // 当用户在系统弹窗选择“选择照片和视频(部分访问)”时  
      // 期望:此处应有状态标识用户已授权部分访问  
      // 实际:granted 为空,全部被塞入 deniedAlways,导致业务层误判为“永久拒绝”  
      uni.showModal({  
        title: '回调异常结果展示',  
        content: `granted 数量: ${resultObj.granted.length}\ndeniedAlways 数量: ${resultObj.deniedAlways.length}\n请查看控制台详细日志。`  
      });  
    },  
    (error) => {  
      console.error('权限申请失败', error);  
    }  
  );  
};  

// 复现 Bug 2 和 3:测试部分授权或权限状态切换时的媒体查询  
const testChooseVideoBug = () => {  
  // 前置条件:先在系统设置中或通过按钮1将 App 相册权限设为“部分访问”,并随意勾选几个图片和视频  
  uni.chooseVideo({  
    sourceType: ['album'],  
    success: (res) => {  
      console.log('选择视频成功:', res.tempFilePath);  
    },  
    fail: (err) => {  
      console.error('选择视频失败:', err);  
      // 在部分访问下,这里打开的选择器大概率只能看到刚才勾选的图片,看不到刚才勾选的视频 (Bug 2)  
      // 如果去系统设置改为“允许全部访问”再回来点这个按钮,列表将完全为空 (Bug 3)  
    }  
  });  
};  
</script>  

<style>  
.content {  
  padding: 30px;  
  display: flex;  
  flex-direction: column;  
  gap: 20px;  
}  
button {  
  background-color: #007aff;  
  color: white;  
}  
</style>  

运行指南:

1.将上述代码粘贴至任意空白的 uni-app (Vue3) 项目的页面中。
2.使用 Android 14 测试机进行真机基座运行。
3.点击按钮 1,在弹出的系统权限框中选择“选择照片和视频”(部分访问),即可复现 Bug 1。
4.点击按钮 2,观察弹出的媒体列表,即可复现 Bug 2;随后去系统设置将权限升级为“全部访问”,再返回 App 点击按钮 2,即可复现 Bug 3。

要回复问题请先登录注册